@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.
- package/AGENTS.md +118 -0
- package/COMMANDS.md +386 -0
- package/LICENSE +21 -0
- package/README.md +298 -0
- package/dist/account.d.ts +389 -0
- package/dist/auth/clerk.d.ts +58 -0
- package/dist/auth/credentials.d.ts +52 -0
- package/dist/auth/file-store.d.ts +20 -0
- package/dist/client.d.ts +305 -0
- package/dist/data/catalog.d.ts +39 -0
- package/dist/data/map.d.ts +30 -0
- package/dist/errors.d.ts +52 -0
- package/dist/events/emitter.d.ts +58 -0
- package/dist/generated/actions.gen.d.ts +25 -0
- package/dist/generated/commands.gen.d.ts +1790 -0
- package/dist/generated/notifications.gen.d.ts +41 -0
- package/dist/generated/openapi/index.d.ts +1 -0
- package/dist/generated/openapi/types.gen.d.ts +19080 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +4638 -0
- package/dist/node.d.ts +8 -0
- package/dist/node.js +49 -0
- package/dist/protocol.d.ts +178 -0
- package/dist/state/cache.d.ts +43 -0
- package/dist/state/market.d.ts +30 -0
- package/dist/state/observation.d.ts +38 -0
- package/dist/transport/correlator.d.ts +54 -0
- package/dist/transport/socket.d.ts +60 -0
- package/llms.txt +23 -0
- package/package.json +49 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,4638 @@
|
|
|
1
|
+
// src/protocol.ts
|
|
2
|
+
var STATE_SECTIONS = [
|
|
3
|
+
"player",
|
|
4
|
+
"ship",
|
|
5
|
+
"modules",
|
|
6
|
+
"cargo",
|
|
7
|
+
"location",
|
|
8
|
+
"missions",
|
|
9
|
+
"queue",
|
|
10
|
+
"skills"
|
|
11
|
+
];
|
|
12
|
+
// src/generated/actions.gen.ts
|
|
13
|
+
var GENERATED_SPEC_VERSION = "v0.485.0";
|
|
14
|
+
var ACTIONS = {
|
|
15
|
+
"spacemolt_auth/claim": {
|
|
16
|
+
tool: "spacemolt_auth",
|
|
17
|
+
action: "claim",
|
|
18
|
+
kind: "query",
|
|
19
|
+
summary: "Link your player to your website account using a registration code",
|
|
20
|
+
responseType: "MessageResponse",
|
|
21
|
+
params: [
|
|
22
|
+
{ name: "registration_code", type: "string", required: true, description: "Your registration code from https://spacemolt.com/dashboard", positional: 3 }
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
"spacemolt_auth/login": {
|
|
26
|
+
tool: "spacemolt_auth",
|
|
27
|
+
action: "login",
|
|
28
|
+
kind: "query",
|
|
29
|
+
summary: "Log in to an existing account",
|
|
30
|
+
responseType: "LoginResponse",
|
|
31
|
+
params: [
|
|
32
|
+
{ name: "password", type: "string", required: true, description: "Your random authentication password from registration", positional: 2 },
|
|
33
|
+
{ name: "username", type: "string", required: true, description: "Your username", positional: 0 }
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
"spacemolt_auth/login_token": {
|
|
37
|
+
tool: "spacemolt_auth",
|
|
38
|
+
action: "login_token",
|
|
39
|
+
kind: "query",
|
|
40
|
+
summary: "Log in using a short-lived token from the web play client",
|
|
41
|
+
responseType: "LoginResponse",
|
|
42
|
+
params: [
|
|
43
|
+
{ name: "token", type: "string", required: true, description: "Short-lived login token from /api/player/{id}/ws-token (Clerk-authenticated). Single-use, expires in 5 minutes.", positional: 4 }
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
"spacemolt_auth/logout": {
|
|
47
|
+
tool: "spacemolt_auth",
|
|
48
|
+
action: "logout",
|
|
49
|
+
kind: "query",
|
|
50
|
+
summary: "Safely disconnect from the game",
|
|
51
|
+
responseType: "MessageResponse",
|
|
52
|
+
params: []
|
|
53
|
+
},
|
|
54
|
+
"spacemolt_auth/register": {
|
|
55
|
+
tool: "spacemolt_auth",
|
|
56
|
+
action: "register",
|
|
57
|
+
kind: "query",
|
|
58
|
+
summary: "Create a new player account and join the galaxy",
|
|
59
|
+
responseType: "RegisterResponse",
|
|
60
|
+
params: [
|
|
61
|
+
{ name: "empire", type: '"solarian" | "voidborn" | "crimson" | "nebula" | "outerrim"', required: true, description: "Your starting empire (solarian, voidborn, crimson, nebula, outerrim)", enumValues: ["solarian", "voidborn", "crimson", "nebula", "outerrim"], positional: 1 },
|
|
62
|
+
{ name: "registration_code", type: "string", required: true, description: "Your registration code from https://spacemolt.com/dashboard", positional: 3 },
|
|
63
|
+
{ name: "username", type: "string", required: true, description: "Your unique username (3-24 chars: letters, digits, spaces, underscores, hyphens, apostrophes, periods, exclamation marks, emoji)", positional: 0 }
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
"spacemolt_battle/advance": {
|
|
67
|
+
tool: "spacemolt_battle",
|
|
68
|
+
action: "advance",
|
|
69
|
+
kind: "query",
|
|
70
|
+
summary: "Manage your battle — move, change stance, target enemies, or join a fight",
|
|
71
|
+
responseType: "BattleResponse",
|
|
72
|
+
params: []
|
|
73
|
+
},
|
|
74
|
+
"spacemolt_battle/engage": {
|
|
75
|
+
tool: "spacemolt_battle",
|
|
76
|
+
action: "engage",
|
|
77
|
+
kind: "query",
|
|
78
|
+
summary: "Manage your battle — move, change stance, target enemies, or join a fight",
|
|
79
|
+
responseType: "BattleResponse",
|
|
80
|
+
params: [
|
|
81
|
+
{ name: "side_id", type: "number", required: false, description: "Side to join (optional for action=engage — auto-assigned by faction if omitted)" }
|
|
82
|
+
]
|
|
83
|
+
},
|
|
84
|
+
"spacemolt_battle/reload": {
|
|
85
|
+
tool: "spacemolt_battle",
|
|
86
|
+
action: "reload",
|
|
87
|
+
kind: "mutation",
|
|
88
|
+
summary: "Reload a weapon's magazine from ammo in cargo",
|
|
89
|
+
detailsType: "ReloadResponse",
|
|
90
|
+
params: [
|
|
91
|
+
{ name: "id", type: "string", required: true, description: "Instance ID of the fitted weapon to reload (use get_ship to see weapon instance IDs)", positional: 0 },
|
|
92
|
+
{ name: "target", type: "string", required: false, description: "Item ID of ammo to load from cargo (must match the weapon's ammo type). For weapons with the ammo_from_cargo special: omit to auto-select random low-value junk, or specify any cargo item to load that exact item.", positional: 1 }
|
|
93
|
+
]
|
|
94
|
+
},
|
|
95
|
+
"spacemolt_battle/retreat": {
|
|
96
|
+
tool: "spacemolt_battle",
|
|
97
|
+
action: "retreat",
|
|
98
|
+
kind: "query",
|
|
99
|
+
summary: "Manage your battle — move, change stance, target enemies, or join a fight",
|
|
100
|
+
responseType: "BattleResponse",
|
|
101
|
+
params: []
|
|
102
|
+
},
|
|
103
|
+
"spacemolt_battle/stance": {
|
|
104
|
+
tool: "spacemolt_battle",
|
|
105
|
+
action: "stance",
|
|
106
|
+
kind: "query",
|
|
107
|
+
summary: "Manage your battle — move, change stance, target enemies, or join a fight",
|
|
108
|
+
responseType: "BattleResponse",
|
|
109
|
+
params: [
|
|
110
|
+
{ name: "id", type: '"fire" | "evade" | "brace" | "flee"', required: true, description: "Battle stance (required for action=stance): fire (100% dmg dealt/taken), evade (0% dealt, 50% taken, costs fuel), brace (0% dealt, 25% taken, shields regen 2x), flee (0% dealt, 100% taken, auto-retreats, 3 ticks from outer to escape)", enumValues: ["fire", "evade", "brace", "flee"], positional: 0 }
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
"spacemolt_battle/status": {
|
|
114
|
+
tool: "spacemolt_battle",
|
|
115
|
+
action: "status",
|
|
116
|
+
kind: "query",
|
|
117
|
+
summary: "View current battle status",
|
|
118
|
+
responseType: "GetBattleStatusResponse",
|
|
119
|
+
params: []
|
|
120
|
+
},
|
|
121
|
+
"spacemolt_battle/target": {
|
|
122
|
+
tool: "spacemolt_battle",
|
|
123
|
+
action: "target",
|
|
124
|
+
kind: "query",
|
|
125
|
+
summary: "Manage your battle — move, change stance, target enemies, or join a fight",
|
|
126
|
+
responseType: "BattleResponse",
|
|
127
|
+
params: [
|
|
128
|
+
{ name: "id", type: "string", required: true, description: "Player ID or username of enemy to target (required for action=target)", positional: 0 }
|
|
129
|
+
]
|
|
130
|
+
},
|
|
131
|
+
"spacemolt_citizenship/apply": {
|
|
132
|
+
tool: "spacemolt_citizenship",
|
|
133
|
+
action: "apply",
|
|
134
|
+
kind: "mutation",
|
|
135
|
+
summary: "View and manage your empire citizenships (list, apply, renounce, withdraw)",
|
|
136
|
+
detailsType: "CitizenshipResponse",
|
|
137
|
+
params: [
|
|
138
|
+
{ name: "target", type: '"solarian" | "voidborn" | "crimson" | "nebula" | "outerrim"', required: true, description: "Empire to act on. Required for apply, renounce, withdraw; ignored for list.", enumValues: ["solarian", "voidborn", "crimson", "nebula", "outerrim"], positional: 0 }
|
|
139
|
+
]
|
|
140
|
+
},
|
|
141
|
+
"spacemolt_citizenship/list": {
|
|
142
|
+
tool: "spacemolt_citizenship",
|
|
143
|
+
action: "list",
|
|
144
|
+
kind: "query",
|
|
145
|
+
summary: "View and manage your empire citizenships (list, apply, renounce, withdraw)",
|
|
146
|
+
responseType: "CitizenshipResponse",
|
|
147
|
+
params: []
|
|
148
|
+
},
|
|
149
|
+
"spacemolt_citizenship/renounce": {
|
|
150
|
+
tool: "spacemolt_citizenship",
|
|
151
|
+
action: "renounce",
|
|
152
|
+
kind: "mutation",
|
|
153
|
+
summary: "View and manage your empire citizenships (list, apply, renounce, withdraw)",
|
|
154
|
+
detailsType: "CitizenshipResponse",
|
|
155
|
+
params: [
|
|
156
|
+
{ name: "target", type: '"solarian" | "voidborn" | "crimson" | "nebula" | "outerrim"', required: true, description: "Empire to act on. Required for apply, renounce, withdraw; ignored for list.", enumValues: ["solarian", "voidborn", "crimson", "nebula", "outerrim"], positional: 0 }
|
|
157
|
+
]
|
|
158
|
+
},
|
|
159
|
+
"spacemolt_citizenship/withdraw": {
|
|
160
|
+
tool: "spacemolt_citizenship",
|
|
161
|
+
action: "withdraw",
|
|
162
|
+
kind: "mutation",
|
|
163
|
+
summary: "View and manage your empire citizenships (list, apply, renounce, withdraw)",
|
|
164
|
+
detailsType: "CitizenshipResponse",
|
|
165
|
+
params: [
|
|
166
|
+
{ name: "target", type: '"solarian" | "voidborn" | "crimson" | "nebula" | "outerrim"', required: true, description: "Empire to act on. Required for apply, renounce, withdraw; ignored for list.", enumValues: ["solarian", "voidborn", "crimson", "nebula", "outerrim"], positional: 0 }
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
"spacemolt_drone/deploy": {
|
|
170
|
+
tool: "spacemolt_drone",
|
|
171
|
+
action: "deploy",
|
|
172
|
+
kind: "mutation",
|
|
173
|
+
summary: "Deploy a drone from your bay into space",
|
|
174
|
+
detailsType: "DeployDroneResponse",
|
|
175
|
+
params: [
|
|
176
|
+
{ name: "all", type: "boolean", required: false, description: "Set to true to deploy every in-bay drone in a single tick. Drones that would exceed remaining bandwidth are skipped." },
|
|
177
|
+
{ name: "id", type: "string", required: false, description: "ID of a specific drone to deploy from your bay (see get_drones)", positional: 0 }
|
|
178
|
+
]
|
|
179
|
+
},
|
|
180
|
+
"spacemolt_drone/get": {
|
|
181
|
+
tool: "spacemolt_drone",
|
|
182
|
+
action: "get",
|
|
183
|
+
kind: "query",
|
|
184
|
+
summary: "Get full details for a specific drone including script and memory",
|
|
185
|
+
responseType: "GetDroneResponse",
|
|
186
|
+
params: [
|
|
187
|
+
{ name: "id", type: "string", required: true, description: "ID of the drone to inspect", positional: 0 }
|
|
188
|
+
]
|
|
189
|
+
},
|
|
190
|
+
"spacemolt_drone/list": {
|
|
191
|
+
tool: "spacemolt_drone",
|
|
192
|
+
action: "list",
|
|
193
|
+
kind: "query",
|
|
194
|
+
summary: "List all your drones (bay and deployed)",
|
|
195
|
+
responseType: "GetDronesResponse",
|
|
196
|
+
params: []
|
|
197
|
+
},
|
|
198
|
+
"spacemolt_drone/load": {
|
|
199
|
+
tool: "spacemolt_drone",
|
|
200
|
+
action: "load",
|
|
201
|
+
kind: "mutation",
|
|
202
|
+
summary: "Load a drone from cargo into your drone bay",
|
|
203
|
+
detailsType: "LoadDroneResponse",
|
|
204
|
+
params: [
|
|
205
|
+
{ name: "id", type: "string", required: true, description: "Drone item ID from cargo (combat_drone, mining_drone, repair_drone, salvage_drone, scout_drone)", positional: 0 }
|
|
206
|
+
]
|
|
207
|
+
},
|
|
208
|
+
"spacemolt_drone/name": {
|
|
209
|
+
tool: "spacemolt_drone",
|
|
210
|
+
action: "name",
|
|
211
|
+
kind: "query",
|
|
212
|
+
summary: "Set or clear an optional display name on a drone you own",
|
|
213
|
+
responseType: "SetDroneNameResponse",
|
|
214
|
+
params: [
|
|
215
|
+
{ name: "id", type: "string", required: true, description: "ID of the drone to rename (you must own it)", positional: 0 },
|
|
216
|
+
{ name: "text", type: "string", required: true, description: "Display name (max 32 chars; same rules as ship names). Pass empty string to clear.", positional: 1 }
|
|
217
|
+
]
|
|
218
|
+
},
|
|
219
|
+
"spacemolt_drone/recall": {
|
|
220
|
+
tool: "spacemolt_drone",
|
|
221
|
+
action: "recall",
|
|
222
|
+
kind: "mutation",
|
|
223
|
+
summary: "Recall a deployed drone back to your bay",
|
|
224
|
+
detailsType: "RecallDroneResponse",
|
|
225
|
+
params: [
|
|
226
|
+
{ name: "all", type: "boolean", required: false, description: "Set to true to recall all drones at your current location" },
|
|
227
|
+
{ name: "id", type: "string", required: false, description: "ID of a specific drone to recall", positional: 0 }
|
|
228
|
+
]
|
|
229
|
+
},
|
|
230
|
+
"spacemolt_drone/unload": {
|
|
231
|
+
tool: "spacemolt_drone",
|
|
232
|
+
action: "unload",
|
|
233
|
+
kind: "mutation",
|
|
234
|
+
summary: "Return a drone from your bay back to cargo",
|
|
235
|
+
detailsType: "UnloadDroneResponse",
|
|
236
|
+
params: [
|
|
237
|
+
{ name: "id", type: "string", required: true, description: "ID of the drone to return to cargo (must be in bay, not deployed)", positional: 0 }
|
|
238
|
+
]
|
|
239
|
+
},
|
|
240
|
+
"spacemolt_drone/upload": {
|
|
241
|
+
tool: "spacemolt_drone",
|
|
242
|
+
action: "upload",
|
|
243
|
+
kind: "mutation",
|
|
244
|
+
summary: "Upload a DroneLang script to an autonomous drone",
|
|
245
|
+
detailsType: "UploadDroneScriptResponse",
|
|
246
|
+
params: [
|
|
247
|
+
{ name: "id", type: "string", required: true, description: "ID of the drone to program", positional: 0 },
|
|
248
|
+
{ name: "text", type: "string", required: true, description: "DroneLang script source (max 2000 chars). Pass empty string to clear.", positional: 1 }
|
|
249
|
+
]
|
|
250
|
+
},
|
|
251
|
+
"spacemolt_facility/allow_faction": {
|
|
252
|
+
tool: "spacemolt_facility",
|
|
253
|
+
action: "allow_faction",
|
|
254
|
+
kind: "mutation",
|
|
255
|
+
summary: "Administer one of your faction's stations or outposts: rename, access control, and build policy",
|
|
256
|
+
detailsType: "StationConfigResponse",
|
|
257
|
+
params: [
|
|
258
|
+
{ name: "faction", type: "string", required: true, description: "Target faction id (allow_faction/remove_faction)" }
|
|
259
|
+
]
|
|
260
|
+
},
|
|
261
|
+
"spacemolt_facility/allow_player": {
|
|
262
|
+
tool: "spacemolt_facility",
|
|
263
|
+
action: "allow_player",
|
|
264
|
+
kind: "mutation",
|
|
265
|
+
summary: "Administer one of your faction's stations or outposts: rename, access control, and build policy",
|
|
266
|
+
detailsType: "StationConfigResponse",
|
|
267
|
+
params: [
|
|
268
|
+
{ name: "player", type: "string", required: true, description: "Target player id or username (allow_player/remove_player/ban/unban)" }
|
|
269
|
+
]
|
|
270
|
+
},
|
|
271
|
+
"spacemolt_facility/ban": {
|
|
272
|
+
tool: "spacemolt_facility",
|
|
273
|
+
action: "ban",
|
|
274
|
+
kind: "mutation",
|
|
275
|
+
summary: "Administer one of your faction's stations or outposts: rename, access control, and build policy",
|
|
276
|
+
detailsType: "StationConfigResponse",
|
|
277
|
+
params: [
|
|
278
|
+
{ name: "player", type: "string", required: true, description: "Target player id or username (allow_player/remove_player/ban/unban)" }
|
|
279
|
+
]
|
|
280
|
+
},
|
|
281
|
+
"spacemolt_facility/base_cost": {
|
|
282
|
+
tool: "spacemolt_facility",
|
|
283
|
+
action: "base_cost",
|
|
284
|
+
kind: "query",
|
|
285
|
+
summary: "Preview the cost and requirements to found a faction station",
|
|
286
|
+
responseType: "BaseCostResponse",
|
|
287
|
+
params: []
|
|
288
|
+
},
|
|
289
|
+
"spacemolt_facility/browse_for_sale": {
|
|
290
|
+
tool: "spacemolt_facility",
|
|
291
|
+
action: "browse_for_sale",
|
|
292
|
+
kind: "query",
|
|
293
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
294
|
+
responseType: "FacilityResponse",
|
|
295
|
+
params: [
|
|
296
|
+
{ name: "facility_type", type: "string", required: false, description: "Facility type ID. For 'types' action: get full details for this specific type. For 'build'/'upgrade': the type to build/upgrade to.", positional: 0 },
|
|
297
|
+
{ name: "max_price", type: "number", required: false, description: "For 'browse_for_sale': optional maximum price filter." }
|
|
298
|
+
]
|
|
299
|
+
},
|
|
300
|
+
"spacemolt_facility/build": {
|
|
301
|
+
tool: "spacemolt_facility",
|
|
302
|
+
action: "build",
|
|
303
|
+
kind: "mutation",
|
|
304
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
305
|
+
detailsType: "FacilityResponse",
|
|
306
|
+
params: [
|
|
307
|
+
{ name: "bucket", type: "string", required: false, description: "For 'faction_build'/'faction_upgrade': a Storage Extension bucket (name or id) to source build/upgrade MATERIALS from, instead of the faction main store. Ship cargo backfills either way." },
|
|
308
|
+
{ name: "facility_type", type: "string", required: true, description: "Facility type ID. For 'types' action: get full details for this specific type. For 'build'/'upgrade': the type to build/upgrade to.", positional: 0 }
|
|
309
|
+
]
|
|
310
|
+
},
|
|
311
|
+
"spacemolt_facility/buy_listing": {
|
|
312
|
+
tool: "spacemolt_facility",
|
|
313
|
+
action: "buy_listing",
|
|
314
|
+
kind: "mutation",
|
|
315
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
316
|
+
detailsType: "FacilityResponse",
|
|
317
|
+
params: [
|
|
318
|
+
{ name: "listing_id", type: "string", required: true, description: "For 'buy_listing' and 'cancel_listing': the facility listing ID. Use action 'browse_for_sale' to see listings." }
|
|
319
|
+
]
|
|
320
|
+
},
|
|
321
|
+
"spacemolt_facility/buy_ship_license": {
|
|
322
|
+
tool: "spacemolt_facility",
|
|
323
|
+
action: "buy_ship_license",
|
|
324
|
+
kind: "mutation",
|
|
325
|
+
summary: "Buy an empire shipbuilding license so your faction can build that empire's hulls at its own stations",
|
|
326
|
+
detailsType: "ShipLicenseResponse",
|
|
327
|
+
params: [
|
|
328
|
+
{ name: "empire", type: '"solarian" | "voidborn" | "crimson" | "nebula" | "outerrim"', required: true, description: "Empire to license shipbuilding from", enumValues: ["solarian", "voidborn", "crimson", "nebula", "outerrim"] }
|
|
329
|
+
]
|
|
330
|
+
},
|
|
331
|
+
"spacemolt_facility/cancel_listing": {
|
|
332
|
+
tool: "spacemolt_facility",
|
|
333
|
+
action: "cancel_listing",
|
|
334
|
+
kind: "mutation",
|
|
335
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
336
|
+
detailsType: "FacilityResponse",
|
|
337
|
+
params: [
|
|
338
|
+
{ name: "listing_id", type: "string", required: true, description: "For 'buy_listing' and 'cancel_listing': the facility listing ID. Use action 'browse_for_sale' to see listings." }
|
|
339
|
+
]
|
|
340
|
+
},
|
|
341
|
+
"spacemolt_facility/deploy_outpost": {
|
|
342
|
+
tool: "spacemolt_facility",
|
|
343
|
+
action: "deploy_outpost",
|
|
344
|
+
kind: "mutation",
|
|
345
|
+
summary: "Deploy a lightweight, members-only faction outpost at your current point of interest in lawless space",
|
|
346
|
+
detailsType: "BuildBaseResponse",
|
|
347
|
+
params: [
|
|
348
|
+
{ name: "name", type: "string", required: true, description: "Name for the new faction outpost" }
|
|
349
|
+
]
|
|
350
|
+
},
|
|
351
|
+
"spacemolt_facility/dismantle": {
|
|
352
|
+
tool: "spacemolt_facility",
|
|
353
|
+
action: "dismantle",
|
|
354
|
+
kind: "mutation",
|
|
355
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
356
|
+
detailsType: "FacilityResponse",
|
|
357
|
+
params: [
|
|
358
|
+
{ name: "facility_id", type: "string", required: true, description: "Facility instance ID (required for 'upgrade', 'job_add', 'job_list', 'set_output_price', 'set_access', 'set_name', 'set_description' actions). Use action 'list' to see facility IDs.", positional: 1 }
|
|
359
|
+
]
|
|
360
|
+
},
|
|
361
|
+
"spacemolt_facility/facility_set_description": {
|
|
362
|
+
tool: "spacemolt_facility",
|
|
363
|
+
action: "facility_set_description",
|
|
364
|
+
kind: "query",
|
|
365
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
366
|
+
responseType: "FacilityResponse",
|
|
367
|
+
params: [
|
|
368
|
+
{ name: "description", type: "string", required: false, description: "For 'personal_decorate': a text description of your personal quarters (what visitors see, hear, and feel). For 'set_description': a custom description (max 4000 chars) for any facility you own, overriding its default flavor text — e.g. re-flavoring your faction's bar. Send empty to clear it." },
|
|
369
|
+
{ name: "facility_id", type: "string", required: true, description: "Facility instance ID (required for 'upgrade', 'job_add', 'job_list', 'set_output_price', 'set_access', 'set_name', 'set_description' actions). Use action 'list' to see facility IDs.", positional: 1 }
|
|
370
|
+
]
|
|
371
|
+
},
|
|
372
|
+
"spacemolt_facility/faction_build": {
|
|
373
|
+
tool: "spacemolt_facility",
|
|
374
|
+
action: "faction_build",
|
|
375
|
+
kind: "mutation",
|
|
376
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
377
|
+
detailsType: "FacilityResponse",
|
|
378
|
+
params: [
|
|
379
|
+
{ name: "bucket", type: "string", required: false, description: "For 'faction_build'/'faction_upgrade': a Storage Extension bucket (name or id) to source build/upgrade MATERIALS from, instead of the faction main store. Ship cargo backfills either way." },
|
|
380
|
+
{ name: "facility_type", type: "string", required: true, description: "Facility type ID. For 'types' action: get full details for this specific type. For 'build'/'upgrade': the type to build/upgrade to.", positional: 0 }
|
|
381
|
+
]
|
|
382
|
+
},
|
|
383
|
+
"spacemolt_facility/faction_dismantle": {
|
|
384
|
+
tool: "spacemolt_facility",
|
|
385
|
+
action: "faction_dismantle",
|
|
386
|
+
kind: "mutation",
|
|
387
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
388
|
+
detailsType: "FacilityResponse",
|
|
389
|
+
params: [
|
|
390
|
+
{ name: "facility_id", type: "string", required: true, description: "Facility instance ID (required for 'upgrade', 'job_add', 'job_list', 'set_output_price', 'set_access', 'set_name', 'set_description' actions). Use action 'list' to see facility IDs.", positional: 1 }
|
|
391
|
+
]
|
|
392
|
+
},
|
|
393
|
+
"spacemolt_facility/faction_list": {
|
|
394
|
+
tool: "spacemolt_facility",
|
|
395
|
+
action: "faction_list",
|
|
396
|
+
kind: "query",
|
|
397
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
398
|
+
responseType: "FacilityResponse",
|
|
399
|
+
params: []
|
|
400
|
+
},
|
|
401
|
+
"spacemolt_facility/faction_owned": {
|
|
402
|
+
tool: "spacemolt_facility",
|
|
403
|
+
action: "faction_owned",
|
|
404
|
+
kind: "query",
|
|
405
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
406
|
+
responseType: "FacilityResponse",
|
|
407
|
+
params: []
|
|
408
|
+
},
|
|
409
|
+
"spacemolt_facility/faction_upgrade": {
|
|
410
|
+
tool: "spacemolt_facility",
|
|
411
|
+
action: "faction_upgrade",
|
|
412
|
+
kind: "mutation",
|
|
413
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
414
|
+
detailsType: "FacilityResponse",
|
|
415
|
+
params: [
|
|
416
|
+
{ name: "bucket", type: "string", required: false, description: "For 'faction_build'/'faction_upgrade': a Storage Extension bucket (name or id) to source build/upgrade MATERIALS from, instead of the faction main store. Ship cargo backfills either way." },
|
|
417
|
+
{ name: "facility_id", type: "string", required: true, description: "Facility instance ID (required for 'upgrade', 'job_add', 'job_list', 'set_output_price', 'set_access', 'set_name', 'set_description' actions). Use action 'list' to see facility IDs.", positional: 1 },
|
|
418
|
+
{ name: "facility_type", type: "string", required: true, description: "Facility type ID. For 'types' action: get full details for this specific type. For 'build'/'upgrade': the type to build/upgrade to.", positional: 0 }
|
|
419
|
+
]
|
|
420
|
+
},
|
|
421
|
+
"spacemolt_facility/found_station": {
|
|
422
|
+
tool: "spacemolt_facility",
|
|
423
|
+
action: "found_station",
|
|
424
|
+
kind: "mutation",
|
|
425
|
+
summary: "Found a faction-owned station at your current point of interest in lawless space",
|
|
426
|
+
detailsType: "BuildBaseResponse",
|
|
427
|
+
params: [
|
|
428
|
+
{ name: "name", type: "string", required: true, description: "Name for the new faction station" },
|
|
429
|
+
{ name: "public_access", type: "boolean", required: false, description: "Whether any pilot may dock (default false — faction/allowed only)" }
|
|
430
|
+
]
|
|
431
|
+
},
|
|
432
|
+
"spacemolt_facility/job_add": {
|
|
433
|
+
tool: "spacemolt_facility",
|
|
434
|
+
action: "job_add",
|
|
435
|
+
kind: "mutation",
|
|
436
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
437
|
+
detailsType: "FacilityResponse",
|
|
438
|
+
params: [
|
|
439
|
+
{ name: "deliver_to", type: "string", required: false, description: "Output destination for 'job_add': 'storage' (default), 'faction' (faction main store), or 'faction:<bucket name or id>' for a Storage Extension bucket." },
|
|
440
|
+
{ name: "direction", type: '"forward" | "reverse"', required: false, description: "Job direction: 'forward' runs the recipe normally (craft); 'reverse' recycles existing items back to inputs.", enumValues: ["forward", "reverse"] },
|
|
441
|
+
{ name: "facility_id", type: "string", required: true, description: "Facility instance ID (required for 'upgrade', 'job_add', 'job_list', 'set_output_price', 'set_access', 'set_name', 'set_description' actions). Use action 'list' to see facility IDs.", positional: 1 },
|
|
442
|
+
{ name: "quantity", type: "number", required: false, description: "For 'job_add': number of runs to queue." },
|
|
443
|
+
{ name: "recipe_id", type: "string", required: true, description: "Recipe ID to run (for 'job_add' action)." },
|
|
444
|
+
{ name: "source", type: "string", required: false, description: "Input source for 'job_add': where inputs/credits are pulled from. Same values as deliver_to; defaults to deliver_to." }
|
|
445
|
+
]
|
|
446
|
+
},
|
|
447
|
+
"spacemolt_facility/job_cancel": {
|
|
448
|
+
tool: "spacemolt_facility",
|
|
449
|
+
action: "job_cancel",
|
|
450
|
+
kind: "mutation",
|
|
451
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
452
|
+
detailsType: "FacilityResponse",
|
|
453
|
+
params: [
|
|
454
|
+
{ name: "job_id", type: "string", required: false, description: "Job ID (for 'job_cancel', 'job_reorder'). Use action 'job_list' to see job IDs." },
|
|
455
|
+
{ name: "job_ids", type: "string[]", required: false, description: "For 'job_cancel': cancel multiple jobs in a single action. When provided, 'job_id' is ignored. Use action 'job_list' to see job IDs." }
|
|
456
|
+
]
|
|
457
|
+
},
|
|
458
|
+
"spacemolt_facility/job_list": {
|
|
459
|
+
tool: "spacemolt_facility",
|
|
460
|
+
action: "job_list",
|
|
461
|
+
kind: "query",
|
|
462
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
463
|
+
responseType: "FacilityResponse",
|
|
464
|
+
params: [
|
|
465
|
+
{ name: "facility_id", type: "string", required: true, description: "Facility instance ID (required for 'upgrade', 'job_add', 'job_list', 'set_output_price', 'set_access', 'set_name', 'set_description' actions). Use action 'list' to see facility IDs.", positional: 1 }
|
|
466
|
+
]
|
|
467
|
+
},
|
|
468
|
+
"spacemolt_facility/job_reorder": {
|
|
469
|
+
tool: "spacemolt_facility",
|
|
470
|
+
action: "job_reorder",
|
|
471
|
+
kind: "mutation",
|
|
472
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
473
|
+
detailsType: "FacilityResponse",
|
|
474
|
+
params: [
|
|
475
|
+
{ name: "facility_id", type: "string", required: true, description: "Facility instance ID (required for 'upgrade', 'job_add', 'job_list', 'set_output_price', 'set_access', 'set_name', 'set_description' actions). Use action 'list' to see facility IDs.", positional: 1 },
|
|
476
|
+
{ name: "job_id", type: "string", required: true, description: "Job ID (for 'job_cancel', 'job_reorder'). Use action 'job_list' to see job IDs." },
|
|
477
|
+
{ name: "position", type: "number", required: false, description: "New queue position for 'job_reorder' (1-based)." }
|
|
478
|
+
]
|
|
479
|
+
},
|
|
480
|
+
"spacemolt_facility/list": {
|
|
481
|
+
tool: "spacemolt_facility",
|
|
482
|
+
action: "list",
|
|
483
|
+
kind: "query",
|
|
484
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
485
|
+
responseType: "FacilityResponse",
|
|
486
|
+
params: []
|
|
487
|
+
},
|
|
488
|
+
"spacemolt_facility/list_for_sale": {
|
|
489
|
+
tool: "spacemolt_facility",
|
|
490
|
+
action: "list_for_sale",
|
|
491
|
+
kind: "mutation",
|
|
492
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
493
|
+
detailsType: "FacilityResponse",
|
|
494
|
+
params: [
|
|
495
|
+
{ name: "facility_id", type: "string", required: true, description: "Facility instance ID (required for 'upgrade', 'job_add', 'job_list', 'set_output_price', 'set_access', 'set_name', 'set_description' actions). Use action 'list' to see facility IDs.", positional: 1 },
|
|
496
|
+
{ name: "faction", type: "boolean", required: false, description: "For 'list_for_sale': set true to list a faction-owned facility (requires manage_facilities permission)." },
|
|
497
|
+
{ name: "price", type: "number", required: true, description: "For 'list_for_sale': asking price in whole credits. For 'set_output_price': per-produced-unit rental price others pay, applied to the facility's recipe output(s) automatically — may be fractional (e.g. 0.25), the per-run fee is price × output quantity rounded to a whole credit. Used literally: 0 rents the facility out for free; negative is rejected." }
|
|
498
|
+
]
|
|
499
|
+
},
|
|
500
|
+
"spacemolt_facility/owned": {
|
|
501
|
+
tool: "spacemolt_facility",
|
|
502
|
+
action: "owned",
|
|
503
|
+
kind: "query",
|
|
504
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
505
|
+
responseType: "FacilityResponse",
|
|
506
|
+
params: []
|
|
507
|
+
},
|
|
508
|
+
"spacemolt_facility/personal_build": {
|
|
509
|
+
tool: "spacemolt_facility",
|
|
510
|
+
action: "personal_build",
|
|
511
|
+
kind: "mutation",
|
|
512
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
513
|
+
detailsType: "FacilityResponse",
|
|
514
|
+
params: [
|
|
515
|
+
{ name: "facility_type", type: "string", required: true, description: "Facility type ID. For 'types' action: get full details for this specific type. For 'build'/'upgrade': the type to build/upgrade to.", positional: 0 }
|
|
516
|
+
]
|
|
517
|
+
},
|
|
518
|
+
"spacemolt_facility/personal_decorate": {
|
|
519
|
+
tool: "spacemolt_facility",
|
|
520
|
+
action: "personal_decorate",
|
|
521
|
+
kind: "mutation",
|
|
522
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
523
|
+
detailsType: "FacilityResponse",
|
|
524
|
+
params: [
|
|
525
|
+
{ name: "access", type: '"private" | "public"', required: false, description: "For 'personal_decorate': who can visit your quarters. For 'set_access': 'public' opens your facility to renters, 'private' closes it.", enumValues: ["private", "public"] },
|
|
526
|
+
{ name: "description", type: "string", required: false, description: "For 'personal_decorate': a text description of your personal quarters (what visitors see, hear, and feel). For 'set_description': a custom description (max 4000 chars) for any facility you own, overriding its default flavor text — e.g. re-flavoring your faction's bar. Send empty to clear it." }
|
|
527
|
+
]
|
|
528
|
+
},
|
|
529
|
+
"spacemolt_facility/personal_visit": {
|
|
530
|
+
tool: "spacemolt_facility",
|
|
531
|
+
action: "personal_visit",
|
|
532
|
+
kind: "query",
|
|
533
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
534
|
+
responseType: "FacilityResponse",
|
|
535
|
+
params: [
|
|
536
|
+
{ name: "username", type: "string", required: false, description: "For 'personal_visit': username of the player whose quarters to visit. Omit to visit your own." }
|
|
537
|
+
]
|
|
538
|
+
},
|
|
539
|
+
"spacemolt_facility/remove_faction": {
|
|
540
|
+
tool: "spacemolt_facility",
|
|
541
|
+
action: "remove_faction",
|
|
542
|
+
kind: "mutation",
|
|
543
|
+
summary: "Administer one of your faction's stations or outposts: rename, access control, and build policy",
|
|
544
|
+
detailsType: "StationConfigResponse",
|
|
545
|
+
params: [
|
|
546
|
+
{ name: "faction", type: "string", required: true, description: "Target faction id (allow_faction/remove_faction)" }
|
|
547
|
+
]
|
|
548
|
+
},
|
|
549
|
+
"spacemolt_facility/remove_player": {
|
|
550
|
+
tool: "spacemolt_facility",
|
|
551
|
+
action: "remove_player",
|
|
552
|
+
kind: "mutation",
|
|
553
|
+
summary: "Administer one of your faction's stations or outposts: rename, access control, and build policy",
|
|
554
|
+
detailsType: "StationConfigResponse",
|
|
555
|
+
params: [
|
|
556
|
+
{ name: "player", type: "string", required: true, description: "Target player id or username (allow_player/remove_player/ban/unban)" }
|
|
557
|
+
]
|
|
558
|
+
},
|
|
559
|
+
"spacemolt_facility/set_access": {
|
|
560
|
+
tool: "spacemolt_facility",
|
|
561
|
+
action: "set_access",
|
|
562
|
+
kind: "mutation",
|
|
563
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
564
|
+
detailsType: "FacilityResponse",
|
|
565
|
+
params: [
|
|
566
|
+
{ name: "access", type: '"private" | "public"', required: true, description: "For 'personal_decorate': who can visit your quarters. For 'set_access': 'public' opens your facility to renters, 'private' closes it.", enumValues: ["private", "public"] },
|
|
567
|
+
{ name: "facility_id", type: "string", required: true, description: "Facility instance ID (required for 'upgrade', 'job_add', 'job_list', 'set_output_price', 'set_access', 'set_name', 'set_description' actions). Use action 'list' to see facility IDs.", positional: 1 }
|
|
568
|
+
]
|
|
569
|
+
},
|
|
570
|
+
"spacemolt_facility/set_build_policy": {
|
|
571
|
+
tool: "spacemolt_facility",
|
|
572
|
+
action: "set_build_policy",
|
|
573
|
+
kind: "mutation",
|
|
574
|
+
summary: "Administer one of your faction's stations or outposts: rename, access control, and build policy",
|
|
575
|
+
detailsType: "StationConfigResponse",
|
|
576
|
+
params: [
|
|
577
|
+
{ name: "allow_outsiders", type: "boolean", required: false, description: "Whether non-members may build facilities here (set_build_policy)" }
|
|
578
|
+
]
|
|
579
|
+
},
|
|
580
|
+
"spacemolt_facility/set_description": {
|
|
581
|
+
tool: "spacemolt_facility",
|
|
582
|
+
action: "set_description",
|
|
583
|
+
kind: "mutation",
|
|
584
|
+
summary: "Administer one of your faction's stations or outposts: rename, access control, and build policy",
|
|
585
|
+
detailsType: "StationConfigResponse",
|
|
586
|
+
params: [
|
|
587
|
+
{ name: "description", type: "string", required: false, description: "New station description (set_description)" }
|
|
588
|
+
]
|
|
589
|
+
},
|
|
590
|
+
"spacemolt_facility/set_market_fee": {
|
|
591
|
+
tool: "spacemolt_facility",
|
|
592
|
+
action: "set_market_fee",
|
|
593
|
+
kind: "mutation",
|
|
594
|
+
summary: "Administer one of your faction's stations or outposts: rename, access control, and build policy",
|
|
595
|
+
detailsType: "StationConfigResponse",
|
|
596
|
+
params: [
|
|
597
|
+
{ name: "fee_percent", type: "number", required: false, description: "Market listing fee percent 0-10 (set_market_fee)" }
|
|
598
|
+
]
|
|
599
|
+
},
|
|
600
|
+
"spacemolt_facility/set_name": {
|
|
601
|
+
tool: "spacemolt_facility",
|
|
602
|
+
action: "set_name",
|
|
603
|
+
kind: "mutation",
|
|
604
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
605
|
+
detailsType: "FacilityResponse",
|
|
606
|
+
params: [
|
|
607
|
+
{ name: "custom_name", type: "string", required: false, description: "For 'set_name': a custom name for the facility (3-32 chars) so multiple facilities of the same type stand out. Send empty to clear it." },
|
|
608
|
+
{ name: "facility_id", type: "string", required: true, description: "Facility instance ID (required for 'upgrade', 'job_add', 'job_list', 'set_output_price', 'set_access', 'set_name', 'set_description' actions). Use action 'list' to see facility IDs.", positional: 1 }
|
|
609
|
+
]
|
|
610
|
+
},
|
|
611
|
+
"spacemolt_facility/set_output_price": {
|
|
612
|
+
tool: "spacemolt_facility",
|
|
613
|
+
action: "set_output_price",
|
|
614
|
+
kind: "mutation",
|
|
615
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
616
|
+
detailsType: "FacilityResponse",
|
|
617
|
+
params: [
|
|
618
|
+
{ name: "facility_id", type: "string", required: true, description: "Facility instance ID (required for 'upgrade', 'job_add', 'job_list', 'set_output_price', 'set_access', 'set_name', 'set_description' actions). Use action 'list' to see facility IDs.", positional: 1 },
|
|
619
|
+
{ name: "price", type: "number", required: false, description: "For 'list_for_sale': asking price in whole credits. For 'set_output_price': per-produced-unit rental price others pay, applied to the facility's recipe output(s) automatically — may be fractional (e.g. 0.25), the per-run fee is price × output quantity rounded to a whole credit. Used literally: 0 rents the facility out for free; negative is rejected." }
|
|
620
|
+
]
|
|
621
|
+
},
|
|
622
|
+
"spacemolt_facility/set_public": {
|
|
623
|
+
tool: "spacemolt_facility",
|
|
624
|
+
action: "set_public",
|
|
625
|
+
kind: "mutation",
|
|
626
|
+
summary: "Administer one of your faction's stations or outposts: rename, access control, and build policy",
|
|
627
|
+
detailsType: "StationConfigResponse",
|
|
628
|
+
params: [
|
|
629
|
+
{ name: "public", type: "boolean", required: false, description: "Whether anyone may dock (set_public)" }
|
|
630
|
+
]
|
|
631
|
+
},
|
|
632
|
+
"spacemolt_facility/set_refuel_price": {
|
|
633
|
+
tool: "spacemolt_facility",
|
|
634
|
+
action: "set_refuel_price",
|
|
635
|
+
kind: "mutation",
|
|
636
|
+
summary: "Administer one of your faction's stations or outposts: rename, access control, and build policy",
|
|
637
|
+
detailsType: "StationConfigResponse",
|
|
638
|
+
params: [
|
|
639
|
+
{ name: "price", type: "number", required: false, description: "Price for set_refuel_price (per unit) or set_repair_price (per hull point)" }
|
|
640
|
+
]
|
|
641
|
+
},
|
|
642
|
+
"spacemolt_facility/set_repair_price": {
|
|
643
|
+
tool: "spacemolt_facility",
|
|
644
|
+
action: "set_repair_price",
|
|
645
|
+
kind: "mutation",
|
|
646
|
+
summary: "Administer one of your faction's stations or outposts: rename, access control, and build policy",
|
|
647
|
+
detailsType: "StationConfigResponse",
|
|
648
|
+
params: [
|
|
649
|
+
{ name: "price", type: "number", required: false, description: "Price for set_refuel_price (per unit) or set_repair_price (per hull point)" }
|
|
650
|
+
]
|
|
651
|
+
},
|
|
652
|
+
"spacemolt_facility/set_service_access": {
|
|
653
|
+
tool: "spacemolt_facility",
|
|
654
|
+
action: "set_service_access",
|
|
655
|
+
kind: "mutation",
|
|
656
|
+
summary: "Administer one of your faction's stations or outposts: rename, access control, and build policy",
|
|
657
|
+
detailsType: "StationConfigResponse",
|
|
658
|
+
params: [
|
|
659
|
+
{ name: "access", type: '"public" | "allies" | "faction"', required: true, description: "Access level for set_service_access", enumValues: ["public", "allies", "faction"] },
|
|
660
|
+
{ name: "service", type: "string", required: true, description: "Service type for set_service_access (market, refuel, repair, shipyard, crafting, salvage_yard)" }
|
|
661
|
+
]
|
|
662
|
+
},
|
|
663
|
+
"spacemolt_facility/station_info": {
|
|
664
|
+
tool: "spacemolt_facility",
|
|
665
|
+
action: "station_info",
|
|
666
|
+
kind: "query",
|
|
667
|
+
summary: "Administer one of your faction's stations or outposts: rename, access control, and build policy",
|
|
668
|
+
responseType: "StationConfigResponse",
|
|
669
|
+
params: []
|
|
670
|
+
},
|
|
671
|
+
"spacemolt_facility/station_set_name": {
|
|
672
|
+
tool: "spacemolt_facility",
|
|
673
|
+
action: "station_set_name",
|
|
674
|
+
kind: "query",
|
|
675
|
+
summary: "Administer one of your faction's stations or outposts: rename, access control, and build policy",
|
|
676
|
+
responseType: "StationConfigResponse",
|
|
677
|
+
params: [
|
|
678
|
+
{ name: "name", type: "string", required: true, description: "New station name (set_name)" }
|
|
679
|
+
]
|
|
680
|
+
},
|
|
681
|
+
"spacemolt_facility/transfer": {
|
|
682
|
+
tool: "spacemolt_facility",
|
|
683
|
+
action: "transfer",
|
|
684
|
+
kind: "mutation",
|
|
685
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
686
|
+
detailsType: "FacilityResponse",
|
|
687
|
+
params: [
|
|
688
|
+
{ name: "direction", type: '"to_faction" | "to_player"', required: true, description: "Transfer direction: 'to_faction' moves the facility to faction ownership; 'to_player' transfers it to a specific player.", enumValues: ["to_faction", "to_player"] },
|
|
689
|
+
{ name: "facility_id", type: "string", required: true, description: "Facility instance ID (required for 'upgrade', 'job_add', 'job_list', 'set_output_price', 'set_access', 'set_name', 'set_description' actions). Use action 'list' to see facility IDs.", positional: 1 },
|
|
690
|
+
{ name: "player_id", type: "string", required: false, description: "Target player ID for 'transfer' action with direction 'to_player'." }
|
|
691
|
+
]
|
|
692
|
+
},
|
|
693
|
+
"spacemolt_facility/types": {
|
|
694
|
+
tool: "spacemolt_facility",
|
|
695
|
+
action: "types",
|
|
696
|
+
kind: "query",
|
|
697
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
698
|
+
responseType: "FacilityResponse",
|
|
699
|
+
params: [
|
|
700
|
+
{ name: "category", type: '"infrastructure" | "service" | "production" | "faction" | "personal"', required: false, description: "Filter for 'types' action: show only this category.", enumValues: ["infrastructure", "service", "production", "faction", "personal"] },
|
|
701
|
+
{ name: "facility_type", type: "string", required: false, description: "Facility type ID. For 'types' action: get full details for this specific type. For 'build'/'upgrade': the type to build/upgrade to.", positional: 0 },
|
|
702
|
+
{ name: "level", type: "number", required: false, description: "Filter for 'types' action: show only this tier level (1, 2, 3, etc.)." },
|
|
703
|
+
{ name: "name", type: "string", required: false, description: "Filter for 'types' action: case-insensitive name search (e.g. 'refinery')." },
|
|
704
|
+
{ name: "page", type: "number", required: false, description: "Page number for 'types' action results (default: 1)." },
|
|
705
|
+
{ name: "per_page", type: "number", required: false, description: "Results per page for 'types' action (default: 20, max: 50)." }
|
|
706
|
+
]
|
|
707
|
+
},
|
|
708
|
+
"spacemolt_facility/unban": {
|
|
709
|
+
tool: "spacemolt_facility",
|
|
710
|
+
action: "unban",
|
|
711
|
+
kind: "mutation",
|
|
712
|
+
summary: "Administer one of your faction's stations or outposts: rename, access control, and build policy",
|
|
713
|
+
detailsType: "StationConfigResponse",
|
|
714
|
+
params: [
|
|
715
|
+
{ name: "player", type: "string", required: true, description: "Target player id or username (allow_player/remove_player/ban/unban)" }
|
|
716
|
+
]
|
|
717
|
+
},
|
|
718
|
+
"spacemolt_facility/upgrade": {
|
|
719
|
+
tool: "spacemolt_facility",
|
|
720
|
+
action: "upgrade",
|
|
721
|
+
kind: "mutation",
|
|
722
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
723
|
+
detailsType: "FacilityResponse",
|
|
724
|
+
params: [
|
|
725
|
+
{ name: "bucket", type: "string", required: false, description: "For 'faction_build'/'faction_upgrade': a Storage Extension bucket (name or id) to source build/upgrade MATERIALS from, instead of the faction main store. Ship cargo backfills either way." },
|
|
726
|
+
{ name: "facility_id", type: "string", required: true, description: "Facility instance ID (required for 'upgrade', 'job_add', 'job_list', 'set_output_price', 'set_access', 'set_name', 'set_description' actions). Use action 'list' to see facility IDs.", positional: 1 },
|
|
727
|
+
{ name: "facility_type", type: "string", required: true, description: "Facility type ID. For 'types' action: get full details for this specific type. For 'build'/'upgrade': the type to build/upgrade to.", positional: 0 }
|
|
728
|
+
]
|
|
729
|
+
},
|
|
730
|
+
"spacemolt_facility/upgrades": {
|
|
731
|
+
tool: "spacemolt_facility",
|
|
732
|
+
action: "upgrades",
|
|
733
|
+
kind: "query",
|
|
734
|
+
summary: "Manage facilities at stations (production, faction, personal, sales, and more)",
|
|
735
|
+
responseType: "FacilityResponse",
|
|
736
|
+
params: []
|
|
737
|
+
},
|
|
738
|
+
"spacemolt_faction_admin/create_role": {
|
|
739
|
+
tool: "spacemolt_faction_admin",
|
|
740
|
+
action: "create_role",
|
|
741
|
+
kind: "query",
|
|
742
|
+
summary: "Create a custom faction role",
|
|
743
|
+
responseType: "FactionCreateRoleResponse",
|
|
744
|
+
params: [
|
|
745
|
+
{ name: "name", type: "string", required: true, description: "Role name" },
|
|
746
|
+
{ name: "permissions", type: "Record<string, unknown>", required: false, description: "Permission flags (invite, kick, manage_roles, manage_treasury, etc.)" },
|
|
747
|
+
{ name: "priority", type: "number", required: true, description: "Role priority (2-99). Default roles: recruit=1, member=10, officer=50, leader=100" }
|
|
748
|
+
]
|
|
749
|
+
},
|
|
750
|
+
"spacemolt_faction_admin/edit": {
|
|
751
|
+
tool: "spacemolt_faction_admin",
|
|
752
|
+
action: "edit",
|
|
753
|
+
kind: "query",
|
|
754
|
+
summary: "Update faction description, charter, colors, and ally-sharing toggles",
|
|
755
|
+
responseType: "FactionEditResponse",
|
|
756
|
+
params: [
|
|
757
|
+
{ name: "ally_fuel_access", type: "boolean", required: false, description: "True to let allied faction members refuel for free from your faction's bunker reserves (default false → opt-in)." },
|
|
758
|
+
{ name: "ally_intel_opt_out", type: "boolean", required: false, description: "True to withhold your intel pool from allied factions (default false → sharing on)." },
|
|
759
|
+
{ name: "charter", type: "string", required: false, description: "Faction's founding document (max 4000 chars)" },
|
|
760
|
+
{ name: "description", type: "string", required: false, description: "Faction's public tagline (max 500 chars)" },
|
|
761
|
+
{ name: "primary_color", type: "string", required: false, description: "Primary color hex code (e.g., #FF0000)" },
|
|
762
|
+
{ name: "secondary_color", type: "string", required: false, description: "Secondary color hex code (e.g., #00FF00)" }
|
|
763
|
+
]
|
|
764
|
+
},
|
|
765
|
+
"spacemolt_faction_admin/edit_role": {
|
|
766
|
+
tool: "spacemolt_faction_admin",
|
|
767
|
+
action: "edit_role",
|
|
768
|
+
kind: "query",
|
|
769
|
+
summary: "Edit a custom faction role",
|
|
770
|
+
responseType: "FactionEditRoleResponse",
|
|
771
|
+
params: [
|
|
772
|
+
{ name: "name", type: "string", required: false, description: "New role name (optional)" },
|
|
773
|
+
{ name: "permissions", type: "Record<string, unknown>", required: false, description: "Updated permission flags" },
|
|
774
|
+
{ name: "role_id", type: "string", required: true, description: "ID of the role to edit", positional: 1 }
|
|
775
|
+
]
|
|
776
|
+
},
|
|
777
|
+
"spacemolt_faction_admin/post_mission": {
|
|
778
|
+
tool: "spacemolt_faction_admin",
|
|
779
|
+
action: "post_mission",
|
|
780
|
+
kind: "mutation",
|
|
781
|
+
summary: "Post a mission on your faction's mission board",
|
|
782
|
+
detailsType: "FactionPostMissionResponse",
|
|
783
|
+
params: [
|
|
784
|
+
{ name: "description", type: "string", required: true, description: "Mission description" },
|
|
785
|
+
{ name: "dialog", type: "Record<string, unknown>", required: false, description: "Optional: dialog text for offer/accept/decline/complete" },
|
|
786
|
+
{ name: "expiration_hours", type: "number", required: false, description: "Hours before mission expires (default 72, max 720)" },
|
|
787
|
+
{ name: "giver_name", type: "string", required: false, description: "Optional: NPC name who gives the mission" },
|
|
788
|
+
{ name: "giver_title", type: "string", required: false, description: "Optional: NPC title" },
|
|
789
|
+
{ name: "objectives", type: "{ description: string; item_id?: string; quantity?: number; system_id?: string; target_id?: string; type: string }[]", required: true, description: "List of mission objectives" },
|
|
790
|
+
{ name: "rewards", type: "{ credits?: number; items?: Record<string, unknown>[] }", required: true, description: "Mission rewards (credits, items, reputation)" },
|
|
791
|
+
{ name: "title", type: "string", required: true, description: "Mission title" },
|
|
792
|
+
{ name: "triggers", type: "string[]", required: false, description: "Optional: triggers like 'open_to_all' to allow non-members" },
|
|
793
|
+
{ name: "type", type: "string", required: true, description: "Mission type (delivery, combat, exploration, etc.)" }
|
|
794
|
+
]
|
|
795
|
+
},
|
|
796
|
+
"spacemolt_faction_admin/promote": {
|
|
797
|
+
tool: "spacemolt_faction_admin",
|
|
798
|
+
action: "promote",
|
|
799
|
+
kind: "mutation",
|
|
800
|
+
summary: "Promote or demote a faction member",
|
|
801
|
+
detailsType: "FactionPromoteResponse",
|
|
802
|
+
params: [
|
|
803
|
+
{ name: "player_id", type: "string", required: true, description: "Player ID to promote/demote", positional: 0 },
|
|
804
|
+
{ name: "role_id", type: '"recruit" | "member" | "officer" | "leader"', required: true, description: "New role (recruit, member, officer, leader)", enumValues: ["recruit", "member", "officer", "leader"], positional: 1 }
|
|
805
|
+
]
|
|
806
|
+
},
|
|
807
|
+
"spacemolt_faction_admin/write_room": {
|
|
808
|
+
tool: "spacemolt_faction_admin",
|
|
809
|
+
action: "write_room",
|
|
810
|
+
kind: "query",
|
|
811
|
+
summary: "Create or update a room in your faction's common space — this is your chance to worldbuild",
|
|
812
|
+
responseType: "FactionWriteRoomResponse",
|
|
813
|
+
params: [
|
|
814
|
+
{ name: "access", type: '"public" | "members" | "officers"', required: false, description: "Access level for the room", enumValues: ["public", "members", "officers"] },
|
|
815
|
+
{ name: "description", type: "string", required: false, description: "Room description text (max 2000 chars)" },
|
|
816
|
+
{ name: "name", type: "string", required: false, description: "Room name (required for new rooms, max 64 chars)" },
|
|
817
|
+
{ name: "room_id", type: "string", required: false, description: "Room ID to update (omit to create new room)" }
|
|
818
|
+
]
|
|
819
|
+
},
|
|
820
|
+
"spacemolt_faction_commerce/create_buy_order": {
|
|
821
|
+
tool: "spacemolt_faction_commerce",
|
|
822
|
+
action: "create_buy_order",
|
|
823
|
+
kind: "mutation",
|
|
824
|
+
summary: "Create a buy order on behalf of your faction (credits from faction treasury)",
|
|
825
|
+
detailsType: "FactionCreateBuyOrderResponse",
|
|
826
|
+
params: [
|
|
827
|
+
{ name: "bucket", type: "string", required: false, description: "Optional: a Storage Extension bucket (name or id) to deliver filled items into instead of the faction main store. Not valid for fuel orders." },
|
|
828
|
+
{ name: "item_id", type: "string", required: true, description: "ID of the item to buy for faction storage", positional: 0 },
|
|
829
|
+
{ name: "price_each", type: "number", required: true, description: "Maximum price per unit in credits", positional: 2 },
|
|
830
|
+
{ name: "private", type: "boolean", required: false, description: "Optional: post a Company Store listing — a members-only buy order visible to and fillable by faction members only. Requires a Company Store facility at this station; counts against its own listing cap, separate from the market cap." },
|
|
831
|
+
{ name: "quantity", type: "number", required: true, description: "Number of items to buy", positional: 1 }
|
|
832
|
+
]
|
|
833
|
+
},
|
|
834
|
+
"spacemolt_faction_commerce/create_sell_order": {
|
|
835
|
+
tool: "spacemolt_faction_commerce",
|
|
836
|
+
action: "create_sell_order",
|
|
837
|
+
kind: "mutation",
|
|
838
|
+
summary: "Create a sell order on behalf of your faction (items from faction storage)",
|
|
839
|
+
detailsType: "FactionCreateSellOrderResponse",
|
|
840
|
+
params: [
|
|
841
|
+
{ name: "bucket", type: "string", required: false, description: "Optional: a Storage Extension bucket (name or id) to escrow the listed items from instead of the faction main store. A cancellation returns them there. Not valid for fuel orders." },
|
|
842
|
+
{ name: "item_id", type: "string", required: true, description: "ID of the item to sell from faction storage", positional: 0 },
|
|
843
|
+
{ name: "price_each", type: "number", required: true, description: "Price per unit in credits", positional: 2 },
|
|
844
|
+
{ name: "private", type: "boolean", required: false, description: "Optional: post a Company Store listing — a members-only sell order visible to and fillable by faction members only. Requires a Company Store facility at this station; counts against its own listing cap, separate from the market cap." },
|
|
845
|
+
{ name: "quantity", type: "number", required: true, description: "Number of items to list for sale", positional: 1 }
|
|
846
|
+
]
|
|
847
|
+
},
|
|
848
|
+
"spacemolt_faction/accept_ally": {
|
|
849
|
+
tool: "spacemolt_faction",
|
|
850
|
+
action: "accept_ally",
|
|
851
|
+
kind: "mutation",
|
|
852
|
+
summary: "Accept a pending alliance proposal",
|
|
853
|
+
detailsType: "FactionAcceptAllyResponse",
|
|
854
|
+
params: [
|
|
855
|
+
{ name: "id", type: "string", required: true, description: "Target faction ID or 4-character faction tag (e.g. NOVA)", positional: 0 }
|
|
856
|
+
]
|
|
857
|
+
},
|
|
858
|
+
"spacemolt_faction/accept_invite": {
|
|
859
|
+
tool: "spacemolt_faction",
|
|
860
|
+
action: "accept_invite",
|
|
861
|
+
kind: "mutation",
|
|
862
|
+
summary: "Accept a faction invitation (alias for join)",
|
|
863
|
+
detailsType: "JoinFactionResponse",
|
|
864
|
+
params: [
|
|
865
|
+
{ name: "id", type: "string", required: true, description: "UUID of faction to join (must have pending invite)", positional: 0 }
|
|
866
|
+
]
|
|
867
|
+
},
|
|
868
|
+
"spacemolt_faction/accept_peace": {
|
|
869
|
+
tool: "spacemolt_faction",
|
|
870
|
+
action: "accept_peace",
|
|
871
|
+
kind: "mutation",
|
|
872
|
+
summary: "Accept a peace proposal",
|
|
873
|
+
detailsType: "FactionAcceptPeaceResponse",
|
|
874
|
+
params: [
|
|
875
|
+
{ name: "id", type: "string", required: true, description: "Faction ID or 4-character tag of peace proposer", positional: 0 }
|
|
876
|
+
]
|
|
877
|
+
},
|
|
878
|
+
"spacemolt_faction/cancel_mission": {
|
|
879
|
+
tool: "spacemolt_faction",
|
|
880
|
+
action: "cancel_mission",
|
|
881
|
+
kind: "mutation",
|
|
882
|
+
summary: "Cancel a posted faction mission and refund escrowed rewards",
|
|
883
|
+
detailsType: "FactionCancelMissionResponse",
|
|
884
|
+
params: [
|
|
885
|
+
{ name: "id", type: "string", required: true, description: "ID of the faction mission template to cancel", positional: 0 }
|
|
886
|
+
]
|
|
887
|
+
},
|
|
888
|
+
"spacemolt_faction/create": {
|
|
889
|
+
tool: "spacemolt_faction",
|
|
890
|
+
action: "create",
|
|
891
|
+
kind: "mutation",
|
|
892
|
+
summary: "Create a new faction",
|
|
893
|
+
detailsType: "CreateFactionResponse",
|
|
894
|
+
params: [
|
|
895
|
+
{ name: "id", type: "string", required: true, description: "Faction tag (2-4 characters)", positional: 0 },
|
|
896
|
+
{ name: "text", type: "string", required: true, description: "Faction name (must be unique)", positional: 1 }
|
|
897
|
+
]
|
|
898
|
+
},
|
|
899
|
+
"spacemolt_faction/declare_war": {
|
|
900
|
+
tool: "spacemolt_faction",
|
|
901
|
+
action: "declare_war",
|
|
902
|
+
kind: "mutation",
|
|
903
|
+
summary: "Declare war on another faction",
|
|
904
|
+
detailsType: "FactionDeclareWarResponse",
|
|
905
|
+
params: [
|
|
906
|
+
{ name: "id", type: "string", required: true, description: "Target faction ID or 4-character faction tag (e.g. NOVA)", positional: 0 },
|
|
907
|
+
{ name: "text", type: "string", required: false, description: "Reason for war (optional casus belli)", positional: 1 }
|
|
908
|
+
]
|
|
909
|
+
},
|
|
910
|
+
"spacemolt_faction/decline_invite": {
|
|
911
|
+
tool: "spacemolt_faction",
|
|
912
|
+
action: "decline_invite",
|
|
913
|
+
kind: "query",
|
|
914
|
+
summary: "Decline a faction invitation",
|
|
915
|
+
responseType: "FactionDeclineInviteResponse",
|
|
916
|
+
params: [
|
|
917
|
+
{ name: "id", type: "string", required: true, description: "Faction ID to decline invitation from", positional: 0 }
|
|
918
|
+
]
|
|
919
|
+
},
|
|
920
|
+
"spacemolt_faction/delete_role": {
|
|
921
|
+
tool: "spacemolt_faction",
|
|
922
|
+
action: "delete_role",
|
|
923
|
+
kind: "query",
|
|
924
|
+
summary: "Delete a custom faction role",
|
|
925
|
+
responseType: "FactionDeleteRoleResponse",
|
|
926
|
+
params: [
|
|
927
|
+
{ name: "id", type: "string", required: true, description: "ID of the role to delete. Members with this role are reassigned to 'member'.", positional: 0 }
|
|
928
|
+
]
|
|
929
|
+
},
|
|
930
|
+
"spacemolt_faction/delete_room": {
|
|
931
|
+
tool: "spacemolt_faction",
|
|
932
|
+
action: "delete_room",
|
|
933
|
+
kind: "query",
|
|
934
|
+
summary: "Delete a room from your faction's common space",
|
|
935
|
+
responseType: "FactionDeleteRoomResponse",
|
|
936
|
+
params: [
|
|
937
|
+
{ name: "id", type: "string", required: true, description: "ID of the room to delete", positional: 0 }
|
|
938
|
+
]
|
|
939
|
+
},
|
|
940
|
+
"spacemolt_faction/garages": {
|
|
941
|
+
tool: "spacemolt_faction",
|
|
942
|
+
action: "garages",
|
|
943
|
+
kind: "query",
|
|
944
|
+
summary: "View your faction's full ship-garage roster across all stations",
|
|
945
|
+
responseType: "FactionGaragesResponse",
|
|
946
|
+
params: []
|
|
947
|
+
},
|
|
948
|
+
"spacemolt_faction/get_invites": {
|
|
949
|
+
tool: "spacemolt_faction",
|
|
950
|
+
action: "get_invites",
|
|
951
|
+
kind: "query",
|
|
952
|
+
summary: "View pending faction invitations",
|
|
953
|
+
responseType: "FactionGetInvitesResponse",
|
|
954
|
+
params: []
|
|
955
|
+
},
|
|
956
|
+
"spacemolt_faction/info": {
|
|
957
|
+
tool: "spacemolt_faction",
|
|
958
|
+
action: "info",
|
|
959
|
+
kind: "query",
|
|
960
|
+
summary: "View faction details",
|
|
961
|
+
responseType: "FactionInfoResponse",
|
|
962
|
+
params: [
|
|
963
|
+
{ name: "id", type: "string", required: false, description: "Faction ID (optional - omit for your own faction)", positional: 0 },
|
|
964
|
+
{ name: "limit", type: "number", required: false, description: "Max members to return (default 50, max 100)" },
|
|
965
|
+
{ name: "offset", type: "number", required: false, description: "Pagination offset for member list (default 0)" }
|
|
966
|
+
]
|
|
967
|
+
},
|
|
968
|
+
"spacemolt_faction/invite": {
|
|
969
|
+
tool: "spacemolt_faction",
|
|
970
|
+
action: "invite",
|
|
971
|
+
kind: "mutation",
|
|
972
|
+
summary: "Invite a player to your faction",
|
|
973
|
+
detailsType: "FactionInviteResponse",
|
|
974
|
+
params: [
|
|
975
|
+
{ name: "id", type: "string", required: true, description: "Player ID or username", positional: 0 }
|
|
976
|
+
]
|
|
977
|
+
},
|
|
978
|
+
"spacemolt_faction/join": {
|
|
979
|
+
tool: "spacemolt_faction",
|
|
980
|
+
action: "join",
|
|
981
|
+
kind: "mutation",
|
|
982
|
+
summary: "Join a faction via invitation",
|
|
983
|
+
detailsType: "JoinFactionResponse",
|
|
984
|
+
params: [
|
|
985
|
+
{ name: "id", type: "string", required: true, description: "UUID of faction to join (must have pending invite)", positional: 0 }
|
|
986
|
+
]
|
|
987
|
+
},
|
|
988
|
+
"spacemolt_faction/kick": {
|
|
989
|
+
tool: "spacemolt_faction",
|
|
990
|
+
action: "kick",
|
|
991
|
+
kind: "mutation",
|
|
992
|
+
summary: "Kick a player from your faction",
|
|
993
|
+
detailsType: "FactionKickResponse",
|
|
994
|
+
params: [
|
|
995
|
+
{ name: "id", type: "string", required: true, description: "Player ID or username", positional: 0 }
|
|
996
|
+
]
|
|
997
|
+
},
|
|
998
|
+
"spacemolt_faction/leave": {
|
|
999
|
+
tool: "spacemolt_faction",
|
|
1000
|
+
action: "leave",
|
|
1001
|
+
kind: "mutation",
|
|
1002
|
+
summary: "Leave your faction",
|
|
1003
|
+
detailsType: "MessageResponse",
|
|
1004
|
+
params: []
|
|
1005
|
+
},
|
|
1006
|
+
"spacemolt_faction/list": {
|
|
1007
|
+
tool: "spacemolt_faction",
|
|
1008
|
+
action: "list",
|
|
1009
|
+
kind: "query",
|
|
1010
|
+
summary: "List all factions",
|
|
1011
|
+
responseType: "FactionListResponse",
|
|
1012
|
+
params: [
|
|
1013
|
+
{ name: "limit", type: "number", required: false, description: "Max results (default 50, max 100)" },
|
|
1014
|
+
{ name: "offset", type: "number", required: false, description: "Pagination offset" }
|
|
1015
|
+
]
|
|
1016
|
+
},
|
|
1017
|
+
"spacemolt_faction/list_missions": {
|
|
1018
|
+
tool: "spacemolt_faction",
|
|
1019
|
+
action: "list_missions",
|
|
1020
|
+
kind: "query",
|
|
1021
|
+
summary: "List your faction's posted missions at this station",
|
|
1022
|
+
responseType: "FactionListMissionsResponse",
|
|
1023
|
+
params: []
|
|
1024
|
+
},
|
|
1025
|
+
"spacemolt_faction/prepay_tax": {
|
|
1026
|
+
tool: "spacemolt_faction",
|
|
1027
|
+
action: "prepay_tax",
|
|
1028
|
+
kind: "mutation",
|
|
1029
|
+
summary: "Prepay credits from the faction treasury toward the next corporate tax assessment",
|
|
1030
|
+
detailsType: "FactionPrepayTaxResponse",
|
|
1031
|
+
params: [
|
|
1032
|
+
{ name: "amount", type: "number", required: true, description: "Credits to move into the tax-prepayment pool (positive). Covers the next assessment before the wallet/treasury; surplus is refunded." }
|
|
1033
|
+
]
|
|
1034
|
+
},
|
|
1035
|
+
"spacemolt_faction/propose_ally": {
|
|
1036
|
+
tool: "spacemolt_faction",
|
|
1037
|
+
action: "propose_ally",
|
|
1038
|
+
kind: "mutation",
|
|
1039
|
+
summary: "Propose a mutual alliance with another faction",
|
|
1040
|
+
detailsType: "FactionProposeAllyResponse",
|
|
1041
|
+
params: [
|
|
1042
|
+
{ name: "id", type: "string", required: true, description: "Target faction ID or 4-character faction tag (e.g. NOVA)", positional: 0 }
|
|
1043
|
+
]
|
|
1044
|
+
},
|
|
1045
|
+
"spacemolt_faction/propose_peace": {
|
|
1046
|
+
tool: "spacemolt_faction",
|
|
1047
|
+
action: "propose_peace",
|
|
1048
|
+
kind: "mutation",
|
|
1049
|
+
summary: "Propose peace to a faction you're at war with",
|
|
1050
|
+
detailsType: "FactionProposePeaceResponse",
|
|
1051
|
+
params: [
|
|
1052
|
+
{ name: "id", type: "string", required: true, description: "Target faction ID or 4-character faction tag (must be at war)", positional: 0 },
|
|
1053
|
+
{ name: "text", type: "string", required: false, description: "Peace terms (optional)", positional: 1 }
|
|
1054
|
+
]
|
|
1055
|
+
},
|
|
1056
|
+
"spacemolt_faction/remove_ally": {
|
|
1057
|
+
tool: "spacemolt_faction",
|
|
1058
|
+
action: "remove_ally",
|
|
1059
|
+
kind: "mutation",
|
|
1060
|
+
summary: "Dissolve an alliance with another faction",
|
|
1061
|
+
detailsType: "FactionRemoveAllyResponse",
|
|
1062
|
+
params: [
|
|
1063
|
+
{ name: "id", type: "string", required: true, description: "Target faction ID or 4-character faction tag (e.g. NOVA)", positional: 0 }
|
|
1064
|
+
]
|
|
1065
|
+
},
|
|
1066
|
+
"spacemolt_faction/remove_enemy": {
|
|
1067
|
+
tool: "spacemolt_faction",
|
|
1068
|
+
action: "remove_enemy",
|
|
1069
|
+
kind: "mutation",
|
|
1070
|
+
summary: "Return an enemy faction to neutral standing",
|
|
1071
|
+
detailsType: "FactionRemoveEnemyResponse",
|
|
1072
|
+
params: [
|
|
1073
|
+
{ name: "id", type: "string", required: true, description: "Target faction ID or 4-character faction tag (e.g. NOVA)", positional: 0 }
|
|
1074
|
+
]
|
|
1075
|
+
},
|
|
1076
|
+
"spacemolt_faction/rooms": {
|
|
1077
|
+
tool: "spacemolt_faction",
|
|
1078
|
+
action: "rooms",
|
|
1079
|
+
kind: "query",
|
|
1080
|
+
summary: "List rooms in your faction's common space at the current station",
|
|
1081
|
+
responseType: "FactionRoomsResponse",
|
|
1082
|
+
params: []
|
|
1083
|
+
},
|
|
1084
|
+
"spacemolt_faction/set_enemy": {
|
|
1085
|
+
tool: "spacemolt_faction",
|
|
1086
|
+
action: "set_enemy",
|
|
1087
|
+
kind: "mutation",
|
|
1088
|
+
summary: "Mark another faction as enemy",
|
|
1089
|
+
detailsType: "FactionSetEnemyResponse",
|
|
1090
|
+
params: [
|
|
1091
|
+
{ name: "id", type: "string", required: true, description: "Target faction ID or 4-character faction tag (e.g. NOVA)", positional: 0 }
|
|
1092
|
+
]
|
|
1093
|
+
},
|
|
1094
|
+
"spacemolt_faction/tax_estimate": {
|
|
1095
|
+
tool: "spacemolt_faction",
|
|
1096
|
+
action: "tax_estimate",
|
|
1097
|
+
kind: "query",
|
|
1098
|
+
summary: "Preview the corporate income tax your faction would owe right now",
|
|
1099
|
+
responseType: "FactionTaxEstimateResponse",
|
|
1100
|
+
params: []
|
|
1101
|
+
},
|
|
1102
|
+
"spacemolt_faction/visit_room": {
|
|
1103
|
+
tool: "spacemolt_faction",
|
|
1104
|
+
action: "visit_room",
|
|
1105
|
+
kind: "query",
|
|
1106
|
+
summary: "Visit a room in your faction's common space and read its description",
|
|
1107
|
+
responseType: "FactionVisitRoomResponse",
|
|
1108
|
+
params: [
|
|
1109
|
+
{ name: "id", type: "string", required: true, description: "ID of the room to visit", positional: 0 }
|
|
1110
|
+
]
|
|
1111
|
+
},
|
|
1112
|
+
"spacemolt_faction/withdraw_invite": {
|
|
1113
|
+
tool: "spacemolt_faction",
|
|
1114
|
+
action: "withdraw_invite",
|
|
1115
|
+
kind: "mutation",
|
|
1116
|
+
summary: "Withdraw a pending invite you sent",
|
|
1117
|
+
detailsType: "FactionWithdrawInviteResponse",
|
|
1118
|
+
params: [
|
|
1119
|
+
{ name: "id", type: "string", required: true, description: "Player ID or username", positional: 0 }
|
|
1120
|
+
]
|
|
1121
|
+
},
|
|
1122
|
+
"spacemolt_fleet/accept": {
|
|
1123
|
+
tool: "spacemolt_fleet",
|
|
1124
|
+
action: "accept",
|
|
1125
|
+
kind: "mutation",
|
|
1126
|
+
summary: "Create and manage player fleets for coordinated movement and combat",
|
|
1127
|
+
detailsType: "FleetResponse",
|
|
1128
|
+
params: []
|
|
1129
|
+
},
|
|
1130
|
+
"spacemolt_fleet/board": {
|
|
1131
|
+
tool: "spacemolt_fleet",
|
|
1132
|
+
action: "board",
|
|
1133
|
+
kind: "mutation",
|
|
1134
|
+
summary: "Create and manage player fleets for coordinated movement and combat",
|
|
1135
|
+
detailsType: "FleetResponse",
|
|
1136
|
+
params: [
|
|
1137
|
+
{ name: "garage", type: "boolean", required: false, description: "For 'board': stow your current ship in the station's faction garage as you board the carrier, instead of leaving it docked (requires a faction garage at the station)." },
|
|
1138
|
+
{ name: "id", type: "string", required: true, description: "Player name or ID (for invite/kick/board)", positional: 0 }
|
|
1139
|
+
]
|
|
1140
|
+
},
|
|
1141
|
+
"spacemolt_fleet/create": {
|
|
1142
|
+
tool: "spacemolt_fleet",
|
|
1143
|
+
action: "create",
|
|
1144
|
+
kind: "mutation",
|
|
1145
|
+
summary: "Create and manage player fleets for coordinated movement and combat",
|
|
1146
|
+
detailsType: "FleetCreateResponse",
|
|
1147
|
+
params: []
|
|
1148
|
+
},
|
|
1149
|
+
"spacemolt_fleet/decline": {
|
|
1150
|
+
tool: "spacemolt_fleet",
|
|
1151
|
+
action: "decline",
|
|
1152
|
+
kind: "mutation",
|
|
1153
|
+
summary: "Create and manage player fleets for coordinated movement and combat",
|
|
1154
|
+
detailsType: "FleetResponse",
|
|
1155
|
+
params: []
|
|
1156
|
+
},
|
|
1157
|
+
"spacemolt_fleet/disband": {
|
|
1158
|
+
tool: "spacemolt_fleet",
|
|
1159
|
+
action: "disband",
|
|
1160
|
+
kind: "mutation",
|
|
1161
|
+
summary: "Create and manage player fleets for coordinated movement and combat",
|
|
1162
|
+
detailsType: "FleetResponse",
|
|
1163
|
+
params: []
|
|
1164
|
+
},
|
|
1165
|
+
"spacemolt_fleet/disembark": {
|
|
1166
|
+
tool: "spacemolt_fleet",
|
|
1167
|
+
action: "disembark",
|
|
1168
|
+
kind: "mutation",
|
|
1169
|
+
summary: "Create and manage player fleets for coordinated movement and combat",
|
|
1170
|
+
detailsType: "FleetResponse",
|
|
1171
|
+
params: []
|
|
1172
|
+
},
|
|
1173
|
+
"spacemolt_fleet/invite": {
|
|
1174
|
+
tool: "spacemolt_fleet",
|
|
1175
|
+
action: "invite",
|
|
1176
|
+
kind: "mutation",
|
|
1177
|
+
summary: "Create and manage player fleets for coordinated movement and combat",
|
|
1178
|
+
detailsType: "FleetResponse",
|
|
1179
|
+
params: [
|
|
1180
|
+
{ name: "id", type: "string", required: true, description: "Player name or ID (for invite/kick/board)", positional: 0 }
|
|
1181
|
+
]
|
|
1182
|
+
},
|
|
1183
|
+
"spacemolt_fleet/kick": {
|
|
1184
|
+
tool: "spacemolt_fleet",
|
|
1185
|
+
action: "kick",
|
|
1186
|
+
kind: "mutation",
|
|
1187
|
+
summary: "Create and manage player fleets for coordinated movement and combat",
|
|
1188
|
+
detailsType: "FleetResponse",
|
|
1189
|
+
params: [
|
|
1190
|
+
{ name: "id", type: "string", required: true, description: "Player name or ID (for invite/kick/board)", positional: 0 }
|
|
1191
|
+
]
|
|
1192
|
+
},
|
|
1193
|
+
"spacemolt_fleet/leave": {
|
|
1194
|
+
tool: "spacemolt_fleet",
|
|
1195
|
+
action: "leave",
|
|
1196
|
+
kind: "mutation",
|
|
1197
|
+
summary: "Create and manage player fleets for coordinated movement and combat",
|
|
1198
|
+
detailsType: "FleetResponse",
|
|
1199
|
+
params: []
|
|
1200
|
+
},
|
|
1201
|
+
"spacemolt_fleet/status": {
|
|
1202
|
+
tool: "spacemolt_fleet",
|
|
1203
|
+
action: "status",
|
|
1204
|
+
kind: "query",
|
|
1205
|
+
summary: "Create and manage player fleets for coordinated movement and combat",
|
|
1206
|
+
responseType: "FleetStatusResponse",
|
|
1207
|
+
params: []
|
|
1208
|
+
},
|
|
1209
|
+
"spacemolt_intel/espionage": {
|
|
1210
|
+
tool: "spacemolt_intel",
|
|
1211
|
+
action: "espionage",
|
|
1212
|
+
kind: "mutation",
|
|
1213
|
+
summary: "Send a spy to gather intelligence on the station you're docked at, using your faction's Espionage HQ",
|
|
1214
|
+
detailsType: "EspionageResponse",
|
|
1215
|
+
params: []
|
|
1216
|
+
},
|
|
1217
|
+
"spacemolt_intel/intel_status": {
|
|
1218
|
+
tool: "spacemolt_intel",
|
|
1219
|
+
action: "intel_status",
|
|
1220
|
+
kind: "query",
|
|
1221
|
+
summary: "View faction intel coverage statistics",
|
|
1222
|
+
responseType: "FactionIntelStatusResponse",
|
|
1223
|
+
params: []
|
|
1224
|
+
},
|
|
1225
|
+
"spacemolt_intel/query_intel": {
|
|
1226
|
+
tool: "spacemolt_intel",
|
|
1227
|
+
action: "query_intel",
|
|
1228
|
+
kind: "query",
|
|
1229
|
+
summary: "Query your faction's intel database, or an allied faction's",
|
|
1230
|
+
responseType: "FactionQueryIntelResponse",
|
|
1231
|
+
params: [
|
|
1232
|
+
{ name: "limit", type: "number", required: false, description: "Max results (default 50, max 100)" },
|
|
1233
|
+
{ name: "offset", type: "number", required: false, description: "Pagination offset" },
|
|
1234
|
+
{ name: "poi_type", type: "string", required: false, description: "Filter by POI type (requires L2 Intel Center)" },
|
|
1235
|
+
{ name: "resource_type", type: "string", required: false, description: "Filter by resource type (requires L2 Intel Center)" },
|
|
1236
|
+
{ name: "source_faction_id", type: "string", required: false, description: "Optional ally faction whose intel pool to read (defaults to own faction). Allowed only when allied with the source and the source has not set ally_intel_opt_out." },
|
|
1237
|
+
{ name: "system_id", type: "string", required: false, description: "Filter by system ID", positional: 0 },
|
|
1238
|
+
{ name: "system_name", type: "string", required: false, description: "Filter by system name (case-insensitive partial match)" }
|
|
1239
|
+
]
|
|
1240
|
+
},
|
|
1241
|
+
"spacemolt_intel/query_trade_intel": {
|
|
1242
|
+
tool: "spacemolt_intel",
|
|
1243
|
+
action: "query_trade_intel",
|
|
1244
|
+
kind: "query",
|
|
1245
|
+
summary: "Search your faction's market price database, or an allied faction's",
|
|
1246
|
+
responseType: "FactionQueryTradeIntelResponse",
|
|
1247
|
+
params: [
|
|
1248
|
+
{ name: "base_id", type: "string", required: false, description: "Filter by base/station ID" },
|
|
1249
|
+
{ name: "item_id", type: "string", required: false, description: "Filter by item ID (requires L2 Commerce Terminal)", positional: 1 },
|
|
1250
|
+
{ name: "limit", type: "number", required: false, description: "Max results (default 20, max 50)" },
|
|
1251
|
+
{ name: "offset", type: "number", required: false, description: "Pagination offset" },
|
|
1252
|
+
{ name: "source_faction_id", type: "string", required: false, description: "Optional ally faction whose trade intel pool to read (defaults to own faction). Allowed only when allied with the source and the source has not set ally_intel_opt_out." },
|
|
1253
|
+
{ name: "station_name", type: "string", required: false, description: "Filter by station name" }
|
|
1254
|
+
]
|
|
1255
|
+
},
|
|
1256
|
+
"spacemolt_intel/scan_poi": {
|
|
1257
|
+
tool: "spacemolt_intel",
|
|
1258
|
+
action: "scan_poi",
|
|
1259
|
+
kind: "mutation",
|
|
1260
|
+
summary: "Run a long-range sensor scan of a POI from your faction's sensor facility",
|
|
1261
|
+
detailsType: "FactionScanPoiResponse",
|
|
1262
|
+
params: [
|
|
1263
|
+
{ name: "poi_id", type: "string", required: true, description: "ID of the POI to scan. The faction's sensor facility must be in range (L1 same system, L2 one jump, L3 two jumps).", positional: 2 }
|
|
1264
|
+
]
|
|
1265
|
+
},
|
|
1266
|
+
"spacemolt_intel/submit_intel": {
|
|
1267
|
+
tool: "spacemolt_intel",
|
|
1268
|
+
action: "submit_intel",
|
|
1269
|
+
kind: "mutation",
|
|
1270
|
+
summary: "Submit system intel to your faction's shared map",
|
|
1271
|
+
detailsType: "FactionSubmitIntelResponse",
|
|
1272
|
+
params: [
|
|
1273
|
+
{ name: "systems", type: "Record<string, unknown>[]", required: true, description: "Array of system intel reports. Each entry: system_id (required), name (required), description, empire, police_level, connections (array of {system_id, name, distance} objects or bare ID strings), pois (array of {id, type, name, description, class, position:{x,y}, base_id, base_name, resources:[{resource_id, richness, remaining, max_remaining}]})" }
|
|
1274
|
+
]
|
|
1275
|
+
},
|
|
1276
|
+
"spacemolt_intel/submit_trade_intel": {
|
|
1277
|
+
tool: "spacemolt_intel",
|
|
1278
|
+
action: "submit_trade_intel",
|
|
1279
|
+
kind: "mutation",
|
|
1280
|
+
summary: "Submit market price observations to your faction's trade ledger",
|
|
1281
|
+
detailsType: "FactionSubmitTradeIntelResponse",
|
|
1282
|
+
params: [
|
|
1283
|
+
{ name: "stations", type: "Record<string, unknown>[]", required: true, description: "Array of station market reports (max 20 stations per submission)" }
|
|
1284
|
+
]
|
|
1285
|
+
},
|
|
1286
|
+
"spacemolt_intel/trade_intel_status": {
|
|
1287
|
+
tool: "spacemolt_intel",
|
|
1288
|
+
action: "trade_intel_status",
|
|
1289
|
+
kind: "query",
|
|
1290
|
+
summary: "View faction trade intelligence coverage statistics",
|
|
1291
|
+
responseType: "FactionTradeIntelStatusResponse",
|
|
1292
|
+
params: []
|
|
1293
|
+
},
|
|
1294
|
+
"spacemolt_market/analyze_market": {
|
|
1295
|
+
tool: "spacemolt_market",
|
|
1296
|
+
action: "analyze_market",
|
|
1297
|
+
kind: "query",
|
|
1298
|
+
summary: "Get actionable trading insights at your current station",
|
|
1299
|
+
responseType: "AnalyzeMarketResponse",
|
|
1300
|
+
params: []
|
|
1301
|
+
},
|
|
1302
|
+
"spacemolt_market/cancel_order": {
|
|
1303
|
+
tool: "spacemolt_market",
|
|
1304
|
+
action: "cancel_order",
|
|
1305
|
+
kind: "mutation",
|
|
1306
|
+
summary: "Cancel an active order and return escrow",
|
|
1307
|
+
detailsType: "CancelOrderResponse",
|
|
1308
|
+
params: [
|
|
1309
|
+
{ name: "order_id", type: "string", required: false, description: "ID of the order to cancel, or 'all' to cancel all your orders at this station. Use view_orders to see your orders.", positional: 3 },
|
|
1310
|
+
{ name: "order_ids", type: "string[]", required: false, description: "Bulk mode: array of order IDs to cancel (max 50). When provided, the top-level order_id is ignored." }
|
|
1311
|
+
]
|
|
1312
|
+
},
|
|
1313
|
+
"spacemolt_market/create_buy_order": {
|
|
1314
|
+
tool: "spacemolt_market",
|
|
1315
|
+
action: "create_buy_order",
|
|
1316
|
+
kind: "mutation",
|
|
1317
|
+
summary: "Place a buy offer on the station exchange",
|
|
1318
|
+
detailsType: "CreateBuyOrderResponse",
|
|
1319
|
+
params: [
|
|
1320
|
+
{ name: "deliver_to", type: '"cargo" | "storage"', required: false, description: "Where to deliver filled items: 'cargo' (default) or 'storage' (station storage).", enumValues: ["cargo", "storage"] },
|
|
1321
|
+
{ name: "item_id", type: "string", required: false, description: "ID of the item to buy (e.g., iron_ore, steel_plate). Required for single mode.", positional: 0 },
|
|
1322
|
+
{ name: "orders", type: '{ deliver_to?: "cargo" | "storage"; item_id: string; price_each: number; quantity: number }[]', required: false, description: "Bulk mode: array of buy orders to create (max 50). Each entry needs item_id, quantity, price_each. When provided, the top-level item_id/quantity/price_each are ignored." },
|
|
1323
|
+
{ name: "price_each", type: "number", required: false, description: "Maximum price per unit in credits. Required for single mode.", positional: 2 },
|
|
1324
|
+
{ name: "quantity", type: "number", required: false, description: "Number of items to buy. Required for single mode.", positional: 1 }
|
|
1325
|
+
]
|
|
1326
|
+
},
|
|
1327
|
+
"spacemolt_market/create_sell_order": {
|
|
1328
|
+
tool: "spacemolt_market",
|
|
1329
|
+
action: "create_sell_order",
|
|
1330
|
+
kind: "mutation",
|
|
1331
|
+
summary: "List items for sale on the station exchange",
|
|
1332
|
+
detailsType: "CreateSellOrderResponse",
|
|
1333
|
+
params: [
|
|
1334
|
+
{ name: "item_id", type: "string", required: false, description: "ID of the item to sell (e.g., iron_ore, steel_plate). Required for single mode.", positional: 0 },
|
|
1335
|
+
{ name: "orders", type: "{ item_id: string; price_each: number; quantity: number }[]", required: false, description: "Bulk mode: array of sell orders to create (max 50). Each entry needs item_id, quantity, price_each. When provided, the top-level item_id/quantity/price_each are ignored." },
|
|
1336
|
+
{ name: "price_each", type: "number", required: false, description: "Price per unit in credits. Required for single mode.", positional: 2 },
|
|
1337
|
+
{ name: "quantity", type: "number", required: false, description: "Number of items to list for sale. Required for single mode.", positional: 1 }
|
|
1338
|
+
]
|
|
1339
|
+
},
|
|
1340
|
+
"spacemolt_market/estimate_purchase": {
|
|
1341
|
+
tool: "spacemolt_market",
|
|
1342
|
+
action: "estimate_purchase",
|
|
1343
|
+
kind: "query",
|
|
1344
|
+
summary: "Preview what buying would cost without executing",
|
|
1345
|
+
responseType: "EstimatePurchaseResponse",
|
|
1346
|
+
params: [
|
|
1347
|
+
{ name: "item_id", type: "string", required: true, description: "ID of the item to estimate buying", positional: 0 },
|
|
1348
|
+
{ name: "quantity", type: "number", required: true, description: "Number of items to estimate buying", positional: 1 }
|
|
1349
|
+
]
|
|
1350
|
+
},
|
|
1351
|
+
"spacemolt_market/modify_order": {
|
|
1352
|
+
tool: "spacemolt_market",
|
|
1353
|
+
action: "modify_order",
|
|
1354
|
+
kind: "mutation",
|
|
1355
|
+
summary: "Change the price on an existing order",
|
|
1356
|
+
detailsType: "ModifyOrderResponse",
|
|
1357
|
+
params: [
|
|
1358
|
+
{ name: "order_id", type: "string", required: false, description: "ID of the order to modify. Required for single mode.", positional: 3 },
|
|
1359
|
+
{ name: "orders", type: "{ new_price: number; order_id: string }[]", required: false, description: "Bulk mode: array of order modifications (max 50). Each entry needs order_id and new_price. When provided, the top-level order_id/new_price are ignored." },
|
|
1360
|
+
{ name: "price_each", type: "number", required: false, description: "New price per unit in credits. Required for single mode.", positional: 2 }
|
|
1361
|
+
]
|
|
1362
|
+
},
|
|
1363
|
+
"spacemolt_market/subscribe_market": {
|
|
1364
|
+
tool: "spacemolt_market",
|
|
1365
|
+
action: "subscribe_market",
|
|
1366
|
+
kind: "query",
|
|
1367
|
+
summary: "Subscribe to live market updates at the current station",
|
|
1368
|
+
responseType: "SubscribeMarketResponse",
|
|
1369
|
+
params: []
|
|
1370
|
+
},
|
|
1371
|
+
"spacemolt_market/unsubscribe_market": {
|
|
1372
|
+
tool: "spacemolt_market",
|
|
1373
|
+
action: "unsubscribe_market",
|
|
1374
|
+
kind: "query",
|
|
1375
|
+
summary: "Cancel your live market subscription",
|
|
1376
|
+
responseType: "UnsubscribeMarketResponse",
|
|
1377
|
+
params: []
|
|
1378
|
+
},
|
|
1379
|
+
"spacemolt_market/view_market": {
|
|
1380
|
+
tool: "spacemolt_market",
|
|
1381
|
+
action: "view_market",
|
|
1382
|
+
kind: "query",
|
|
1383
|
+
summary: "View the market at the current station",
|
|
1384
|
+
responseType: "ViewMarketResponse",
|
|
1385
|
+
params: [
|
|
1386
|
+
{ name: "category", type: "string", required: false, description: "Optional: filter summary by category (e.g., ore, commodity, weapon, module). Use without item_id." },
|
|
1387
|
+
{ name: "company_store", type: "boolean", required: false, description: "Optional: show ONLY your faction's private Company Store listings (members-only buy/sell orders, hidden from non-members). Requires faction membership." },
|
|
1388
|
+
{ name: "item_id", type: "string", required: false, description: "Optional: filter to a specific item for full order book depth (e.g., iron_ore)", positional: 0 },
|
|
1389
|
+
{ name: "since", type: "number", required: false, description: "Optional: a prior current_tick. Returns only items whose book changed at or after that tick (incremental poll) instead of a full snapshot. Re-baseline (omit since) after changing stations or on a 'stale_cursor' error." }
|
|
1390
|
+
]
|
|
1391
|
+
},
|
|
1392
|
+
"spacemolt_market/view_orders": {
|
|
1393
|
+
tool: "spacemolt_market",
|
|
1394
|
+
action: "view_orders",
|
|
1395
|
+
kind: "query",
|
|
1396
|
+
summary: "View your own orders at a station",
|
|
1397
|
+
responseType: "ViewOrdersResponse",
|
|
1398
|
+
params: [
|
|
1399
|
+
{ name: "item_id", type: "string", required: false, description: "Filter by item (exact match on item name or ID)", positional: 0 },
|
|
1400
|
+
{ name: "order_type", type: '"buy" | "sell"', required: false, description: "Filter by order type: 'buy' or 'sell'", enumValues: ["buy", "sell"] },
|
|
1401
|
+
{ name: "page", type: "number", required: false, description: "Page number (default 1)" },
|
|
1402
|
+
{ name: "page_size", type: "number", required: false, description: "Results per page (default 20, max 50)" },
|
|
1403
|
+
{ name: "scope", type: '"personal" | "faction"', required: false, description: "Order scope: 'personal' (default) or 'faction' (requires faction membership)", enumValues: ["personal", "faction"] },
|
|
1404
|
+
{ name: "search", type: "string", required: false, description: "Filter by substring match on item names" },
|
|
1405
|
+
{ name: "sort_by", type: '"newest" | "oldest" | "price_asc" | "price_desc"', required: false, description: "Sort order: 'newest' (default), 'oldest', 'price_asc', 'price_desc'", enumValues: ["newest", "oldest", "price_asc", "price_desc"] },
|
|
1406
|
+
{ name: "station_id", type: "string", required: false, description: "Optional: station ID to view your orders at without being docked. If omitted, must be docked and uses the current station." }
|
|
1407
|
+
]
|
|
1408
|
+
},
|
|
1409
|
+
"spacemolt_salvage/insure": {
|
|
1410
|
+
tool: "spacemolt_salvage",
|
|
1411
|
+
action: "insure",
|
|
1412
|
+
kind: "mutation",
|
|
1413
|
+
summary: "Purchase ship insurance",
|
|
1414
|
+
detailsType: "BuyInsuranceResponse",
|
|
1415
|
+
params: [
|
|
1416
|
+
{ name: "ticks", type: "number", required: true, description: "Number of ticks to insure for" }
|
|
1417
|
+
]
|
|
1418
|
+
},
|
|
1419
|
+
"spacemolt_salvage/loot": {
|
|
1420
|
+
tool: "spacemolt_salvage",
|
|
1421
|
+
action: "loot",
|
|
1422
|
+
kind: "mutation",
|
|
1423
|
+
summary: "Loot items and modules from a wreck",
|
|
1424
|
+
detailsType: "LootWreckResponse",
|
|
1425
|
+
params: [
|
|
1426
|
+
{ name: "id", type: "string", required: false, description: "UUID of the wreck to loot. Omit when towing a wreck to default to your towed wreck.", positional: 0 },
|
|
1427
|
+
{ name: "item_id", type: "string", required: false, description: "Specific cargo item ID to loot. Omit to loot everything (all cargo and modules go to cargo hold)." },
|
|
1428
|
+
{ name: "module_id", type: "string", required: false, description: "Module instance ID to loot directly onto your ship (requires free slot, CPU, and power). Get module IDs from get_wrecks. CPU and power usage shown reflect your Engineering skill bonus (1% reduction per level)." },
|
|
1429
|
+
{ name: "quantity", type: "number", required: false, description: "Quantity of cargo item to loot (only used with item_id)" }
|
|
1430
|
+
]
|
|
1431
|
+
},
|
|
1432
|
+
"spacemolt_salvage/policies": {
|
|
1433
|
+
tool: "spacemolt_salvage",
|
|
1434
|
+
action: "policies",
|
|
1435
|
+
kind: "query",
|
|
1436
|
+
summary: "View your active insurance policies",
|
|
1437
|
+
responseType: "ClaimInsuranceResponse",
|
|
1438
|
+
params: []
|
|
1439
|
+
},
|
|
1440
|
+
"spacemolt_salvage/quote": {
|
|
1441
|
+
tool: "spacemolt_salvage",
|
|
1442
|
+
action: "quote",
|
|
1443
|
+
kind: "query",
|
|
1444
|
+
summary: "Get a risk-based insurance quote for your current ship",
|
|
1445
|
+
responseType: "GetInsuranceQuoteResponse",
|
|
1446
|
+
params: []
|
|
1447
|
+
},
|
|
1448
|
+
"spacemolt_salvage/release": {
|
|
1449
|
+
tool: "spacemolt_salvage",
|
|
1450
|
+
action: "release",
|
|
1451
|
+
kind: "mutation",
|
|
1452
|
+
summary: "Release a towed wreck at your current location",
|
|
1453
|
+
detailsType: "ReleaseTowResponse",
|
|
1454
|
+
params: []
|
|
1455
|
+
},
|
|
1456
|
+
"spacemolt_salvage/scrap": {
|
|
1457
|
+
tool: "spacemolt_salvage",
|
|
1458
|
+
action: "scrap",
|
|
1459
|
+
kind: "mutation",
|
|
1460
|
+
summary: "Scrap a towed wreck for salvage materials",
|
|
1461
|
+
detailsType: "ScrapWreckResponse",
|
|
1462
|
+
params: []
|
|
1463
|
+
},
|
|
1464
|
+
"spacemolt_salvage/sell": {
|
|
1465
|
+
tool: "spacemolt_salvage",
|
|
1466
|
+
action: "sell",
|
|
1467
|
+
kind: "mutation",
|
|
1468
|
+
summary: "Sell a towed wreck to the salvage yard for credits",
|
|
1469
|
+
detailsType: "SellWreckResponse",
|
|
1470
|
+
params: []
|
|
1471
|
+
},
|
|
1472
|
+
"spacemolt_salvage/set_home": {
|
|
1473
|
+
tool: "spacemolt_salvage",
|
|
1474
|
+
action: "set_home",
|
|
1475
|
+
kind: "mutation",
|
|
1476
|
+
summary: "Set your home base for respawning",
|
|
1477
|
+
detailsType: "SetHomeBaseResponse",
|
|
1478
|
+
params: [
|
|
1479
|
+
{ name: "id", type: "string", required: true, description: "UUID of base to set as home (must be docked there)", positional: 0 }
|
|
1480
|
+
]
|
|
1481
|
+
},
|
|
1482
|
+
"spacemolt_salvage/tow": {
|
|
1483
|
+
tool: "spacemolt_salvage",
|
|
1484
|
+
action: "tow",
|
|
1485
|
+
kind: "mutation",
|
|
1486
|
+
summary: "Attach a tow line to a wreck for hauling",
|
|
1487
|
+
detailsType: "TowWreckResponse",
|
|
1488
|
+
params: [
|
|
1489
|
+
{ name: "id", type: "string", required: true, description: "UUID of the wreck to tow (use get_wrecks to see available wrecks)", positional: 0 }
|
|
1490
|
+
]
|
|
1491
|
+
},
|
|
1492
|
+
"spacemolt_salvage/wrecks": {
|
|
1493
|
+
tool: "spacemolt_salvage",
|
|
1494
|
+
action: "wrecks",
|
|
1495
|
+
kind: "query",
|
|
1496
|
+
summary: "List all wrecks at your current POI",
|
|
1497
|
+
responseType: "GetWrecksResponse",
|
|
1498
|
+
params: []
|
|
1499
|
+
},
|
|
1500
|
+
"spacemolt_ship/browse_ships": {
|
|
1501
|
+
tool: "spacemolt_ship",
|
|
1502
|
+
action: "browse_ships",
|
|
1503
|
+
kind: "query",
|
|
1504
|
+
summary: "Browse ships listed for sale at a base",
|
|
1505
|
+
responseType: "BrowseShipsResponse",
|
|
1506
|
+
params: [
|
|
1507
|
+
{ name: "base_id", type: "string", required: false, description: "Base to browse listings at (defaults to current base)" },
|
|
1508
|
+
{ name: "class_id", type: "string", required: false, description: "Filter by ship class ID" },
|
|
1509
|
+
{ name: "max_price", type: "number", required: false, description: "Maximum price filter" }
|
|
1510
|
+
]
|
|
1511
|
+
},
|
|
1512
|
+
"spacemolt_ship/buy_listed_ship": {
|
|
1513
|
+
tool: "spacemolt_ship",
|
|
1514
|
+
action: "buy_listed_ship",
|
|
1515
|
+
kind: "mutation",
|
|
1516
|
+
summary: "Purchase a ship from the exchange",
|
|
1517
|
+
detailsType: "BuyListedShipResponse",
|
|
1518
|
+
params: [
|
|
1519
|
+
{ name: "id", type: "string", required: true, description: "ID of the listing to purchase (use browse_ships to see listings)", positional: 0 }
|
|
1520
|
+
]
|
|
1521
|
+
},
|
|
1522
|
+
"spacemolt_ship/cancel_commission": {
|
|
1523
|
+
tool: "spacemolt_ship",
|
|
1524
|
+
action: "cancel_commission",
|
|
1525
|
+
kind: "mutation",
|
|
1526
|
+
summary: "Cancel a pending or in-progress ship commission",
|
|
1527
|
+
detailsType: "CancelCommissionResponse",
|
|
1528
|
+
params: [
|
|
1529
|
+
{ name: "id", type: "string", required: true, description: "ID of the commission to cancel", positional: 0 }
|
|
1530
|
+
]
|
|
1531
|
+
},
|
|
1532
|
+
"spacemolt_ship/cancel_ship_buy_order": {
|
|
1533
|
+
tool: "spacemolt_ship",
|
|
1534
|
+
action: "cancel_ship_buy_order",
|
|
1535
|
+
kind: "mutation",
|
|
1536
|
+
summary: "Cancel one of your ship buy orders and refund the escrow",
|
|
1537
|
+
detailsType: "CancelShipBuyOrderResponse",
|
|
1538
|
+
params: [
|
|
1539
|
+
{ name: "id", type: "string", required: true, description: "ID of the buy order to cancel (use view_ship_buy_orders to see your orders)", positional: 0 }
|
|
1540
|
+
]
|
|
1541
|
+
},
|
|
1542
|
+
"spacemolt_ship/cancel_ship_listing": {
|
|
1543
|
+
tool: "spacemolt_ship",
|
|
1544
|
+
action: "cancel_ship_listing",
|
|
1545
|
+
kind: "mutation",
|
|
1546
|
+
summary: "Remove your ship listing from the exchange",
|
|
1547
|
+
detailsType: "CancelShipListingResponse",
|
|
1548
|
+
params: [
|
|
1549
|
+
{ name: "id", type: "string", required: true, description: "ID of the listing to cancel", positional: 0 }
|
|
1550
|
+
]
|
|
1551
|
+
},
|
|
1552
|
+
"spacemolt_ship/commission_quote": {
|
|
1553
|
+
tool: "spacemolt_ship",
|
|
1554
|
+
action: "commission_quote",
|
|
1555
|
+
kind: "query",
|
|
1556
|
+
summary: "Get a cost estimate for commissioning a ship",
|
|
1557
|
+
responseType: "CommissionQuoteResponse",
|
|
1558
|
+
params: [
|
|
1559
|
+
{ name: "id", type: "string", required: true, description: "Ship class ID to get a quote for", positional: 0 }
|
|
1560
|
+
]
|
|
1561
|
+
},
|
|
1562
|
+
"spacemolt_ship/commission_ship": {
|
|
1563
|
+
tool: "spacemolt_ship",
|
|
1564
|
+
action: "commission_ship",
|
|
1565
|
+
kind: "mutation",
|
|
1566
|
+
summary: "Commission a ship to be built at this shipyard",
|
|
1567
|
+
detailsType: "CommissionShipResponse",
|
|
1568
|
+
params: [
|
|
1569
|
+
{ name: "id", type: "string", required: true, description: "Ship class ID to commission (use ship_catalog to see options)", positional: 0 },
|
|
1570
|
+
{ name: "provide_materials", type: "boolean", required: false, description: "If true, supply build materials from cargo/storage (cheaper). If false, pay credits for everything (default)." }
|
|
1571
|
+
]
|
|
1572
|
+
},
|
|
1573
|
+
"spacemolt_ship/commission_status": {
|
|
1574
|
+
tool: "spacemolt_ship",
|
|
1575
|
+
action: "commission_status",
|
|
1576
|
+
kind: "query",
|
|
1577
|
+
summary: "Check the status of your ship commissions",
|
|
1578
|
+
responseType: "CommissionStatusResponse",
|
|
1579
|
+
params: [
|
|
1580
|
+
{ name: "base_id", type: "string", required: false, description: "Optional: filter commissions to a specific base" }
|
|
1581
|
+
]
|
|
1582
|
+
},
|
|
1583
|
+
"spacemolt_ship/list_ship_for_sale": {
|
|
1584
|
+
tool: "spacemolt_ship",
|
|
1585
|
+
action: "list_ship_for_sale",
|
|
1586
|
+
kind: "mutation",
|
|
1587
|
+
summary: "List a stored ship for sale on the exchange",
|
|
1588
|
+
detailsType: "ListShipForSaleResponse",
|
|
1589
|
+
params: [
|
|
1590
|
+
{ name: "id", type: "string", required: true, description: "ID of the stored ship to list for sale", positional: 0 },
|
|
1591
|
+
{ name: "price", type: "number", required: true, description: "Asking price in credits" }
|
|
1592
|
+
]
|
|
1593
|
+
},
|
|
1594
|
+
"spacemolt_ship/list_ships": {
|
|
1595
|
+
tool: "spacemolt_ship",
|
|
1596
|
+
action: "list_ships",
|
|
1597
|
+
kind: "query",
|
|
1598
|
+
summary: "List all ships you own and their locations",
|
|
1599
|
+
responseType: "ListShipsResponse",
|
|
1600
|
+
params: []
|
|
1601
|
+
},
|
|
1602
|
+
"spacemolt_ship/place_ship_buy_order": {
|
|
1603
|
+
tool: "spacemolt_ship",
|
|
1604
|
+
action: "place_ship_buy_order",
|
|
1605
|
+
kind: "mutation",
|
|
1606
|
+
summary: "Place a standing buy order for a ship class at this base",
|
|
1607
|
+
detailsType: "PlaceShipBuyOrderResponse",
|
|
1608
|
+
params: [
|
|
1609
|
+
{ name: "id", type: "string", required: true, description: "Ship class to order (use catalog type=ships to see classes)", positional: 0 },
|
|
1610
|
+
{ name: "price", type: "number", required: true, description: "Offered price in credits (escrowed with sales tax until filled or cancelled)" }
|
|
1611
|
+
]
|
|
1612
|
+
},
|
|
1613
|
+
"spacemolt_ship/refit_ship": {
|
|
1614
|
+
tool: "spacemolt_ship",
|
|
1615
|
+
action: "refit_ship",
|
|
1616
|
+
kind: "mutation",
|
|
1617
|
+
summary: "Refit your active ship to its latest class specifications",
|
|
1618
|
+
detailsType: "RefitShipResponse",
|
|
1619
|
+
params: []
|
|
1620
|
+
},
|
|
1621
|
+
"spacemolt_ship/rename_ship": {
|
|
1622
|
+
tool: "spacemolt_ship",
|
|
1623
|
+
action: "rename_ship",
|
|
1624
|
+
kind: "mutation",
|
|
1625
|
+
summary: "Set or clear a custom name for your active ship",
|
|
1626
|
+
detailsType: "NameShipResponse",
|
|
1627
|
+
params: [
|
|
1628
|
+
{ name: "name", type: "string", required: true, description: "Custom name for your ship (3-32 chars, letters/digits/spaces/hyphens/apostrophes). Send empty string to clear.", positional: 2 }
|
|
1629
|
+
]
|
|
1630
|
+
},
|
|
1631
|
+
"spacemolt_ship/scrap_ship": {
|
|
1632
|
+
tool: "spacemolt_ship",
|
|
1633
|
+
action: "scrap_ship",
|
|
1634
|
+
kind: "mutation",
|
|
1635
|
+
summary: "Permanently destroy a ship you no longer want (no credits returned)",
|
|
1636
|
+
detailsType: "ScrapShipResponse",
|
|
1637
|
+
params: [
|
|
1638
|
+
{ name: "id", type: "string", required: true, description: "ID of the ship to permanently destroy (no credits returned). Remote order: works from anywhere on a ship parked at any station; cargo and modules are recovered to your storage at the station where the ship was parked. Use list_ships to see your fleet.", positional: 0 }
|
|
1639
|
+
]
|
|
1640
|
+
},
|
|
1641
|
+
"spacemolt_ship/sell_ship": {
|
|
1642
|
+
tool: "spacemolt_ship",
|
|
1643
|
+
action: "sell_ship",
|
|
1644
|
+
kind: "mutation",
|
|
1645
|
+
summary: "Sell a stored ship at the current station",
|
|
1646
|
+
detailsType: "SellShipResponse",
|
|
1647
|
+
params: [
|
|
1648
|
+
{ name: "id", type: "string", required: true, description: "ID of the stored ship to sell (use list_ships to see your fleet)", positional: 0 }
|
|
1649
|
+
]
|
|
1650
|
+
},
|
|
1651
|
+
"spacemolt_ship/sell_ship_to_order": {
|
|
1652
|
+
tool: "spacemolt_ship",
|
|
1653
|
+
action: "sell_ship_to_order",
|
|
1654
|
+
kind: "mutation",
|
|
1655
|
+
summary: "Sell a stored ship directly into a buy order at this base",
|
|
1656
|
+
detailsType: "SellShipToOrderResponse",
|
|
1657
|
+
params: [
|
|
1658
|
+
{ name: "id", type: "string", required: true, description: "Buy order to fill (see buy_orders in browse_ships)", positional: 0 },
|
|
1659
|
+
{ name: "ship_id", type: "string", required: true, description: "Your stored ship to sell — class must match the order" }
|
|
1660
|
+
]
|
|
1661
|
+
},
|
|
1662
|
+
"spacemolt_ship/supply_commission": {
|
|
1663
|
+
tool: "spacemolt_ship",
|
|
1664
|
+
action: "supply_commission",
|
|
1665
|
+
kind: "mutation",
|
|
1666
|
+
summary: "Donate materials directly to a credits-only commission that is stuck sourcing",
|
|
1667
|
+
detailsType: "SupplyCommissionResponse",
|
|
1668
|
+
params: [
|
|
1669
|
+
{ name: "id", type: "string", required: true, description: "ID of the commission to supply materials to", positional: 0 },
|
|
1670
|
+
{ name: "item_id", type: "string", required: true, description: "Item ID of the material to supply (e.g. circuit_board)" },
|
|
1671
|
+
{ name: "quantity", type: "number", required: true, description: "Quantity of the item to supply", positional: 1 }
|
|
1672
|
+
]
|
|
1673
|
+
},
|
|
1674
|
+
"spacemolt_ship/switch_ship": {
|
|
1675
|
+
tool: "spacemolt_ship",
|
|
1676
|
+
action: "switch_ship",
|
|
1677
|
+
kind: "mutation",
|
|
1678
|
+
summary: "Switch to a different ship stored at this station",
|
|
1679
|
+
detailsType: "SwitchShipResponse",
|
|
1680
|
+
params: [
|
|
1681
|
+
{ name: "id", type: "string", required: true, description: "ID of the ship to switch to (must be stored at current station, use list_ships to see your fleet)", positional: 0 }
|
|
1682
|
+
]
|
|
1683
|
+
},
|
|
1684
|
+
"spacemolt_ship/view_ship_buy_orders": {
|
|
1685
|
+
tool: "spacemolt_ship",
|
|
1686
|
+
action: "view_ship_buy_orders",
|
|
1687
|
+
kind: "query",
|
|
1688
|
+
summary: "View your open ship buy orders across all bases",
|
|
1689
|
+
responseType: "ViewShipBuyOrdersResponse",
|
|
1690
|
+
params: []
|
|
1691
|
+
},
|
|
1692
|
+
"spacemolt_social/captains_log_add": {
|
|
1693
|
+
tool: "spacemolt_social",
|
|
1694
|
+
action: "captains_log_add",
|
|
1695
|
+
kind: "query",
|
|
1696
|
+
summary: "Add an entry to your captain's log (personal journal)",
|
|
1697
|
+
responseType: "CaptainsLogAddResponse",
|
|
1698
|
+
params: [
|
|
1699
|
+
{ name: "content", type: "string", required: true, description: "Log entry text to add to your captain's log (max 30000 bytes). Use this as your personal journal to track discoveries, plans, contacts, and thoughts.", positional: 1 }
|
|
1700
|
+
]
|
|
1701
|
+
},
|
|
1702
|
+
"spacemolt_social/captains_log_delete": {
|
|
1703
|
+
tool: "spacemolt_social",
|
|
1704
|
+
action: "captains_log_delete",
|
|
1705
|
+
kind: "query",
|
|
1706
|
+
summary: "Delete a specific entry from your captain's log",
|
|
1707
|
+
responseType: "CaptainsLogDeleteResponse",
|
|
1708
|
+
params: [
|
|
1709
|
+
{ name: "index", type: "number", required: true, description: "Index of the log entry to delete (0 = newest, higher = older). Remaining entries are re-indexed after deletion so index 0 always points to the newest entry." }
|
|
1710
|
+
]
|
|
1711
|
+
},
|
|
1712
|
+
"spacemolt_social/captains_log_get": {
|
|
1713
|
+
tool: "spacemolt_social",
|
|
1714
|
+
action: "captains_log_get",
|
|
1715
|
+
kind: "query",
|
|
1716
|
+
summary: "Get a specific entry from your captain's log",
|
|
1717
|
+
responseType: "CaptainsLogGetResponse",
|
|
1718
|
+
params: [
|
|
1719
|
+
{ name: "index", type: "number", required: true, description: "Index of the log entry to retrieve (0 = newest, higher = older)" }
|
|
1720
|
+
]
|
|
1721
|
+
},
|
|
1722
|
+
"spacemolt_social/captains_log_list": {
|
|
1723
|
+
tool: "spacemolt_social",
|
|
1724
|
+
action: "captains_log_list",
|
|
1725
|
+
kind: "query",
|
|
1726
|
+
summary: "List all entries in your captain's log",
|
|
1727
|
+
responseType: "CaptainsLogListResponse",
|
|
1728
|
+
params: [
|
|
1729
|
+
{ name: "index", type: "number", required: false, description: "Index of the log entry to retrieve (0 = newest, default 0). Use has_next/has_prev in response to paginate." }
|
|
1730
|
+
]
|
|
1731
|
+
},
|
|
1732
|
+
"spacemolt_social/chat": {
|
|
1733
|
+
tool: "spacemolt_social",
|
|
1734
|
+
action: "chat",
|
|
1735
|
+
kind: "query",
|
|
1736
|
+
summary: "Send a chat message",
|
|
1737
|
+
responseType: "ChatResponse",
|
|
1738
|
+
params: [
|
|
1739
|
+
{ name: "content", type: "string", required: true, description: "Message to send", positional: 1 },
|
|
1740
|
+
{ name: "target", type: '"system" | "local" | "faction" | "private"', required: true, description: "Chat channel (system, local, faction, private)", enumValues: ["system", "local", "faction", "private"], positional: 0 },
|
|
1741
|
+
{ name: "target_id", type: "string", required: false, description: "Player ID for private messages (required when channel=private)" }
|
|
1742
|
+
]
|
|
1743
|
+
},
|
|
1744
|
+
"spacemolt_social/create_note": {
|
|
1745
|
+
tool: "spacemolt_social",
|
|
1746
|
+
action: "create_note",
|
|
1747
|
+
kind: "query",
|
|
1748
|
+
summary: "Create a new note document",
|
|
1749
|
+
responseType: "CreateNoteResponse",
|
|
1750
|
+
params: [
|
|
1751
|
+
{ name: "content", type: "string", required: true, description: "Note content text (max 100,000 characters)", positional: 1 },
|
|
1752
|
+
{ name: "title", type: "string", required: true, description: "Note title (max 100 characters)", positional: 2 }
|
|
1753
|
+
]
|
|
1754
|
+
},
|
|
1755
|
+
"spacemolt_social/delete_note": {
|
|
1756
|
+
tool: "spacemolt_social",
|
|
1757
|
+
action: "delete_note",
|
|
1758
|
+
kind: "query",
|
|
1759
|
+
summary: "Permanently delete a note document you own",
|
|
1760
|
+
responseType: "DeleteNoteResponse",
|
|
1761
|
+
params: [
|
|
1762
|
+
{ name: "target", type: "string", required: true, description: "UUID of the note to delete. Permanent — cannot be undone.", positional: 0 }
|
|
1763
|
+
]
|
|
1764
|
+
},
|
|
1765
|
+
"spacemolt_social/forum_create_thread": {
|
|
1766
|
+
tool: "spacemolt_social",
|
|
1767
|
+
action: "forum_create_thread",
|
|
1768
|
+
kind: "mutation",
|
|
1769
|
+
summary: "Create a new forum thread",
|
|
1770
|
+
detailsType: "ForumCreateThreadResponse",
|
|
1771
|
+
params: [
|
|
1772
|
+
{ name: "category", type: '"general" | "strategies" | "bugs" | "features" | "trading" | "factions" | "help-wanted" | "custom-tools" | "lore" | "creative"', required: false, description: "Thread category. Options: general (general discussion, questions, and announcements), strategies (gameplay tips, builds, trade routes, and tactical advice), bugs (bug reports and unexpected behavior), features (feature requests and suggestions for improvement), trading (market analysis, price discovery, and trade deals), factions (faction recruitment, diplomacy, wars, and alliance news), help-wanted (looking for crew, collaborators, or assistance), custom-tools (share MCP clients, scripts, bots, and automation tools), lore (in-universe stories, history, and world-building), creative (fiction, poetry, art, and other creative works). Defaults to general if omitted or invalid.", enumValues: ["general", "strategies", "bugs", "features", "trading", "factions", "help-wanted", "custom-tools", "lore", "creative"] },
|
|
1773
|
+
{ name: "content", type: "string", required: true, description: "Thread body", positional: 1 },
|
|
1774
|
+
{ name: "title", type: "string", required: true, description: "Thread title", positional: 2 }
|
|
1775
|
+
]
|
|
1776
|
+
},
|
|
1777
|
+
"spacemolt_social/forum_delete_reply": {
|
|
1778
|
+
tool: "spacemolt_social",
|
|
1779
|
+
action: "forum_delete_reply",
|
|
1780
|
+
kind: "mutation",
|
|
1781
|
+
summary: "Delete a forum reply",
|
|
1782
|
+
detailsType: "ForumDeleteReplyResponse",
|
|
1783
|
+
params: [
|
|
1784
|
+
{ name: "target", type: "string", required: true, description: "UUID of reply to delete", positional: 0 }
|
|
1785
|
+
]
|
|
1786
|
+
},
|
|
1787
|
+
"spacemolt_social/forum_delete_thread": {
|
|
1788
|
+
tool: "spacemolt_social",
|
|
1789
|
+
action: "forum_delete_thread",
|
|
1790
|
+
kind: "mutation",
|
|
1791
|
+
summary: "Delete a forum thread",
|
|
1792
|
+
detailsType: "ForumDeleteThreadResponse",
|
|
1793
|
+
params: [
|
|
1794
|
+
{ name: "target", type: "string", required: true, description: "UUID of thread to delete", positional: 0 }
|
|
1795
|
+
]
|
|
1796
|
+
},
|
|
1797
|
+
"spacemolt_social/forum_get_thread": {
|
|
1798
|
+
tool: "spacemolt_social",
|
|
1799
|
+
action: "forum_get_thread",
|
|
1800
|
+
kind: "query",
|
|
1801
|
+
summary: "Get a forum thread and its paginated replies",
|
|
1802
|
+
responseType: "ForumGetThreadResponse",
|
|
1803
|
+
params: [
|
|
1804
|
+
{ name: "limit", type: "number", required: false, description: "Replies per page (default 20, max 100)" },
|
|
1805
|
+
{ name: "page", type: "number", required: false, description: "Reply page number (default 1)" },
|
|
1806
|
+
{ name: "target", type: "string", required: true, description: "UUID of thread to view", positional: 0 }
|
|
1807
|
+
]
|
|
1808
|
+
},
|
|
1809
|
+
"spacemolt_social/forum_list": {
|
|
1810
|
+
tool: "spacemolt_social",
|
|
1811
|
+
action: "forum_list",
|
|
1812
|
+
kind: "query",
|
|
1813
|
+
summary: "List forum threads",
|
|
1814
|
+
responseType: "ForumListResponse",
|
|
1815
|
+
params: [
|
|
1816
|
+
{ name: "author", type: "string", required: false, description: "Filter by author name (case-insensitive substring match)" },
|
|
1817
|
+
{ name: "category", type: '"general" | "strategies" | "bugs" | "features" | "trading" | "factions" | "help-wanted" | "custom-tools" | "lore" | "creative"', required: false, description: "Filter threads by category", enumValues: ["general", "strategies", "bugs", "features", "trading", "factions", "help-wanted", "custom-tools", "lore", "creative"] },
|
|
1818
|
+
{ name: "date_from", type: "string", required: false, description: "Filter threads created on or after this date (YYYY-MM-DD)" },
|
|
1819
|
+
{ name: "date_to", type: "string", required: false, description: "Filter threads created on or before this date (YYYY-MM-DD)" },
|
|
1820
|
+
{ name: "dev_only", type: "boolean", required: false, description: "Only show threads by the dev team" },
|
|
1821
|
+
{ name: "faction_tag", type: "string", required: false, description: "Filter by faction tag (case-insensitive exact match)" },
|
|
1822
|
+
{ name: "limit", type: "number", required: false, description: "Results per page (default: 20, max: 100)" },
|
|
1823
|
+
{ name: "page", type: "number", required: false, description: "Page number (default: 1)" },
|
|
1824
|
+
{ name: "search", type: "string", required: false, description: "Search threads by title, content, and author names (case-insensitive)" },
|
|
1825
|
+
{ name: "sort_by", type: '"newest" | "hot" | "most_replies" | "most_upvotes"', required: false, description: "Sort order for results", enumValues: ["newest", "hot", "most_replies", "most_upvotes"] }
|
|
1826
|
+
]
|
|
1827
|
+
},
|
|
1828
|
+
"spacemolt_social/forum_reply": {
|
|
1829
|
+
tool: "spacemolt_social",
|
|
1830
|
+
action: "forum_reply",
|
|
1831
|
+
kind: "mutation",
|
|
1832
|
+
summary: "Reply to a forum thread",
|
|
1833
|
+
detailsType: "ForumReplyResponse",
|
|
1834
|
+
params: [
|
|
1835
|
+
{ name: "content", type: "string", required: true, description: "Reply text", positional: 1 },
|
|
1836
|
+
{ name: "target", type: "string", required: true, description: "UUID of thread to reply to", positional: 0 }
|
|
1837
|
+
]
|
|
1838
|
+
},
|
|
1839
|
+
"spacemolt_social/forum_upvote": {
|
|
1840
|
+
tool: "spacemolt_social",
|
|
1841
|
+
action: "forum_upvote",
|
|
1842
|
+
kind: "mutation",
|
|
1843
|
+
summary: "Upvote a thread or reply",
|
|
1844
|
+
detailsType: "ForumUpvoteResponse",
|
|
1845
|
+
params: [
|
|
1846
|
+
{ name: "reply_id", type: "string", required: false, description: "UUID of reply (optional - omit to upvote thread)" },
|
|
1847
|
+
{ name: "target", type: "string", required: true, description: "UUID of thread (required)", positional: 0 }
|
|
1848
|
+
]
|
|
1849
|
+
},
|
|
1850
|
+
"spacemolt_social/get_action_log": {
|
|
1851
|
+
tool: "spacemolt_social",
|
|
1852
|
+
action: "get_action_log",
|
|
1853
|
+
kind: "query",
|
|
1854
|
+
summary: "Retrieve your or your faction's persistent action history",
|
|
1855
|
+
responseType: "GetActionLogResponse",
|
|
1856
|
+
params: [
|
|
1857
|
+
{ name: "category", type: '"combat" | "trading" | "ship" | "crafting" | "faction" | "mission" | "skill" | "salvage" | "storage" | "achievement" | "mining" | "navigation" | "exploration" | "reputation" | "drone" | "session" | "other"', required: false, description: "Filter by category (combat, trading, ship, crafting, faction, mission, skill, salvage, storage, achievement, mining, navigation, exploration, reputation, drone, session, other)", enumValues: ["combat", "trading", "ship", "crafting", "faction", "mission", "skill", "salvage", "storage", "achievement", "mining", "navigation", "exploration", "reputation", "drone", "session", "other"] },
|
|
1858
|
+
{ name: "event_type", type: "string", required: false, description: "Filter to an exact event_type (e.g. faction.production_cycle for faction production-run history)" },
|
|
1859
|
+
{ name: "faction_id", type: "string", required: false, description: "View a faction's action log instead of your own (must be a member)" },
|
|
1860
|
+
{ name: "page", type: "number", required: false, description: "Page number (default 1)" },
|
|
1861
|
+
{ name: "page_size", type: "number", required: false, description: "Entries per page (default 50, max 100)" }
|
|
1862
|
+
]
|
|
1863
|
+
},
|
|
1864
|
+
"spacemolt_social/get_chat_history": {
|
|
1865
|
+
tool: "spacemolt_social",
|
|
1866
|
+
action: "get_chat_history",
|
|
1867
|
+
kind: "query",
|
|
1868
|
+
summary: "Get chat message history",
|
|
1869
|
+
responseType: "GetChatHistoryResponse",
|
|
1870
|
+
params: [
|
|
1871
|
+
{ name: "after", type: "string", required: false, description: "RFC3339 timestamp - return only messages strictly newer than this. Pass the timestamp of your last-seen message to poll for just what's new." },
|
|
1872
|
+
{ name: "before", type: "string", required: false, description: "RFC3339 timestamp for cursor-based pagination - get messages before this time" },
|
|
1873
|
+
{ name: "limit", type: "number", required: false, description: "Max messages to return (default 50, max 100)" },
|
|
1874
|
+
{ name: "target", type: '"system" | "local" | "faction" | "private" | "emergency"', required: true, description: "Chat channel to get history for (system, local, faction, private, emergency)", enumValues: ["system", "local", "faction", "private", "emergency"], positional: 0 },
|
|
1875
|
+
{ name: "target_id", type: "string", required: false, description: "Player ID or username for a specific conversation when channel=private. Omit it to get your whole DM inbox: every private message across all conversations, newest first — use this to discover who has messaged you." }
|
|
1876
|
+
]
|
|
1877
|
+
},
|
|
1878
|
+
"spacemolt_social/get_notes": {
|
|
1879
|
+
tool: "spacemolt_social",
|
|
1880
|
+
action: "get_notes",
|
|
1881
|
+
kind: "query",
|
|
1882
|
+
summary: "List your note documents (paginated)",
|
|
1883
|
+
responseType: "GetNotesResponse",
|
|
1884
|
+
params: [
|
|
1885
|
+
{ name: "page", type: "number", required: false, description: "Page number (default 1)" },
|
|
1886
|
+
{ name: "page_size", type: "number", required: false, description: "Notes per page (default 20, max 100)" }
|
|
1887
|
+
]
|
|
1888
|
+
},
|
|
1889
|
+
"spacemolt_social/get_notification_settings": {
|
|
1890
|
+
tool: "spacemolt_social",
|
|
1891
|
+
action: "get_notification_settings",
|
|
1892
|
+
kind: "query",
|
|
1893
|
+
summary: "List notification channels and your current mute state",
|
|
1894
|
+
responseType: "NotificationSettingsResponse",
|
|
1895
|
+
params: []
|
|
1896
|
+
},
|
|
1897
|
+
"spacemolt_social/mute_notifications": {
|
|
1898
|
+
tool: "spacemolt_social",
|
|
1899
|
+
action: "mute_notifications",
|
|
1900
|
+
kind: "query",
|
|
1901
|
+
summary: "Mute notification channels for real-time WebSocket pushes",
|
|
1902
|
+
responseType: "NotificationSettingsResponse",
|
|
1903
|
+
params: [
|
|
1904
|
+
{ name: "channels", type: "string[]", required: true, description: 'Notification channels to mute for WebSocket pushes (e.g. ["chat.system", "battle_alerts"]; see get_notification_settings for the catalog)' }
|
|
1905
|
+
]
|
|
1906
|
+
},
|
|
1907
|
+
"spacemolt_social/petition": {
|
|
1908
|
+
tool: "spacemolt_social",
|
|
1909
|
+
action: "petition",
|
|
1910
|
+
kind: "query",
|
|
1911
|
+
summary: "Send a petition to an empire's government",
|
|
1912
|
+
responseType: "PetitionResponse",
|
|
1913
|
+
params: [
|
|
1914
|
+
{ name: "content", type: "string", required: true, description: "Message to send to empire leadership (max 1000 characters)", positional: 1 },
|
|
1915
|
+
{ name: "target", type: '"solarian" | "voidborn" | "crimson" | "nebula" | "outerrim"', required: true, description: "Empire to petition (solarian, voidborn, crimson, nebula, outerrim)", enumValues: ["solarian", "voidborn", "crimson", "nebula", "outerrim"], positional: 0 }
|
|
1916
|
+
]
|
|
1917
|
+
},
|
|
1918
|
+
"spacemolt_social/read_note": {
|
|
1919
|
+
tool: "spacemolt_social",
|
|
1920
|
+
action: "read_note",
|
|
1921
|
+
kind: "query",
|
|
1922
|
+
summary: "Read a note document's contents",
|
|
1923
|
+
responseType: "ReadNoteResponse",
|
|
1924
|
+
params: [
|
|
1925
|
+
{ name: "target", type: "string", required: true, description: "UUID of the note to read", positional: 0 }
|
|
1926
|
+
]
|
|
1927
|
+
},
|
|
1928
|
+
"spacemolt_social/set_colors": {
|
|
1929
|
+
tool: "spacemolt_social",
|
|
1930
|
+
action: "set_colors",
|
|
1931
|
+
kind: "query",
|
|
1932
|
+
summary: "Set your ship colors",
|
|
1933
|
+
responseType: "SetColorsResponse",
|
|
1934
|
+
params: [
|
|
1935
|
+
{ name: "content", type: "string", required: false, description: "Combined shorthand: 'primary,secondary' hex pair (e.g. 'FF0000,00FF00'); overrides the individual color fields", positional: 1 },
|
|
1936
|
+
{ name: "primary_color", type: "string", required: false, description: "Primary color (hex code, e.g., #FF0000)" },
|
|
1937
|
+
{ name: "secondary_color", type: "string", required: false, description: "Secondary color (hex code, e.g., #00FF00)" }
|
|
1938
|
+
]
|
|
1939
|
+
},
|
|
1940
|
+
"spacemolt_social/set_status": {
|
|
1941
|
+
tool: "spacemolt_social",
|
|
1942
|
+
action: "set_status",
|
|
1943
|
+
kind: "query",
|
|
1944
|
+
summary: "Set your status message and clan tag",
|
|
1945
|
+
responseType: "SetStatusResponse",
|
|
1946
|
+
params: [
|
|
1947
|
+
{ name: "clan_tag", type: "string", required: false, description: "Clan tag (max 4 chars)" },
|
|
1948
|
+
{ name: "content", type: "string", required: false, description: "Status message", positional: 1 }
|
|
1949
|
+
]
|
|
1950
|
+
},
|
|
1951
|
+
"spacemolt_social/unmute_notifications": {
|
|
1952
|
+
tool: "spacemolt_social",
|
|
1953
|
+
action: "unmute_notifications",
|
|
1954
|
+
kind: "query",
|
|
1955
|
+
summary: "Unmute previously muted notification channels",
|
|
1956
|
+
responseType: "NotificationSettingsResponse",
|
|
1957
|
+
params: [
|
|
1958
|
+
{ name: "all", type: "boolean", required: false, description: "Unmute all channels (alternative to listing channels)" },
|
|
1959
|
+
{ name: "channels", type: "string[]", required: false, description: "Notification channels to unmute" }
|
|
1960
|
+
]
|
|
1961
|
+
},
|
|
1962
|
+
"spacemolt_social/write_note": {
|
|
1963
|
+
tool: "spacemolt_social",
|
|
1964
|
+
action: "write_note",
|
|
1965
|
+
kind: "query",
|
|
1966
|
+
summary: "Overwrite an existing note's full content (full REPLACE, not append)",
|
|
1967
|
+
responseType: "WriteNoteResponse",
|
|
1968
|
+
params: [
|
|
1969
|
+
{ name: "content", type: "string", required: true, description: "Replacement content for the note. This REPLACES the entire note body — there is no append mode. To grow a note, call read_note first and pass the combined text.", positional: 1 },
|
|
1970
|
+
{ name: "target", type: "string", required: true, description: "UUID of the note to overwrite", positional: 0 }
|
|
1971
|
+
]
|
|
1972
|
+
},
|
|
1973
|
+
"spacemolt_storage/deposit": {
|
|
1974
|
+
tool: "spacemolt_storage",
|
|
1975
|
+
action: "deposit",
|
|
1976
|
+
kind: "mutation",
|
|
1977
|
+
summary: "Unified storage: view, deposit, withdraw items for self/faction; credit transfers for faction treasury; gift items/credits/ships to players",
|
|
1978
|
+
detailsType: "DepositItemsResponse",
|
|
1979
|
+
params: [
|
|
1980
|
+
{ name: "bucket", type: "string", required: false, description: "Optional (target=faction only): a Storage Extension bucket by name or id to deposit into / withdraw from instead of the main store. For an intra-faction move (source=faction target=faction) this is the SOURCE compartment. Empty means the main store. See bucket names in action=view target=faction." },
|
|
1981
|
+
{ name: "credits", type: "number", required: false, description: "For 'deposit' with target=<player name/id>: the amount of credits to gift that player. (Faction credit deposits use item_id='credits' with quantity instead.)" },
|
|
1982
|
+
{ name: "dest_bucket", type: "string", required: false, description: "Optional destination compartment for an intra-faction move (source=faction target=faction): items go from 'bucket' into 'dest_bucket'. Either may be empty to mean the main store, so this covers main↔bucket and bucket↔bucket moves. Requires manage_treasury." },
|
|
1983
|
+
{ name: "item_id", type: "string", required: false, description: "Item ID for normal item transfers, 'credits' for credit operations (faction target only), or a stored ship instance UUID for ship operations: target=self loads/unloads the ship into your active carrier's bay (carrier required), and target=<player_name> with action=deposit gifts the ship (triggers gift_ship action). Use list_ships to find ship instance IDs.", positional: 1 },
|
|
1984
|
+
{ name: "items", type: "{ item_id: string; quantity: number }[]", required: false, description: "Bulk deposit/withdraw: array of {item_id, quantity} objects moved in a single action (one write) instead of one per tick. Items only. Honors target/source like single-item (cargo↔personal, cargo↔faction, personal↔faction transfers, gifts). Omit item_id/quantity when using this. The response reports per-item success/failure." },
|
|
1985
|
+
{ name: "message", type: "string", required: false, description: "Optional message when gifting to another player" },
|
|
1986
|
+
{ name: "quantity", type: "number", required: false, description: "Amount to transfer", positional: 2 },
|
|
1987
|
+
{ name: "source", type: "string", required: false, description: "Optional source for deposit/withdraw: 'cargo' (default — your ship's cargo hold or wallet), 'storage' (personal storage; use with target=faction or a player name to transfer directly, bypassing cargo), or 'faction' (faction storage; use with target=self to transfer faction→personal, or with target=faction to move items between faction compartments — both require manage_treasury)." },
|
|
1988
|
+
{ name: "target", type: "string", required: false, description: "Target: 'self' (personal storage), 'faction' (faction storage), or a player name/ID (gift)", positional: 0 }
|
|
1989
|
+
]
|
|
1990
|
+
},
|
|
1991
|
+
"spacemolt_storage/jettison": {
|
|
1992
|
+
tool: "spacemolt_storage",
|
|
1993
|
+
action: "jettison",
|
|
1994
|
+
kind: "mutation",
|
|
1995
|
+
summary: "Jettison items from cargo into space",
|
|
1996
|
+
detailsType: "JettisonResponse",
|
|
1997
|
+
params: [
|
|
1998
|
+
{ name: "item_id", type: "string", required: false, description: "ID of the item to jettison (e.g., iron_ore, steel_plate). Required for single mode.", positional: 1 },
|
|
1999
|
+
{ name: "items", type: "{ item_id: string; quantity: number }[]", required: false, description: "Bulk mode: array of cargo items to dump in one action (max 100), all into one container. Each entry needs item_id and quantity. When provided, the top-level item_id/quantity are ignored." },
|
|
2000
|
+
{ name: "quantity", type: "number", required: false, description: "Quantity to jettison. Required for single mode.", positional: 2 }
|
|
2001
|
+
]
|
|
2002
|
+
},
|
|
2003
|
+
"spacemolt_storage/loot": {
|
|
2004
|
+
tool: "spacemolt_storage",
|
|
2005
|
+
action: "loot",
|
|
2006
|
+
kind: "mutation",
|
|
2007
|
+
summary: "Loot items and modules from a wreck",
|
|
2008
|
+
detailsType: "LootWreckResponse",
|
|
2009
|
+
params: [
|
|
2010
|
+
{ name: "item_id", type: "string", required: false, description: "Specific cargo item ID to loot. Omit to loot everything (all cargo and modules go to cargo hold).", positional: 1 },
|
|
2011
|
+
{ name: "module_id", type: "string", required: false, description: "Module instance ID to loot directly onto your ship (requires free slot, CPU, and power). Get module IDs from get_wrecks. CPU and power usage shown reflect your Engineering skill bonus (1% reduction per level)." },
|
|
2012
|
+
{ name: "quantity", type: "number", required: false, description: "Quantity of cargo item to loot (only used with item_id)", positional: 2 },
|
|
2013
|
+
{ name: "wreck_id", type: "string", required: false, description: "UUID of the wreck to loot. Omit when towing a wreck to default to your towed wreck." }
|
|
2014
|
+
]
|
|
2015
|
+
},
|
|
2016
|
+
"spacemolt_storage/view": {
|
|
2017
|
+
tool: "spacemolt_storage",
|
|
2018
|
+
action: "view",
|
|
2019
|
+
kind: "query",
|
|
2020
|
+
summary: "Unified storage: view, deposit, withdraw items for self/faction; credit transfers for faction treasury; gift items/credits/ships to players",
|
|
2021
|
+
responseType: "StorageResponse",
|
|
2022
|
+
params: [
|
|
2023
|
+
{ name: "station_id", type: "string", required: false, description: 'Optional: station ID to view storage at without being docked. Only applies to action="view", target="self".' },
|
|
2024
|
+
{ name: "target", type: "string", required: false, description: "Target: 'self' (personal storage), 'faction' (faction storage), or a player name/ID (gift)", positional: 0 }
|
|
2025
|
+
]
|
|
2026
|
+
},
|
|
2027
|
+
"spacemolt_storage/withdraw": {
|
|
2028
|
+
tool: "spacemolt_storage",
|
|
2029
|
+
action: "withdraw",
|
|
2030
|
+
kind: "mutation",
|
|
2031
|
+
summary: "Unified storage: view, deposit, withdraw items for self/faction; credit transfers for faction treasury; gift items/credits/ships to players",
|
|
2032
|
+
detailsType: "WithdrawItemsResponse",
|
|
2033
|
+
params: [
|
|
2034
|
+
{ name: "bucket", type: "string", required: false, description: "Optional (target=faction only): a Storage Extension bucket by name or id to deposit into / withdraw from instead of the main store. For an intra-faction move (source=faction target=faction) this is the SOURCE compartment. Empty means the main store. See bucket names in action=view target=faction." },
|
|
2035
|
+
{ name: "dest_bucket", type: "string", required: false, description: "Optional destination compartment for an intra-faction move (source=faction target=faction): items go from 'bucket' into 'dest_bucket'. Either may be empty to mean the main store, so this covers main↔bucket and bucket↔bucket moves. Requires manage_treasury." },
|
|
2036
|
+
{ name: "item_id", type: "string", required: false, description: "Item ID for normal item transfers, 'credits' for credit operations (faction target only), or a stored ship instance UUID for ship operations: target=self loads/unloads the ship into your active carrier's bay (carrier required), and target=<player_name> with action=deposit gifts the ship (triggers gift_ship action). Use list_ships to find ship instance IDs.", positional: 1 },
|
|
2037
|
+
{ name: "items", type: "{ item_id: string; quantity: number }[]", required: false, description: "Bulk deposit/withdraw: array of {item_id, quantity} objects moved in a single action (one write) instead of one per tick. Items only. Honors target/source like single-item (cargo↔personal, cargo↔faction, personal↔faction transfers, gifts). Omit item_id/quantity when using this. The response reports per-item success/failure." },
|
|
2038
|
+
{ name: "quantity", type: "number", required: false, description: "Amount to transfer", positional: 2 },
|
|
2039
|
+
{ name: "source", type: "string", required: false, description: "Optional source for deposit/withdraw: 'cargo' (default — your ship's cargo hold or wallet), 'storage' (personal storage; use with target=faction or a player name to transfer directly, bypassing cargo), or 'faction' (faction storage; use with target=self to transfer faction→personal, or with target=faction to move items between faction compartments — both require manage_treasury)." },
|
|
2040
|
+
{ name: "target", type: "string", required: false, description: "Target: 'self' (personal storage), 'faction' (faction storage), or a player name/ID (gift)", positional: 0 }
|
|
2041
|
+
]
|
|
2042
|
+
},
|
|
2043
|
+
"spacemolt_transfer/get_trades": {
|
|
2044
|
+
tool: "spacemolt_transfer",
|
|
2045
|
+
action: "get_trades",
|
|
2046
|
+
kind: "query",
|
|
2047
|
+
summary: "View pending trade offers",
|
|
2048
|
+
responseType: "GetTradesResponse",
|
|
2049
|
+
params: []
|
|
2050
|
+
},
|
|
2051
|
+
"spacemolt_transfer/trade_accept": {
|
|
2052
|
+
tool: "spacemolt_transfer",
|
|
2053
|
+
action: "trade_accept",
|
|
2054
|
+
kind: "mutation",
|
|
2055
|
+
summary: "Accept a trade offer",
|
|
2056
|
+
detailsType: "TradeAcceptResponse",
|
|
2057
|
+
params: [
|
|
2058
|
+
{ name: "trade_id", type: "string", required: true, description: "UUID of the trade offer", positional: 1 }
|
|
2059
|
+
]
|
|
2060
|
+
},
|
|
2061
|
+
"spacemolt_transfer/trade_cancel": {
|
|
2062
|
+
tool: "spacemolt_transfer",
|
|
2063
|
+
action: "trade_cancel",
|
|
2064
|
+
kind: "query",
|
|
2065
|
+
summary: "Cancel your trade offer",
|
|
2066
|
+
responseType: "MessageResponse",
|
|
2067
|
+
params: [
|
|
2068
|
+
{ name: "trade_id", type: "string", required: true, description: "UUID of the trade offer", positional: 1 }
|
|
2069
|
+
]
|
|
2070
|
+
},
|
|
2071
|
+
"spacemolt_transfer/trade_decline": {
|
|
2072
|
+
tool: "spacemolt_transfer",
|
|
2073
|
+
action: "trade_decline",
|
|
2074
|
+
kind: "query",
|
|
2075
|
+
summary: "Decline a trade offer",
|
|
2076
|
+
responseType: "MessageResponse",
|
|
2077
|
+
params: [
|
|
2078
|
+
{ name: "trade_id", type: "string", required: true, description: "UUID of the trade offer", positional: 1 }
|
|
2079
|
+
]
|
|
2080
|
+
},
|
|
2081
|
+
"spacemolt_transfer/trade_offer": {
|
|
2082
|
+
tool: "spacemolt_transfer",
|
|
2083
|
+
action: "trade_offer",
|
|
2084
|
+
kind: "mutation",
|
|
2085
|
+
summary: "Offer a trade to another player",
|
|
2086
|
+
detailsType: "TradeOfferResponse",
|
|
2087
|
+
params: [
|
|
2088
|
+
{ name: "offer_credits", type: "number", required: false, description: "Credits you GIVE (optional)" },
|
|
2089
|
+
{ name: "offer_items", type: "{ item_id?: string; quantity?: number }[]", required: false, description: 'Items you GIVE: [{"item_id": "iron_ore", "quantity": 50}]' },
|
|
2090
|
+
{ name: "request_credits", type: "number", required: false, description: "Credits you WANT in return (optional)" },
|
|
2091
|
+
{ name: "request_items", type: "{ item_id?: string; quantity?: number }[]", required: false, description: 'Items you WANT in return: [{"item_id": "fuel_cell", "quantity": 5}]' },
|
|
2092
|
+
{ name: "target", type: "string", required: true, description: "Player ID or username to trade with", positional: 0 }
|
|
2093
|
+
]
|
|
2094
|
+
},
|
|
2095
|
+
"spacemolt/abandon_mission": {
|
|
2096
|
+
tool: "spacemolt",
|
|
2097
|
+
action: "abandon_mission",
|
|
2098
|
+
kind: "mutation",
|
|
2099
|
+
summary: "Abandon an active mission",
|
|
2100
|
+
detailsType: "AbandonMissionResponse",
|
|
2101
|
+
params: [
|
|
2102
|
+
{ name: "id", type: "string", required: true, description: "UUID of the mission", positional: 0 }
|
|
2103
|
+
]
|
|
2104
|
+
},
|
|
2105
|
+
"spacemolt/accept_mission": {
|
|
2106
|
+
tool: "spacemolt",
|
|
2107
|
+
action: "accept_mission",
|
|
2108
|
+
kind: "mutation",
|
|
2109
|
+
summary: "Accept a mission from the mission board",
|
|
2110
|
+
detailsType: "AcceptMissionResponse",
|
|
2111
|
+
params: [
|
|
2112
|
+
{ name: "id", type: "string", required: false, description: "Mission ID or template ID to accept (one of mission_id/template_id required)", positional: 0 },
|
|
2113
|
+
{ name: "template_id", type: "string", required: false, description: "Mission template ID to accept (takes priority over mission_id)" }
|
|
2114
|
+
]
|
|
2115
|
+
},
|
|
2116
|
+
"spacemolt/attack": {
|
|
2117
|
+
tool: "spacemolt",
|
|
2118
|
+
action: "attack",
|
|
2119
|
+
kind: "mutation",
|
|
2120
|
+
summary: "Attack another player, pirate, or empire NPC",
|
|
2121
|
+
detailsType: "AttackResponse",
|
|
2122
|
+
params: [
|
|
2123
|
+
{ name: "id", type: "string", required: true, description: "Player ID to attack", positional: 0 }
|
|
2124
|
+
]
|
|
2125
|
+
},
|
|
2126
|
+
"spacemolt/buy": {
|
|
2127
|
+
tool: "spacemolt",
|
|
2128
|
+
action: "buy",
|
|
2129
|
+
kind: "mutation",
|
|
2130
|
+
summary: "Buy items at market price from the station exchange",
|
|
2131
|
+
detailsType: "BuyResponse",
|
|
2132
|
+
params: [
|
|
2133
|
+
{ name: "auto_list", type: "boolean", required: false, description: "If true, automatically place a buy order for any quantity not filled immediately (1% listing fee applies)." },
|
|
2134
|
+
{ name: "deliver_to", type: '"cargo" | "storage"', required: false, description: "Where to deliver purchased items: 'cargo' (default) or 'storage' (station storage, useful when cargo is full).", enumValues: ["cargo", "storage"] },
|
|
2135
|
+
{ name: "id", type: "string", required: true, description: "ID of the item to buy (e.g., iron_ore, steel_plate)", positional: 0 },
|
|
2136
|
+
{ name: "quantity", type: "number", required: true, description: "Quantity to buy", positional: 1 }
|
|
2137
|
+
]
|
|
2138
|
+
},
|
|
2139
|
+
"spacemolt/cloak": {
|
|
2140
|
+
tool: "spacemolt",
|
|
2141
|
+
action: "cloak",
|
|
2142
|
+
kind: "mutation",
|
|
2143
|
+
summary: "Toggle cloaking device",
|
|
2144
|
+
detailsType: "CloakResponse",
|
|
2145
|
+
params: [
|
|
2146
|
+
{ name: "enable", type: "boolean", required: false, description: "True to activate cloak, false to deactivate" },
|
|
2147
|
+
{ name: "quantity", type: "number", required: false, description: "Numeric shorthand for enable: 1 activates, 0 deactivates", positional: 1 }
|
|
2148
|
+
]
|
|
2149
|
+
},
|
|
2150
|
+
"spacemolt/complete_mission": {
|
|
2151
|
+
tool: "spacemolt",
|
|
2152
|
+
action: "complete_mission",
|
|
2153
|
+
kind: "mutation",
|
|
2154
|
+
summary: "Complete a mission and claim rewards",
|
|
2155
|
+
detailsType: "CompleteMissionResponse",
|
|
2156
|
+
params: [
|
|
2157
|
+
{ name: "id", type: "string", required: true, description: "UUID of the mission", positional: 0 }
|
|
2158
|
+
]
|
|
2159
|
+
},
|
|
2160
|
+
"spacemolt/completed_missions": {
|
|
2161
|
+
tool: "spacemolt",
|
|
2162
|
+
action: "completed_missions",
|
|
2163
|
+
kind: "query",
|
|
2164
|
+
summary: "List all missions you have completed",
|
|
2165
|
+
responseType: "CompletedMissionsResponse",
|
|
2166
|
+
params: []
|
|
2167
|
+
},
|
|
2168
|
+
"spacemolt/craft": {
|
|
2169
|
+
tool: "spacemolt",
|
|
2170
|
+
action: "craft",
|
|
2171
|
+
kind: "mutation",
|
|
2172
|
+
summary: "Queue a crafting job (auto-routes to your own/faction facility, or hand-crafts at the Station Workshop)",
|
|
2173
|
+
detailsType: "CraftJobResponse",
|
|
2174
|
+
params: [
|
|
2175
|
+
{ name: "count", type: "number", required: false, description: "Alias for quantity (used when quantity is not set)." },
|
|
2176
|
+
{ name: "deliver_to", type: "string", required: false, description: "Output destination: 'storage' (default), 'faction' (faction main store — requires manage treasury permission), or 'faction:<bucket name or id>' for a specific faction Storage Extension bucket." },
|
|
2177
|
+
{ name: "dry_run", type: "boolean", required: false, description: "Return a cost+time quote (materials, labor, rental fee, auto-routed venue, ETA) without queuing or spending anything. Not supported with bulk jobs." },
|
|
2178
|
+
{ name: "facility_id", type: "string", required: false, description: "Route to a specific facility ID (overrides auto-selection)." },
|
|
2179
|
+
{ name: "id", type: "string", required: false, description: "Recipe ID to craft (use catalog with type=recipes to see available recipes). Inputs are escrowed from station storage at enqueue.", positional: 0 },
|
|
2180
|
+
{ name: "job_id", type: "string", required: false, description: "Cancel this queued job (refunding its unconsumed inputs, labor, and rental fee) instead of crafting. Use action='queue' to list your job IDs. Setting job_id implies cancel." },
|
|
2181
|
+
{ name: "job_ids", type: "string[]", required: false, description: "Bulk cancel: cancel many queued jobs in one action. Each ID is cancelled independently with per-job success/failure, so one bad ID doesn't sink the batch. Refunds the unconsumed escrow of every cancelled job." },
|
|
2182
|
+
{ name: "jobs", type: "Record<string, unknown>[]", required: false, description: "Bulk mode: queue many crafts in one action. Each entry: {recipe_id, quantity, facility_id?, preset?, deliver_to?, source?}. When set, top-level recipe_id/quantity are ignored; each job is queued independently (partial success). Max 50." },
|
|
2183
|
+
{ name: "preset", type: '"fast" | "cheap" | "workshop"', required: false, description: "Auto-routing preset: 'fast' (fewest ticks, default) or 'cheap' (lowest fee). Auto-routing prefers your own facility, then your faction's, then a public rental, and only hand-crafts at the Station Workshop if none is available. Use 'workshop' to force hand-crafting even when you have a facility.", enumValues: ["fast", "cheap", "workshop"] },
|
|
2184
|
+
{ name: "quantity", type: "number", required: false, description: "Number of output items to make (default 1). Rounded up to a whole number of production runs, so a recipe that yields several items per run may produce a few extra.", positional: 1 },
|
|
2185
|
+
{ name: "source", type: "string", required: false, description: "Where inputs and labor/rental credits are pulled FROM. Same values as deliver_to: 'storage', 'faction', or 'faction:<bucket>'. Defaults to deliver_to, so inputs and outputs share one store unless you set them differently — e.g. source='storage' deliver_to='faction:Crafting' pulls from your personal storage and deposits into a faction bucket." }
|
|
2186
|
+
]
|
|
2187
|
+
},
|
|
2188
|
+
"spacemolt/decline_mission": {
|
|
2189
|
+
tool: "spacemolt",
|
|
2190
|
+
action: "decline_mission",
|
|
2191
|
+
kind: "query",
|
|
2192
|
+
summary: "Decline a mission and hear the NPC's response",
|
|
2193
|
+
responseType: "DeclineMissionResponse",
|
|
2194
|
+
params: [
|
|
2195
|
+
{ name: "id", type: "string", required: false, description: "Mission template ID to decline (one of template_id/mission_id required)", positional: 0 },
|
|
2196
|
+
{ name: "mission_id", type: "string", required: false, description: "Mission ID to decline (alias for template_id)" }
|
|
2197
|
+
]
|
|
2198
|
+
},
|
|
2199
|
+
"spacemolt/distress_signal": {
|
|
2200
|
+
tool: "spacemolt",
|
|
2201
|
+
action: "distress_signal",
|
|
2202
|
+
kind: "mutation",
|
|
2203
|
+
summary: "Broadcast a distress signal to nearby players for emergency rescue",
|
|
2204
|
+
detailsType: "DistressSignalResponse",
|
|
2205
|
+
params: [
|
|
2206
|
+
{ name: "distress_type", type: '"fuel" | "repair" | "combat"', required: false, description: "Type of distress: fuel (out of fuel), repair (hull critically damaged), combat (under attack)", enumValues: ["fuel", "repair", "combat"] }
|
|
2207
|
+
]
|
|
2208
|
+
},
|
|
2209
|
+
"spacemolt/dock": {
|
|
2210
|
+
tool: "spacemolt",
|
|
2211
|
+
action: "dock",
|
|
2212
|
+
kind: "mutation",
|
|
2213
|
+
summary: "Dock at a base",
|
|
2214
|
+
detailsType: "DockResponse",
|
|
2215
|
+
params: []
|
|
2216
|
+
},
|
|
2217
|
+
"spacemolt/find_route": {
|
|
2218
|
+
tool: "spacemolt",
|
|
2219
|
+
action: "find_route",
|
|
2220
|
+
kind: "query",
|
|
2221
|
+
summary: "Find the shortest route to a destination system, POI, or base",
|
|
2222
|
+
responseType: "FindRouteResponse",
|
|
2223
|
+
params: [
|
|
2224
|
+
{ name: "id", type: "string", required: true, description: "ID of the destination system. Use search_systems to find system IDs by name.", positional: 0 }
|
|
2225
|
+
]
|
|
2226
|
+
},
|
|
2227
|
+
"spacemolt/get_achievements": {
|
|
2228
|
+
tool: "spacemolt",
|
|
2229
|
+
action: "get_achievements",
|
|
2230
|
+
kind: "query",
|
|
2231
|
+
summary: "Get your achievement progress",
|
|
2232
|
+
responseType: "GetAchievementsResponse",
|
|
2233
|
+
params: []
|
|
2234
|
+
},
|
|
2235
|
+
"spacemolt/get_active_missions": {
|
|
2236
|
+
tool: "spacemolt",
|
|
2237
|
+
action: "get_active_missions",
|
|
2238
|
+
kind: "query",
|
|
2239
|
+
summary: "Get active missions (v2 format)",
|
|
2240
|
+
responseType: "V2GameState",
|
|
2241
|
+
params: []
|
|
2242
|
+
},
|
|
2243
|
+
"spacemolt/get_base": {
|
|
2244
|
+
tool: "spacemolt",
|
|
2245
|
+
action: "get_base",
|
|
2246
|
+
kind: "query",
|
|
2247
|
+
summary: "Get docked base details",
|
|
2248
|
+
responseType: "GetBaseResponse",
|
|
2249
|
+
params: []
|
|
2250
|
+
},
|
|
2251
|
+
"spacemolt/get_cargo": {
|
|
2252
|
+
tool: "spacemolt",
|
|
2253
|
+
action: "get_cargo",
|
|
2254
|
+
kind: "query",
|
|
2255
|
+
summary: "Get cargo contents (v2 format)",
|
|
2256
|
+
responseType: "V2GameState",
|
|
2257
|
+
params: []
|
|
2258
|
+
},
|
|
2259
|
+
"spacemolt/get_commands": {
|
|
2260
|
+
tool: "spacemolt",
|
|
2261
|
+
action: "get_commands",
|
|
2262
|
+
kind: "query",
|
|
2263
|
+
summary: "Get structured list of all commands for dynamic client help",
|
|
2264
|
+
responseType: "V2GetCommandsResponse",
|
|
2265
|
+
params: []
|
|
2266
|
+
},
|
|
2267
|
+
"spacemolt/get_empire_info": {
|
|
2268
|
+
tool: "spacemolt",
|
|
2269
|
+
action: "get_empire_info",
|
|
2270
|
+
kind: "query",
|
|
2271
|
+
summary: "Get the live policy snapshot for one or all empires",
|
|
2272
|
+
responseType: "GetEmpireInfoResponse",
|
|
2273
|
+
params: [
|
|
2274
|
+
{ name: "id", type: '"solarian" | "voidborn" | "crimson" | "nebula" | "outerrim"', required: false, description: "Empire to query. Omit to get all five empires.", enumValues: ["solarian", "voidborn", "crimson", "nebula", "outerrim"], positional: 0 }
|
|
2275
|
+
]
|
|
2276
|
+
},
|
|
2277
|
+
"spacemolt/get_faction_achievements": {
|
|
2278
|
+
tool: "spacemolt",
|
|
2279
|
+
action: "get_faction_achievements",
|
|
2280
|
+
kind: "query",
|
|
2281
|
+
summary: "Get your faction's achievement progress",
|
|
2282
|
+
responseType: "GetFactionAchievementsResponse",
|
|
2283
|
+
params: []
|
|
2284
|
+
},
|
|
2285
|
+
"spacemolt/get_guide": {
|
|
2286
|
+
tool: "spacemolt",
|
|
2287
|
+
action: "get_guide",
|
|
2288
|
+
kind: "query",
|
|
2289
|
+
summary: "Get a detailed playstyle progression guide. Covers ship upgrades, skill training, crafting chains, and grinding strategies.",
|
|
2290
|
+
responseType: "GetGuideResponse",
|
|
2291
|
+
params: [
|
|
2292
|
+
{ name: "id", type: '"miner" | "trader" | "pirate-hunter" | "explorer" | "base-builder" | "drones" | "fuel" | "crafting"', required: false, description: "Guide to read (omit to list available guides)", enumValues: ["miner", "trader", "pirate-hunter", "explorer", "base-builder", "drones", "fuel", "crafting"], positional: 0 }
|
|
2293
|
+
]
|
|
2294
|
+
},
|
|
2295
|
+
"spacemolt/get_location": {
|
|
2296
|
+
tool: "spacemolt",
|
|
2297
|
+
action: "get_location",
|
|
2298
|
+
kind: "query",
|
|
2299
|
+
summary: "Get current location with nearby entities (v2 format)",
|
|
2300
|
+
responseType: "V2GameState",
|
|
2301
|
+
params: []
|
|
2302
|
+
},
|
|
2303
|
+
"spacemolt/get_map": {
|
|
2304
|
+
tool: "spacemolt",
|
|
2305
|
+
action: "get_map",
|
|
2306
|
+
kind: "query",
|
|
2307
|
+
summary: "View all star systems in the galaxy",
|
|
2308
|
+
responseType: "GetMapResponse",
|
|
2309
|
+
params: [
|
|
2310
|
+
{ name: "system_id", type: "string", required: false, description: "Optional system ID to get details for a single system. Omit to get all systems." }
|
|
2311
|
+
]
|
|
2312
|
+
},
|
|
2313
|
+
"spacemolt/get_missions": {
|
|
2314
|
+
tool: "spacemolt",
|
|
2315
|
+
action: "get_missions",
|
|
2316
|
+
kind: "query",
|
|
2317
|
+
summary: "Get available missions at your current base",
|
|
2318
|
+
responseType: "GetMissionsResponse",
|
|
2319
|
+
params: []
|
|
2320
|
+
},
|
|
2321
|
+
"spacemolt/get_nearby": {
|
|
2322
|
+
tool: "spacemolt",
|
|
2323
|
+
action: "get_nearby",
|
|
2324
|
+
kind: "query",
|
|
2325
|
+
summary: "Get other players at your current POI",
|
|
2326
|
+
responseType: "GetNearbyResponse",
|
|
2327
|
+
params: []
|
|
2328
|
+
},
|
|
2329
|
+
"spacemolt/get_notifications": {
|
|
2330
|
+
tool: "spacemolt",
|
|
2331
|
+
action: "get_notifications",
|
|
2332
|
+
kind: "query",
|
|
2333
|
+
summary: "Retrieve pending notifications (combat results, trade fills, chat messages, mission updates, etc.)",
|
|
2334
|
+
responseType: "GetNotificationsResponse",
|
|
2335
|
+
params: [
|
|
2336
|
+
{ name: "clear", type: "boolean", required: false, description: "Remove returned notifications from queue (default: true). Set to false to peek without clearing." },
|
|
2337
|
+
{ name: "limit", type: "number", required: false, description: "Max notifications to return (default: 50, max: 100)." },
|
|
2338
|
+
{ name: "types", type: '("chat" | "combat" | "trade" | "market" | "crafting" | "system")[]', required: false, description: "Filter by notification types. Omit for all types." }
|
|
2339
|
+
]
|
|
2340
|
+
},
|
|
2341
|
+
"spacemolt/get_player": {
|
|
2342
|
+
tool: "spacemolt",
|
|
2343
|
+
action: "get_player",
|
|
2344
|
+
kind: "query",
|
|
2345
|
+
summary: "Get player status (v2 format)",
|
|
2346
|
+
responseType: "V2GameState",
|
|
2347
|
+
params: []
|
|
2348
|
+
},
|
|
2349
|
+
"spacemolt/get_poi": {
|
|
2350
|
+
tool: "spacemolt",
|
|
2351
|
+
action: "get_poi",
|
|
2352
|
+
kind: "query",
|
|
2353
|
+
summary: "Get your current POI details",
|
|
2354
|
+
responseType: "GetPoiResponse",
|
|
2355
|
+
params: []
|
|
2356
|
+
},
|
|
2357
|
+
"spacemolt/get_queue": {
|
|
2358
|
+
tool: "spacemolt",
|
|
2359
|
+
action: "get_queue",
|
|
2360
|
+
kind: "query",
|
|
2361
|
+
summary: "Get action queue (v2 format)",
|
|
2362
|
+
responseType: "V2GameState",
|
|
2363
|
+
params: []
|
|
2364
|
+
},
|
|
2365
|
+
"spacemolt/get_ship": {
|
|
2366
|
+
tool: "spacemolt",
|
|
2367
|
+
action: "get_ship",
|
|
2368
|
+
kind: "query",
|
|
2369
|
+
summary: "Get ship and module details (v2 format)",
|
|
2370
|
+
responseType: "V2GameState",
|
|
2371
|
+
params: []
|
|
2372
|
+
},
|
|
2373
|
+
"spacemolt/get_skills": {
|
|
2374
|
+
tool: "spacemolt",
|
|
2375
|
+
action: "get_skills",
|
|
2376
|
+
kind: "query",
|
|
2377
|
+
summary: "Get skills progress (v2 format)",
|
|
2378
|
+
responseType: "V2GameState",
|
|
2379
|
+
params: []
|
|
2380
|
+
},
|
|
2381
|
+
"spacemolt/get_state": {
|
|
2382
|
+
tool: "spacemolt",
|
|
2383
|
+
action: "get_state",
|
|
2384
|
+
kind: "query",
|
|
2385
|
+
summary: "Get full canonical game state (v2)",
|
|
2386
|
+
responseType: "V2GameState",
|
|
2387
|
+
params: []
|
|
2388
|
+
},
|
|
2389
|
+
"spacemolt/get_status": {
|
|
2390
|
+
tool: "spacemolt",
|
|
2391
|
+
action: "get_status",
|
|
2392
|
+
kind: "query",
|
|
2393
|
+
summary: "Get full canonical game state (v2)",
|
|
2394
|
+
responseType: "V2GameState",
|
|
2395
|
+
params: []
|
|
2396
|
+
},
|
|
2397
|
+
"spacemolt/get_system": {
|
|
2398
|
+
tool: "spacemolt",
|
|
2399
|
+
action: "get_system",
|
|
2400
|
+
kind: "query",
|
|
2401
|
+
summary: "Get your current system details",
|
|
2402
|
+
responseType: "GetSystemResponse",
|
|
2403
|
+
params: []
|
|
2404
|
+
},
|
|
2405
|
+
"spacemolt/get_system_agents": {
|
|
2406
|
+
tool: "spacemolt",
|
|
2407
|
+
action: "get_system_agents",
|
|
2408
|
+
kind: "query",
|
|
2409
|
+
summary: "Get all uncloaked online players in your current system",
|
|
2410
|
+
responseType: "GetSystemAgentsResponse",
|
|
2411
|
+
params: []
|
|
2412
|
+
},
|
|
2413
|
+
"spacemolt/get_tax_estimate": {
|
|
2414
|
+
tool: "spacemolt",
|
|
2415
|
+
action: "get_tax_estimate",
|
|
2416
|
+
kind: "query",
|
|
2417
|
+
summary: "Preview what taxes you'd owe right now",
|
|
2418
|
+
responseType: "TaxEstimateResponse",
|
|
2419
|
+
params: []
|
|
2420
|
+
},
|
|
2421
|
+
"spacemolt/get_version": {
|
|
2422
|
+
tool: "spacemolt",
|
|
2423
|
+
action: "get_version",
|
|
2424
|
+
kind: "query",
|
|
2425
|
+
summary: "Get game version and release notes, with optional changelog pagination",
|
|
2426
|
+
responseType: "GetVersionResponse",
|
|
2427
|
+
params: [
|
|
2428
|
+
{ name: "count", type: "number", required: false, description: "Number of releases per page (1-20, default 5)" },
|
|
2429
|
+
{ name: "id", type: "string", required: false, description: "Exact version to look up (e.g. '0.188.0' or 'v0.188.0')", positional: 0 },
|
|
2430
|
+
{ name: "page", type: "number", required: false, description: "Page number (default 1)" },
|
|
2431
|
+
{ name: "text", type: "string", required: false, description: "Search term to find in release notes", positional: 2 }
|
|
2432
|
+
]
|
|
2433
|
+
},
|
|
2434
|
+
"spacemolt/hunt": {
|
|
2435
|
+
tool: "spacemolt",
|
|
2436
|
+
action: "hunt",
|
|
2437
|
+
kind: "mutation",
|
|
2438
|
+
summary: "Hunt a wildlife creature to start a battle",
|
|
2439
|
+
detailsType: "HuntResponse",
|
|
2440
|
+
params: [
|
|
2441
|
+
{ name: "id", type: "string", required: true, description: "Creature ID to hunt (from the 'creatures' list in get_nearby)", positional: 0 }
|
|
2442
|
+
]
|
|
2443
|
+
},
|
|
2444
|
+
"spacemolt/install_mod": {
|
|
2445
|
+
tool: "spacemolt",
|
|
2446
|
+
action: "install_mod",
|
|
2447
|
+
kind: "mutation",
|
|
2448
|
+
summary: "Install a module on your ship",
|
|
2449
|
+
detailsType: "InstallModResponse",
|
|
2450
|
+
params: [
|
|
2451
|
+
{ name: "id", type: "string", required: true, description: "Module ID to install/uninstall. CPU and power usage shown reflect your Engineering skill bonus (1% reduction per level).", positional: 0 }
|
|
2452
|
+
]
|
|
2453
|
+
},
|
|
2454
|
+
"spacemolt/jettison": {
|
|
2455
|
+
tool: "spacemolt",
|
|
2456
|
+
action: "jettison",
|
|
2457
|
+
kind: "mutation",
|
|
2458
|
+
summary: "Jettison items from cargo into space",
|
|
2459
|
+
detailsType: "JettisonResponse",
|
|
2460
|
+
params: [
|
|
2461
|
+
{ name: "id", type: "string", required: false, description: "ID of the item to jettison (e.g., iron_ore, steel_plate). Required for single mode.", positional: 0 },
|
|
2462
|
+
{ name: "items", type: "{ item_id: string; quantity: number }[]", required: false, description: "Bulk mode: array of cargo items to dump in one action (max 100), all into one container. Each entry needs item_id and quantity. When provided, the top-level item_id/quantity are ignored." },
|
|
2463
|
+
{ name: "quantity", type: "number", required: false, description: "Quantity to jettison. Required for single mode.", positional: 1 }
|
|
2464
|
+
]
|
|
2465
|
+
},
|
|
2466
|
+
"spacemolt/jump": {
|
|
2467
|
+
tool: "spacemolt",
|
|
2468
|
+
action: "jump",
|
|
2469
|
+
kind: "mutation",
|
|
2470
|
+
summary: "Jump to an adjacent star system, or plot a numeric bearing with a Pathfinder Drive",
|
|
2471
|
+
detailsType: "JumpResponse",
|
|
2472
|
+
params: [
|
|
2473
|
+
{ name: "id", type: "string", required: true, description: "ID of the adjacent system to jump to (use get_system to see connected systems), or a numeric compass bearing in degrees for an off-network Pathfinder Drive jump. While already on a pathfinder drift, a fresh numeric bearing re-plots the heading from your current galactic position (same 5x fuel cost each time).", positional: 0 }
|
|
2474
|
+
]
|
|
2475
|
+
},
|
|
2476
|
+
"spacemolt/list_passengers": {
|
|
2477
|
+
tool: "spacemolt",
|
|
2478
|
+
action: "list_passengers",
|
|
2479
|
+
kind: "query",
|
|
2480
|
+
summary: "List the passengers currently aboard your ship",
|
|
2481
|
+
responseType: "ListPassengersResponse",
|
|
2482
|
+
params: []
|
|
2483
|
+
},
|
|
2484
|
+
"spacemolt/list_station_passengers": {
|
|
2485
|
+
tool: "spacemolt",
|
|
2486
|
+
action: "list_station_passengers",
|
|
2487
|
+
kind: "query",
|
|
2488
|
+
summary: "List citizens waiting for transport at your current station",
|
|
2489
|
+
responseType: "StationPassengersResponse",
|
|
2490
|
+
params: []
|
|
2491
|
+
},
|
|
2492
|
+
"spacemolt/load_passenger": {
|
|
2493
|
+
tool: "spacemolt",
|
|
2494
|
+
action: "load_passenger",
|
|
2495
|
+
kind: "mutation",
|
|
2496
|
+
summary: "Load all waiting passengers bound for a destination into your passenger berths",
|
|
2497
|
+
detailsType: "LoadPassengersResponse",
|
|
2498
|
+
params: [
|
|
2499
|
+
{ name: "id", type: "string", required: true, description: "Destination station ID or name. Loads all waiting passengers here bound for it, up to your free berths.", positional: 0 }
|
|
2500
|
+
]
|
|
2501
|
+
},
|
|
2502
|
+
"spacemolt/mine": {
|
|
2503
|
+
tool: "spacemolt",
|
|
2504
|
+
action: "mine",
|
|
2505
|
+
kind: "mutation",
|
|
2506
|
+
summary: "Mine resources from asteroids, ice fields, or gas clouds",
|
|
2507
|
+
detailsType: "MineResponse",
|
|
2508
|
+
params: []
|
|
2509
|
+
},
|
|
2510
|
+
"spacemolt/prepay_tax": {
|
|
2511
|
+
tool: "spacemolt",
|
|
2512
|
+
action: "prepay_tax",
|
|
2513
|
+
kind: "mutation",
|
|
2514
|
+
summary: "Prepay credits toward your next tax assessment",
|
|
2515
|
+
detailsType: "PrepayTaxResponse",
|
|
2516
|
+
params: [
|
|
2517
|
+
{ name: "quantity", type: "number", required: true, description: "Credits to move into the tax-prepayment pool (positive). Covers the next assessment before the wallet/treasury; surplus is refunded.", positional: 1 }
|
|
2518
|
+
]
|
|
2519
|
+
},
|
|
2520
|
+
"spacemolt/recycle": {
|
|
2521
|
+
tool: "spacemolt",
|
|
2522
|
+
action: "recycle",
|
|
2523
|
+
kind: "mutation",
|
|
2524
|
+
summary: "Queue a recycling job: consume a recipe's outputs to recover a fraction of its inputs",
|
|
2525
|
+
detailsType: "RecycleJobResponse",
|
|
2526
|
+
params: [
|
|
2527
|
+
{ name: "deliver_to", type: "string", required: false, description: "Output destination: 'storage' (default), 'faction' (faction main store — requires manage treasury permission), or 'faction:<bucket name or id>' for a specific Storage Extension bucket." },
|
|
2528
|
+
{ name: "dry_run", type: "boolean", required: false, description: "Return a cost+time quote (feedstock consumed, fees, venue, ETA) without queuing anything. Not supported with bulk jobs." },
|
|
2529
|
+
{ name: "facility_id", type: "string", required: false, description: "Route to a specific recycler facility ID (overrides auto-selection)." },
|
|
2530
|
+
{ name: "id", type: "string", required: false, description: "Recipe ID to recycle (the recycler consumes the recipe's outputs and returns a lossy fraction of its inputs). Use catalog with type=recipes to browse.", positional: 0 },
|
|
2531
|
+
{ name: "job_id", type: "string", required: false, description: "Cancel this queued job (refunding its unconsumed escrow) instead of recycling. Setting job_id implies cancel." },
|
|
2532
|
+
{ name: "job_ids", type: "string[]", required: false, description: "Bulk cancel: cancel many queued jobs in one action. Each ID is cancelled independently with per-job success/failure, so one bad ID doesn't sink the batch. Refunds the unconsumed escrow of every cancelled job." },
|
|
2533
|
+
{ name: "jobs", type: "Record<string, unknown>[]", required: false, description: "Bulk mode: recycle many recipes in one action. Each entry: {recipe_id, quantity, facility_id?, deliver_to?, source?}. When set, top-level recipe_id/quantity are ignored; each job is processed independently (partial success). Max 50." },
|
|
2534
|
+
{ name: "quantity", type: "number", required: false, description: "Number of the recipe's output items to feed in and break down (default 1). Rounded up to a whole number of recycling runs.", positional: 1 },
|
|
2535
|
+
{ name: "source", type: "string", required: false, description: "Where feedstock (and labor/rental credits) are pulled FROM. Same values as deliver_to. Defaults to deliver_to." }
|
|
2536
|
+
]
|
|
2537
|
+
},
|
|
2538
|
+
"spacemolt/refuel": {
|
|
2539
|
+
tool: "spacemolt",
|
|
2540
|
+
action: "refuel",
|
|
2541
|
+
kind: "mutation",
|
|
2542
|
+
summary: "Refuel your ship or transfer fuel to another ship",
|
|
2543
|
+
detailsType: "RefuelResponse",
|
|
2544
|
+
params: [
|
|
2545
|
+
{ name: "id", type: "string", required: false, description: "Specific fuel cell type to use (e.g. fuel_cell, fuel_cell_premium, fuel_cell_military). Auto-selects cheapest if omitted.", positional: 0 },
|
|
2546
|
+
{ name: "quantity", type: "number", required: false, description: "Number of fuel cells to burn or units to transfer (default 1). Applies only to fuel-cell purchases and ship-to-ship transfers; station (credit) refueling ignores quantity and always fills your tank to full.", positional: 1 },
|
|
2547
|
+
{ name: "target", type: "string", required: false, description: "Player ID or username to transfer fuel to, or 'fleet' for fleet fuel status. Requires a Refueling Pump module for transfers." }
|
|
2548
|
+
]
|
|
2549
|
+
},
|
|
2550
|
+
"spacemolt/repair": {
|
|
2551
|
+
tool: "spacemolt",
|
|
2552
|
+
action: "repair",
|
|
2553
|
+
kind: "mutation",
|
|
2554
|
+
summary: "Repair hull — at station (credits), in space (repair kits), or on another ship (repair arm + kits)",
|
|
2555
|
+
detailsType: "RepairResponse",
|
|
2556
|
+
params: [
|
|
2557
|
+
{ name: "item_id", type: "string", required: false, description: "Specific repair item to use (e.g. repair_kit, hull_patch). Auto-selects cheapest if omitted." },
|
|
2558
|
+
{ name: "quantity", type: "number", required: false, description: "Number of repair kits to use (default 1). Capped to what's available and what hull needs.", positional: 1 },
|
|
2559
|
+
{ name: "target", type: "string", required: false, description: "Player ID or username to repair, or 'fleet' for fleet hull status. Requires a Repair Arm module for ship-to-ship repair." }
|
|
2560
|
+
]
|
|
2561
|
+
},
|
|
2562
|
+
"spacemolt/repair_module": {
|
|
2563
|
+
tool: "spacemolt",
|
|
2564
|
+
action: "repair_module",
|
|
2565
|
+
kind: "mutation",
|
|
2566
|
+
summary: "Repair wear on a module using a Repair Kit",
|
|
2567
|
+
detailsType: "RepairModuleResponse",
|
|
2568
|
+
params: [
|
|
2569
|
+
{ name: "id", type: "string", required: true, description: "Instance ID of the module to repair (must be in cargo, not fitted)", positional: 0 }
|
|
2570
|
+
]
|
|
2571
|
+
},
|
|
2572
|
+
"spacemolt/scan": {
|
|
2573
|
+
tool: "spacemolt",
|
|
2574
|
+
action: "scan",
|
|
2575
|
+
kind: "mutation",
|
|
2576
|
+
summary: "Scan a target, or sweep the area for cloaked ships when no target is given",
|
|
2577
|
+
detailsType: "ScanResponse",
|
|
2578
|
+
params: [
|
|
2579
|
+
{ name: "id", type: "string", required: false, description: "ID/username of the player or NPC to scan. Omit to run an area sensor sweep that reveals cloaked ships at your location your scanner out-powers.", positional: 0 }
|
|
2580
|
+
]
|
|
2581
|
+
},
|
|
2582
|
+
"spacemolt/search_systems": {
|
|
2583
|
+
tool: "spacemolt",
|
|
2584
|
+
action: "search_systems",
|
|
2585
|
+
kind: "query",
|
|
2586
|
+
summary: "Search for systems by name",
|
|
2587
|
+
responseType: "SearchSystemsResponse",
|
|
2588
|
+
params: [
|
|
2589
|
+
{ name: "text", type: "string", required: true, description: "Search query - case-insensitive partial match on system names (e.g., 'sol' matches 'Sol', 'Solarian', etc.)", positional: 2 }
|
|
2590
|
+
]
|
|
2591
|
+
},
|
|
2592
|
+
"spacemolt/self_destruct": {
|
|
2593
|
+
tool: "spacemolt",
|
|
2594
|
+
action: "self_destruct",
|
|
2595
|
+
kind: "mutation",
|
|
2596
|
+
summary: "Destroy your own ship",
|
|
2597
|
+
detailsType: "SelfDestructResponse",
|
|
2598
|
+
params: []
|
|
2599
|
+
},
|
|
2600
|
+
"spacemolt/sell": {
|
|
2601
|
+
tool: "spacemolt",
|
|
2602
|
+
action: "sell",
|
|
2603
|
+
kind: "mutation",
|
|
2604
|
+
summary: "Sell items at market price on the station exchange",
|
|
2605
|
+
detailsType: "SellResponse",
|
|
2606
|
+
params: [
|
|
2607
|
+
{ name: "auto_list", type: "boolean", required: false, description: "If true, automatically create a sell order for unsold items at the average fill price (1% listing fee applies)." },
|
|
2608
|
+
{ name: "id", type: "string", required: true, description: "ID of the item to sell (e.g., iron_ore, steel_plate)", positional: 0 },
|
|
2609
|
+
{ name: "quantity", type: "number", required: true, description: "Quantity to sell", positional: 1 }
|
|
2610
|
+
]
|
|
2611
|
+
},
|
|
2612
|
+
"spacemolt/subscribe_observation": {
|
|
2613
|
+
tool: "spacemolt",
|
|
2614
|
+
action: "subscribe_observation",
|
|
2615
|
+
kind: "query",
|
|
2616
|
+
summary: "Subscribe to live presence updates at your current POI and system",
|
|
2617
|
+
responseType: "SubscribeObservationResponse",
|
|
2618
|
+
params: [
|
|
2619
|
+
{ name: "active_scan", type: "boolean", required: false, description: "Also run a continuous active sensor sweep that resolves cloaked ships (requires a scanner, must be undocked, burns 1 fuel/tick, alerts cloakers). Omit or false for the passive presence feed only." }
|
|
2620
|
+
]
|
|
2621
|
+
},
|
|
2622
|
+
"spacemolt/survey_system": {
|
|
2623
|
+
tool: "spacemolt",
|
|
2624
|
+
action: "survey_system",
|
|
2625
|
+
kind: "mutation",
|
|
2626
|
+
summary: "Scan for hidden deep core deposits in the current system",
|
|
2627
|
+
detailsType: "SurveySystemResponse",
|
|
2628
|
+
params: []
|
|
2629
|
+
},
|
|
2630
|
+
"spacemolt/travel": {
|
|
2631
|
+
tool: "spacemolt",
|
|
2632
|
+
action: "travel",
|
|
2633
|
+
kind: "mutation",
|
|
2634
|
+
summary: "Travel to a different Point of Interest (POI) within your current system",
|
|
2635
|
+
detailsType: "TravelResponse",
|
|
2636
|
+
params: [
|
|
2637
|
+
{ name: "id", type: "string", required: true, description: "UUID of the POI to travel to (use get_system to see available POIs)", positional: 0 }
|
|
2638
|
+
]
|
|
2639
|
+
},
|
|
2640
|
+
"spacemolt/undock": {
|
|
2641
|
+
tool: "spacemolt",
|
|
2642
|
+
action: "undock",
|
|
2643
|
+
kind: "mutation",
|
|
2644
|
+
summary: "Undock from a base",
|
|
2645
|
+
detailsType: "UndockResponse",
|
|
2646
|
+
params: []
|
|
2647
|
+
},
|
|
2648
|
+
"spacemolt/uninstall_mod": {
|
|
2649
|
+
tool: "spacemolt",
|
|
2650
|
+
action: "uninstall_mod",
|
|
2651
|
+
kind: "mutation",
|
|
2652
|
+
summary: "Uninstall a module from your ship",
|
|
2653
|
+
detailsType: "UninstallModResponse",
|
|
2654
|
+
params: [
|
|
2655
|
+
{ name: "id", type: "string", required: true, description: "Module ID to install/uninstall. CPU and power usage shown reflect your Engineering skill bonus (1% reduction per level).", positional: 0 }
|
|
2656
|
+
]
|
|
2657
|
+
},
|
|
2658
|
+
"spacemolt/unload_passenger": {
|
|
2659
|
+
tool: "spacemolt",
|
|
2660
|
+
action: "unload_passenger",
|
|
2661
|
+
kind: "mutation",
|
|
2662
|
+
summary: "Put a passenger (or everyone) off the ship at the current station",
|
|
2663
|
+
detailsType: "UnloadPassengerResponse",
|
|
2664
|
+
params: [
|
|
2665
|
+
{ name: "id", type: "string", required: true, description: 'Name (or citizen ID) of the passenger to put off the ship at the current station, or "all" to put every passenger off at once.', positional: 0 }
|
|
2666
|
+
]
|
|
2667
|
+
},
|
|
2668
|
+
"spacemolt/unsubscribe_observation": {
|
|
2669
|
+
tool: "spacemolt",
|
|
2670
|
+
action: "unsubscribe_observation",
|
|
2671
|
+
kind: "query",
|
|
2672
|
+
summary: "Cancel your live observation watch",
|
|
2673
|
+
responseType: "UnsubscribeObservationResponse",
|
|
2674
|
+
params: []
|
|
2675
|
+
},
|
|
2676
|
+
"spacemolt/use_item": {
|
|
2677
|
+
tool: "spacemolt",
|
|
2678
|
+
action: "use_item",
|
|
2679
|
+
kind: "mutation",
|
|
2680
|
+
summary: "Use a consumable item from cargo",
|
|
2681
|
+
detailsType: "UseItemResponse",
|
|
2682
|
+
params: [
|
|
2683
|
+
{ name: "id", type: "string", required: true, description: "ID of the consumable item to use (e.g., repair_kit, shield_cell, emergency_warp)", positional: 0 },
|
|
2684
|
+
{ name: "quantity", type: "number", required: false, description: "Number to consume (default 1). For repair/shield items, using more restores more. For buffs, only 1 is consumed.", positional: 1 }
|
|
2685
|
+
]
|
|
2686
|
+
},
|
|
2687
|
+
"spacemolt/view_completed_mission": {
|
|
2688
|
+
tool: "spacemolt",
|
|
2689
|
+
action: "view_completed_mission",
|
|
2690
|
+
kind: "query",
|
|
2691
|
+
summary: "View full details of a completed mission including dialog",
|
|
2692
|
+
responseType: "ViewCompletedMissionResponse",
|
|
2693
|
+
params: [
|
|
2694
|
+
{ name: "id", type: "string", required: true, description: "Template ID of the completed mission to view", positional: 0 }
|
|
2695
|
+
]
|
|
2696
|
+
}
|
|
2697
|
+
};
|
|
2698
|
+
|
|
2699
|
+
// src/generated/commands.gen.ts
|
|
2700
|
+
function buildCommands(dispatch) {
|
|
2701
|
+
const bind = (tool, action) => (params) => dispatch(tool, action, params);
|
|
2702
|
+
return {
|
|
2703
|
+
spacemolt: {
|
|
2704
|
+
abandon_mission: bind("spacemolt", "abandon_mission"),
|
|
2705
|
+
accept_mission: bind("spacemolt", "accept_mission"),
|
|
2706
|
+
attack: bind("spacemolt", "attack"),
|
|
2707
|
+
buy: bind("spacemolt", "buy"),
|
|
2708
|
+
cloak: bind("spacemolt", "cloak"),
|
|
2709
|
+
complete_mission: bind("spacemolt", "complete_mission"),
|
|
2710
|
+
completed_missions: bind("spacemolt", "completed_missions"),
|
|
2711
|
+
craft: bind("spacemolt", "craft"),
|
|
2712
|
+
decline_mission: bind("spacemolt", "decline_mission"),
|
|
2713
|
+
distress_signal: bind("spacemolt", "distress_signal"),
|
|
2714
|
+
dock: bind("spacemolt", "dock"),
|
|
2715
|
+
find_route: bind("spacemolt", "find_route"),
|
|
2716
|
+
get_achievements: bind("spacemolt", "get_achievements"),
|
|
2717
|
+
get_active_missions: bind("spacemolt", "get_active_missions"),
|
|
2718
|
+
get_base: bind("spacemolt", "get_base"),
|
|
2719
|
+
get_cargo: bind("spacemolt", "get_cargo"),
|
|
2720
|
+
get_commands: bind("spacemolt", "get_commands"),
|
|
2721
|
+
get_empire_info: bind("spacemolt", "get_empire_info"),
|
|
2722
|
+
get_faction_achievements: bind("spacemolt", "get_faction_achievements"),
|
|
2723
|
+
get_guide: bind("spacemolt", "get_guide"),
|
|
2724
|
+
get_location: bind("spacemolt", "get_location"),
|
|
2725
|
+
get_map: bind("spacemolt", "get_map"),
|
|
2726
|
+
get_missions: bind("spacemolt", "get_missions"),
|
|
2727
|
+
get_nearby: bind("spacemolt", "get_nearby"),
|
|
2728
|
+
get_notifications: bind("spacemolt", "get_notifications"),
|
|
2729
|
+
get_player: bind("spacemolt", "get_player"),
|
|
2730
|
+
get_poi: bind("spacemolt", "get_poi"),
|
|
2731
|
+
get_queue: bind("spacemolt", "get_queue"),
|
|
2732
|
+
get_ship: bind("spacemolt", "get_ship"),
|
|
2733
|
+
get_skills: bind("spacemolt", "get_skills"),
|
|
2734
|
+
get_state: bind("spacemolt", "get_state"),
|
|
2735
|
+
get_status: bind("spacemolt", "get_status"),
|
|
2736
|
+
get_system: bind("spacemolt", "get_system"),
|
|
2737
|
+
get_system_agents: bind("spacemolt", "get_system_agents"),
|
|
2738
|
+
get_tax_estimate: bind("spacemolt", "get_tax_estimate"),
|
|
2739
|
+
get_version: bind("spacemolt", "get_version"),
|
|
2740
|
+
hunt: bind("spacemolt", "hunt"),
|
|
2741
|
+
install_mod: bind("spacemolt", "install_mod"),
|
|
2742
|
+
jettison: bind("spacemolt", "jettison"),
|
|
2743
|
+
jump: bind("spacemolt", "jump"),
|
|
2744
|
+
list_passengers: bind("spacemolt", "list_passengers"),
|
|
2745
|
+
list_station_passengers: bind("spacemolt", "list_station_passengers"),
|
|
2746
|
+
load_passenger: bind("spacemolt", "load_passenger"),
|
|
2747
|
+
mine: bind("spacemolt", "mine"),
|
|
2748
|
+
prepay_tax: bind("spacemolt", "prepay_tax"),
|
|
2749
|
+
recycle: bind("spacemolt", "recycle"),
|
|
2750
|
+
refuel: bind("spacemolt", "refuel"),
|
|
2751
|
+
repair: bind("spacemolt", "repair"),
|
|
2752
|
+
repair_module: bind("spacemolt", "repair_module"),
|
|
2753
|
+
scan: bind("spacemolt", "scan"),
|
|
2754
|
+
search_systems: bind("spacemolt", "search_systems"),
|
|
2755
|
+
self_destruct: bind("spacemolt", "self_destruct"),
|
|
2756
|
+
sell: bind("spacemolt", "sell"),
|
|
2757
|
+
subscribe_observation: bind("spacemolt", "subscribe_observation"),
|
|
2758
|
+
survey_system: bind("spacemolt", "survey_system"),
|
|
2759
|
+
travel: bind("spacemolt", "travel"),
|
|
2760
|
+
undock: bind("spacemolt", "undock"),
|
|
2761
|
+
uninstall_mod: bind("spacemolt", "uninstall_mod"),
|
|
2762
|
+
unload_passenger: bind("spacemolt", "unload_passenger"),
|
|
2763
|
+
unsubscribe_observation: bind("spacemolt", "unsubscribe_observation"),
|
|
2764
|
+
use_item: bind("spacemolt", "use_item"),
|
|
2765
|
+
view_completed_mission: bind("spacemolt", "view_completed_mission")
|
|
2766
|
+
},
|
|
2767
|
+
spacemolt_auth: {
|
|
2768
|
+
claim: bind("spacemolt_auth", "claim"),
|
|
2769
|
+
login: bind("spacemolt_auth", "login"),
|
|
2770
|
+
login_token: bind("spacemolt_auth", "login_token"),
|
|
2771
|
+
logout: bind("spacemolt_auth", "logout"),
|
|
2772
|
+
register: bind("spacemolt_auth", "register")
|
|
2773
|
+
},
|
|
2774
|
+
spacemolt_battle: {
|
|
2775
|
+
advance: bind("spacemolt_battle", "advance"),
|
|
2776
|
+
engage: bind("spacemolt_battle", "engage"),
|
|
2777
|
+
reload: bind("spacemolt_battle", "reload"),
|
|
2778
|
+
retreat: bind("spacemolt_battle", "retreat"),
|
|
2779
|
+
stance: bind("spacemolt_battle", "stance"),
|
|
2780
|
+
status: bind("spacemolt_battle", "status"),
|
|
2781
|
+
target: bind("spacemolt_battle", "target")
|
|
2782
|
+
},
|
|
2783
|
+
spacemolt_citizenship: {
|
|
2784
|
+
apply: bind("spacemolt_citizenship", "apply"),
|
|
2785
|
+
list: bind("spacemolt_citizenship", "list"),
|
|
2786
|
+
renounce: bind("spacemolt_citizenship", "renounce"),
|
|
2787
|
+
withdraw: bind("spacemolt_citizenship", "withdraw")
|
|
2788
|
+
},
|
|
2789
|
+
spacemolt_drone: {
|
|
2790
|
+
deploy: bind("spacemolt_drone", "deploy"),
|
|
2791
|
+
get: bind("spacemolt_drone", "get"),
|
|
2792
|
+
list: bind("spacemolt_drone", "list"),
|
|
2793
|
+
load: bind("spacemolt_drone", "load"),
|
|
2794
|
+
name: bind("spacemolt_drone", "name"),
|
|
2795
|
+
recall: bind("spacemolt_drone", "recall"),
|
|
2796
|
+
unload: bind("spacemolt_drone", "unload"),
|
|
2797
|
+
upload: bind("spacemolt_drone", "upload")
|
|
2798
|
+
},
|
|
2799
|
+
spacemolt_facility: {
|
|
2800
|
+
allow_faction: bind("spacemolt_facility", "allow_faction"),
|
|
2801
|
+
allow_player: bind("spacemolt_facility", "allow_player"),
|
|
2802
|
+
ban: bind("spacemolt_facility", "ban"),
|
|
2803
|
+
base_cost: bind("spacemolt_facility", "base_cost"),
|
|
2804
|
+
browse_for_sale: bind("spacemolt_facility", "browse_for_sale"),
|
|
2805
|
+
build: bind("spacemolt_facility", "build"),
|
|
2806
|
+
buy_listing: bind("spacemolt_facility", "buy_listing"),
|
|
2807
|
+
buy_ship_license: bind("spacemolt_facility", "buy_ship_license"),
|
|
2808
|
+
cancel_listing: bind("spacemolt_facility", "cancel_listing"),
|
|
2809
|
+
deploy_outpost: bind("spacemolt_facility", "deploy_outpost"),
|
|
2810
|
+
dismantle: bind("spacemolt_facility", "dismantle"),
|
|
2811
|
+
facility_set_description: bind("spacemolt_facility", "facility_set_description"),
|
|
2812
|
+
faction_build: bind("spacemolt_facility", "faction_build"),
|
|
2813
|
+
faction_dismantle: bind("spacemolt_facility", "faction_dismantle"),
|
|
2814
|
+
faction_list: bind("spacemolt_facility", "faction_list"),
|
|
2815
|
+
faction_owned: bind("spacemolt_facility", "faction_owned"),
|
|
2816
|
+
faction_upgrade: bind("spacemolt_facility", "faction_upgrade"),
|
|
2817
|
+
found_station: bind("spacemolt_facility", "found_station"),
|
|
2818
|
+
job_add: bind("spacemolt_facility", "job_add"),
|
|
2819
|
+
job_cancel: bind("spacemolt_facility", "job_cancel"),
|
|
2820
|
+
job_list: bind("spacemolt_facility", "job_list"),
|
|
2821
|
+
job_reorder: bind("spacemolt_facility", "job_reorder"),
|
|
2822
|
+
list: bind("spacemolt_facility", "list"),
|
|
2823
|
+
list_for_sale: bind("spacemolt_facility", "list_for_sale"),
|
|
2824
|
+
owned: bind("spacemolt_facility", "owned"),
|
|
2825
|
+
personal_build: bind("spacemolt_facility", "personal_build"),
|
|
2826
|
+
personal_decorate: bind("spacemolt_facility", "personal_decorate"),
|
|
2827
|
+
personal_visit: bind("spacemolt_facility", "personal_visit"),
|
|
2828
|
+
remove_faction: bind("spacemolt_facility", "remove_faction"),
|
|
2829
|
+
remove_player: bind("spacemolt_facility", "remove_player"),
|
|
2830
|
+
set_access: bind("spacemolt_facility", "set_access"),
|
|
2831
|
+
set_build_policy: bind("spacemolt_facility", "set_build_policy"),
|
|
2832
|
+
set_description: bind("spacemolt_facility", "set_description"),
|
|
2833
|
+
set_market_fee: bind("spacemolt_facility", "set_market_fee"),
|
|
2834
|
+
set_name: bind("spacemolt_facility", "set_name"),
|
|
2835
|
+
set_output_price: bind("spacemolt_facility", "set_output_price"),
|
|
2836
|
+
set_public: bind("spacemolt_facility", "set_public"),
|
|
2837
|
+
set_refuel_price: bind("spacemolt_facility", "set_refuel_price"),
|
|
2838
|
+
set_repair_price: bind("spacemolt_facility", "set_repair_price"),
|
|
2839
|
+
set_service_access: bind("spacemolt_facility", "set_service_access"),
|
|
2840
|
+
station_info: bind("spacemolt_facility", "station_info"),
|
|
2841
|
+
station_set_name: bind("spacemolt_facility", "station_set_name"),
|
|
2842
|
+
transfer: bind("spacemolt_facility", "transfer"),
|
|
2843
|
+
types: bind("spacemolt_facility", "types"),
|
|
2844
|
+
unban: bind("spacemolt_facility", "unban"),
|
|
2845
|
+
upgrade: bind("spacemolt_facility", "upgrade"),
|
|
2846
|
+
upgrades: bind("spacemolt_facility", "upgrades")
|
|
2847
|
+
},
|
|
2848
|
+
spacemolt_faction: {
|
|
2849
|
+
accept_ally: bind("spacemolt_faction", "accept_ally"),
|
|
2850
|
+
accept_invite: bind("spacemolt_faction", "accept_invite"),
|
|
2851
|
+
accept_peace: bind("spacemolt_faction", "accept_peace"),
|
|
2852
|
+
cancel_mission: bind("spacemolt_faction", "cancel_mission"),
|
|
2853
|
+
create: bind("spacemolt_faction", "create"),
|
|
2854
|
+
declare_war: bind("spacemolt_faction", "declare_war"),
|
|
2855
|
+
decline_invite: bind("spacemolt_faction", "decline_invite"),
|
|
2856
|
+
delete_role: bind("spacemolt_faction", "delete_role"),
|
|
2857
|
+
delete_room: bind("spacemolt_faction", "delete_room"),
|
|
2858
|
+
garages: bind("spacemolt_faction", "garages"),
|
|
2859
|
+
get_invites: bind("spacemolt_faction", "get_invites"),
|
|
2860
|
+
info: bind("spacemolt_faction", "info"),
|
|
2861
|
+
invite: bind("spacemolt_faction", "invite"),
|
|
2862
|
+
join: bind("spacemolt_faction", "join"),
|
|
2863
|
+
kick: bind("spacemolt_faction", "kick"),
|
|
2864
|
+
leave: bind("spacemolt_faction", "leave"),
|
|
2865
|
+
list: bind("spacemolt_faction", "list"),
|
|
2866
|
+
list_missions: bind("spacemolt_faction", "list_missions"),
|
|
2867
|
+
prepay_tax: bind("spacemolt_faction", "prepay_tax"),
|
|
2868
|
+
propose_ally: bind("spacemolt_faction", "propose_ally"),
|
|
2869
|
+
propose_peace: bind("spacemolt_faction", "propose_peace"),
|
|
2870
|
+
remove_ally: bind("spacemolt_faction", "remove_ally"),
|
|
2871
|
+
remove_enemy: bind("spacemolt_faction", "remove_enemy"),
|
|
2872
|
+
rooms: bind("spacemolt_faction", "rooms"),
|
|
2873
|
+
set_enemy: bind("spacemolt_faction", "set_enemy"),
|
|
2874
|
+
tax_estimate: bind("spacemolt_faction", "tax_estimate"),
|
|
2875
|
+
visit_room: bind("spacemolt_faction", "visit_room"),
|
|
2876
|
+
withdraw_invite: bind("spacemolt_faction", "withdraw_invite")
|
|
2877
|
+
},
|
|
2878
|
+
spacemolt_faction_admin: {
|
|
2879
|
+
create_role: bind("spacemolt_faction_admin", "create_role"),
|
|
2880
|
+
edit: bind("spacemolt_faction_admin", "edit"),
|
|
2881
|
+
edit_role: bind("spacemolt_faction_admin", "edit_role"),
|
|
2882
|
+
post_mission: bind("spacemolt_faction_admin", "post_mission"),
|
|
2883
|
+
promote: bind("spacemolt_faction_admin", "promote"),
|
|
2884
|
+
write_room: bind("spacemolt_faction_admin", "write_room")
|
|
2885
|
+
},
|
|
2886
|
+
spacemolt_faction_commerce: {
|
|
2887
|
+
create_buy_order: bind("spacemolt_faction_commerce", "create_buy_order"),
|
|
2888
|
+
create_sell_order: bind("spacemolt_faction_commerce", "create_sell_order")
|
|
2889
|
+
},
|
|
2890
|
+
spacemolt_fleet: {
|
|
2891
|
+
accept: bind("spacemolt_fleet", "accept"),
|
|
2892
|
+
board: bind("spacemolt_fleet", "board"),
|
|
2893
|
+
create: bind("spacemolt_fleet", "create"),
|
|
2894
|
+
decline: bind("spacemolt_fleet", "decline"),
|
|
2895
|
+
disband: bind("spacemolt_fleet", "disband"),
|
|
2896
|
+
disembark: bind("spacemolt_fleet", "disembark"),
|
|
2897
|
+
invite: bind("spacemolt_fleet", "invite"),
|
|
2898
|
+
kick: bind("spacemolt_fleet", "kick"),
|
|
2899
|
+
leave: bind("spacemolt_fleet", "leave"),
|
|
2900
|
+
status: bind("spacemolt_fleet", "status")
|
|
2901
|
+
},
|
|
2902
|
+
spacemolt_intel: {
|
|
2903
|
+
espionage: bind("spacemolt_intel", "espionage"),
|
|
2904
|
+
intel_status: bind("spacemolt_intel", "intel_status"),
|
|
2905
|
+
query_intel: bind("spacemolt_intel", "query_intel"),
|
|
2906
|
+
query_trade_intel: bind("spacemolt_intel", "query_trade_intel"),
|
|
2907
|
+
scan_poi: bind("spacemolt_intel", "scan_poi"),
|
|
2908
|
+
submit_intel: bind("spacemolt_intel", "submit_intel"),
|
|
2909
|
+
submit_trade_intel: bind("spacemolt_intel", "submit_trade_intel"),
|
|
2910
|
+
trade_intel_status: bind("spacemolt_intel", "trade_intel_status")
|
|
2911
|
+
},
|
|
2912
|
+
spacemolt_market: {
|
|
2913
|
+
analyze_market: bind("spacemolt_market", "analyze_market"),
|
|
2914
|
+
cancel_order: bind("spacemolt_market", "cancel_order"),
|
|
2915
|
+
create_buy_order: bind("spacemolt_market", "create_buy_order"),
|
|
2916
|
+
create_sell_order: bind("spacemolt_market", "create_sell_order"),
|
|
2917
|
+
estimate_purchase: bind("spacemolt_market", "estimate_purchase"),
|
|
2918
|
+
modify_order: bind("spacemolt_market", "modify_order"),
|
|
2919
|
+
subscribe_market: bind("spacemolt_market", "subscribe_market"),
|
|
2920
|
+
unsubscribe_market: bind("spacemolt_market", "unsubscribe_market"),
|
|
2921
|
+
view_market: bind("spacemolt_market", "view_market"),
|
|
2922
|
+
view_orders: bind("spacemolt_market", "view_orders")
|
|
2923
|
+
},
|
|
2924
|
+
spacemolt_salvage: {
|
|
2925
|
+
insure: bind("spacemolt_salvage", "insure"),
|
|
2926
|
+
loot: bind("spacemolt_salvage", "loot"),
|
|
2927
|
+
policies: bind("spacemolt_salvage", "policies"),
|
|
2928
|
+
quote: bind("spacemolt_salvage", "quote"),
|
|
2929
|
+
release: bind("spacemolt_salvage", "release"),
|
|
2930
|
+
scrap: bind("spacemolt_salvage", "scrap"),
|
|
2931
|
+
sell: bind("spacemolt_salvage", "sell"),
|
|
2932
|
+
set_home: bind("spacemolt_salvage", "set_home"),
|
|
2933
|
+
tow: bind("spacemolt_salvage", "tow"),
|
|
2934
|
+
wrecks: bind("spacemolt_salvage", "wrecks")
|
|
2935
|
+
},
|
|
2936
|
+
spacemolt_ship: {
|
|
2937
|
+
browse_ships: bind("spacemolt_ship", "browse_ships"),
|
|
2938
|
+
buy_listed_ship: bind("spacemolt_ship", "buy_listed_ship"),
|
|
2939
|
+
cancel_commission: bind("spacemolt_ship", "cancel_commission"),
|
|
2940
|
+
cancel_ship_buy_order: bind("spacemolt_ship", "cancel_ship_buy_order"),
|
|
2941
|
+
cancel_ship_listing: bind("spacemolt_ship", "cancel_ship_listing"),
|
|
2942
|
+
commission_quote: bind("spacemolt_ship", "commission_quote"),
|
|
2943
|
+
commission_ship: bind("spacemolt_ship", "commission_ship"),
|
|
2944
|
+
commission_status: bind("spacemolt_ship", "commission_status"),
|
|
2945
|
+
list_ship_for_sale: bind("spacemolt_ship", "list_ship_for_sale"),
|
|
2946
|
+
list_ships: bind("spacemolt_ship", "list_ships"),
|
|
2947
|
+
place_ship_buy_order: bind("spacemolt_ship", "place_ship_buy_order"),
|
|
2948
|
+
refit_ship: bind("spacemolt_ship", "refit_ship"),
|
|
2949
|
+
rename_ship: bind("spacemolt_ship", "rename_ship"),
|
|
2950
|
+
scrap_ship: bind("spacemolt_ship", "scrap_ship"),
|
|
2951
|
+
sell_ship: bind("spacemolt_ship", "sell_ship"),
|
|
2952
|
+
sell_ship_to_order: bind("spacemolt_ship", "sell_ship_to_order"),
|
|
2953
|
+
supply_commission: bind("spacemolt_ship", "supply_commission"),
|
|
2954
|
+
switch_ship: bind("spacemolt_ship", "switch_ship"),
|
|
2955
|
+
view_ship_buy_orders: bind("spacemolt_ship", "view_ship_buy_orders")
|
|
2956
|
+
},
|
|
2957
|
+
spacemolt_social: {
|
|
2958
|
+
captains_log_add: bind("spacemolt_social", "captains_log_add"),
|
|
2959
|
+
captains_log_delete: bind("spacemolt_social", "captains_log_delete"),
|
|
2960
|
+
captains_log_get: bind("spacemolt_social", "captains_log_get"),
|
|
2961
|
+
captains_log_list: bind("spacemolt_social", "captains_log_list"),
|
|
2962
|
+
chat: bind("spacemolt_social", "chat"),
|
|
2963
|
+
create_note: bind("spacemolt_social", "create_note"),
|
|
2964
|
+
delete_note: bind("spacemolt_social", "delete_note"),
|
|
2965
|
+
forum_create_thread: bind("spacemolt_social", "forum_create_thread"),
|
|
2966
|
+
forum_delete_reply: bind("spacemolt_social", "forum_delete_reply"),
|
|
2967
|
+
forum_delete_thread: bind("spacemolt_social", "forum_delete_thread"),
|
|
2968
|
+
forum_get_thread: bind("spacemolt_social", "forum_get_thread"),
|
|
2969
|
+
forum_list: bind("spacemolt_social", "forum_list"),
|
|
2970
|
+
forum_reply: bind("spacemolt_social", "forum_reply"),
|
|
2971
|
+
forum_upvote: bind("spacemolt_social", "forum_upvote"),
|
|
2972
|
+
get_action_log: bind("spacemolt_social", "get_action_log"),
|
|
2973
|
+
get_chat_history: bind("spacemolt_social", "get_chat_history"),
|
|
2974
|
+
get_notes: bind("spacemolt_social", "get_notes"),
|
|
2975
|
+
get_notification_settings: bind("spacemolt_social", "get_notification_settings"),
|
|
2976
|
+
mute_notifications: bind("spacemolt_social", "mute_notifications"),
|
|
2977
|
+
petition: bind("spacemolt_social", "petition"),
|
|
2978
|
+
read_note: bind("spacemolt_social", "read_note"),
|
|
2979
|
+
set_colors: bind("spacemolt_social", "set_colors"),
|
|
2980
|
+
set_status: bind("spacemolt_social", "set_status"),
|
|
2981
|
+
unmute_notifications: bind("spacemolt_social", "unmute_notifications"),
|
|
2982
|
+
write_note: bind("spacemolt_social", "write_note")
|
|
2983
|
+
},
|
|
2984
|
+
spacemolt_storage: {
|
|
2985
|
+
deposit: bind("spacemolt_storage", "deposit"),
|
|
2986
|
+
jettison: bind("spacemolt_storage", "jettison"),
|
|
2987
|
+
loot: bind("spacemolt_storage", "loot"),
|
|
2988
|
+
view: bind("spacemolt_storage", "view"),
|
|
2989
|
+
withdraw: bind("spacemolt_storage", "withdraw")
|
|
2990
|
+
},
|
|
2991
|
+
spacemolt_transfer: {
|
|
2992
|
+
get_trades: bind("spacemolt_transfer", "get_trades"),
|
|
2993
|
+
trade_accept: bind("spacemolt_transfer", "trade_accept"),
|
|
2994
|
+
trade_cancel: bind("spacemolt_transfer", "trade_cancel"),
|
|
2995
|
+
trade_decline: bind("spacemolt_transfer", "trade_decline"),
|
|
2996
|
+
trade_offer: bind("spacemolt_transfer", "trade_offer")
|
|
2997
|
+
}
|
|
2998
|
+
};
|
|
2999
|
+
}
|
|
3000
|
+
|
|
3001
|
+
// src/auth/clerk.ts
|
|
3002
|
+
async function clerkFetch(fetchImpl, url, apiKey, init) {
|
|
3003
|
+
const res = await fetchImpl(url, {
|
|
3004
|
+
...init,
|
|
3005
|
+
headers: { accept: "application/json", authorization: `Bearer ${apiKey}`, ...init?.headers ?? {} }
|
|
3006
|
+
});
|
|
3007
|
+
if (!res.ok) {
|
|
3008
|
+
const body = await res.text().catch(() => "");
|
|
3009
|
+
throw new Error(`${init?.method ?? "GET"} ${url} -> ${res.status} ${res.statusText}${body ? `: ${body}` : ""}`);
|
|
3010
|
+
}
|
|
3011
|
+
return res.json();
|
|
3012
|
+
}
|
|
3013
|
+
async function mintWsToken(opts) {
|
|
3014
|
+
const base = opts.httpBaseUrl.replace(/\/$/, "");
|
|
3015
|
+
const data = await clerkFetch(opts.fetchImpl ?? fetch, `${base}/api/player/${encodeURIComponent(opts.playerId)}/ws-token`, opts.apiKey, { method: "POST" });
|
|
3016
|
+
if (!data.token)
|
|
3017
|
+
throw new Error("ws-token response had no token");
|
|
3018
|
+
return data.token;
|
|
3019
|
+
}
|
|
3020
|
+
|
|
3021
|
+
class ClerkSource {
|
|
3022
|
+
opts;
|
|
3023
|
+
fetchImpl;
|
|
3024
|
+
httpBaseUrl;
|
|
3025
|
+
constructor(opts) {
|
|
3026
|
+
this.opts = opts;
|
|
3027
|
+
this.fetchImpl = opts.fetchImpl ?? fetch;
|
|
3028
|
+
this.httpBaseUrl = opts.httpBaseUrl.replace(/\/$/, "");
|
|
3029
|
+
}
|
|
3030
|
+
get apiKey() {
|
|
3031
|
+
return this.opts.apiKey;
|
|
3032
|
+
}
|
|
3033
|
+
async listPlayers() {
|
|
3034
|
+
const data = await clerkFetch(this.fetchImpl, `${this.httpBaseUrl}/api/registration-code`, this.opts.apiKey);
|
|
3035
|
+
return data.players ?? [];
|
|
3036
|
+
}
|
|
3037
|
+
mintWsToken(playerId) {
|
|
3038
|
+
return mintWsToken({ httpBaseUrl: this.httpBaseUrl, apiKey: this.opts.apiKey, playerId, fetchImpl: this.fetchImpl });
|
|
3039
|
+
}
|
|
3040
|
+
}
|
|
3041
|
+
|
|
3042
|
+
// src/errors.ts
|
|
3043
|
+
class SpacemoltError extends Error {
|
|
3044
|
+
code;
|
|
3045
|
+
details;
|
|
3046
|
+
requestId;
|
|
3047
|
+
command;
|
|
3048
|
+
tick;
|
|
3049
|
+
pendingCommand;
|
|
3050
|
+
constructor(code, message, opts = {}) {
|
|
3051
|
+
super(message);
|
|
3052
|
+
this.name = "SpacemoltError";
|
|
3053
|
+
this.code = code;
|
|
3054
|
+
this.details = opts.details;
|
|
3055
|
+
this.requestId = opts.requestId;
|
|
3056
|
+
this.command = opts.command;
|
|
3057
|
+
this.tick = opts.tick;
|
|
3058
|
+
this.pendingCommand = opts.pendingCommand;
|
|
3059
|
+
}
|
|
3060
|
+
}
|
|
3061
|
+
function errorFromFrame(frame) {
|
|
3062
|
+
return new SpacemoltError(frame.payload.code, frame.payload.message, {
|
|
3063
|
+
details: frame.payload.details,
|
|
3064
|
+
requestId: frame.request_id,
|
|
3065
|
+
pendingCommand: frame.payload.pending_command
|
|
3066
|
+
});
|
|
3067
|
+
}
|
|
3068
|
+
function errorFromActionFrame(frame) {
|
|
3069
|
+
return new SpacemoltError(frame.payload.code, frame.payload.message, {
|
|
3070
|
+
details: frame.payload.details,
|
|
3071
|
+
requestId: frame.request_id,
|
|
3072
|
+
command: frame.payload.command,
|
|
3073
|
+
tick: frame.payload.tick
|
|
3074
|
+
});
|
|
3075
|
+
}
|
|
3076
|
+
|
|
3077
|
+
class ConnectionClosedError extends Error {
|
|
3078
|
+
code;
|
|
3079
|
+
reason;
|
|
3080
|
+
constructor(message = "WebSocket connection closed", code, reason) {
|
|
3081
|
+
super(message);
|
|
3082
|
+
this.name = "ConnectionClosedError";
|
|
3083
|
+
this.code = code;
|
|
3084
|
+
this.reason = reason;
|
|
3085
|
+
}
|
|
3086
|
+
}
|
|
3087
|
+
var CLOSE_CODE = {
|
|
3088
|
+
SESSION_REPLACED: 4001,
|
|
3089
|
+
AUTH_TIMEOUT: 4002,
|
|
3090
|
+
CONNECTION_RATE_LIMITED: 4003
|
|
3091
|
+
};
|
|
3092
|
+
function retryAfterMsFromClose(err) {
|
|
3093
|
+
if (!(err instanceof ConnectionClosedError) || err.code !== CLOSE_CODE.CONNECTION_RATE_LIMITED)
|
|
3094
|
+
return;
|
|
3095
|
+
const match = err.reason?.match(/retry_after=(\d+)/);
|
|
3096
|
+
return match ? Number(match[1]) * 1000 : undefined;
|
|
3097
|
+
}
|
|
3098
|
+
|
|
3099
|
+
// src/events/emitter.ts
|
|
3100
|
+
class EventStream {
|
|
3101
|
+
onClose;
|
|
3102
|
+
buffer = [];
|
|
3103
|
+
waiting = [];
|
|
3104
|
+
ended = false;
|
|
3105
|
+
constructor(onClose) {
|
|
3106
|
+
this.onClose = onClose;
|
|
3107
|
+
}
|
|
3108
|
+
push(value) {
|
|
3109
|
+
if (this.ended)
|
|
3110
|
+
return;
|
|
3111
|
+
const next = this.waiting.shift();
|
|
3112
|
+
if (next)
|
|
3113
|
+
next({ value, done: false });
|
|
3114
|
+
else
|
|
3115
|
+
this.buffer.push(value);
|
|
3116
|
+
}
|
|
3117
|
+
end() {
|
|
3118
|
+
if (this.ended)
|
|
3119
|
+
return;
|
|
3120
|
+
this.ended = true;
|
|
3121
|
+
for (const w of this.waiting)
|
|
3122
|
+
w({ value: undefined, done: true });
|
|
3123
|
+
this.waiting.length = 0;
|
|
3124
|
+
}
|
|
3125
|
+
next() {
|
|
3126
|
+
if (this.buffer.length)
|
|
3127
|
+
return Promise.resolve({ value: this.buffer.shift(), done: false });
|
|
3128
|
+
if (this.ended)
|
|
3129
|
+
return Promise.resolve({ value: undefined, done: true });
|
|
3130
|
+
return new Promise((resolve) => this.waiting.push(resolve));
|
|
3131
|
+
}
|
|
3132
|
+
return() {
|
|
3133
|
+
this.end();
|
|
3134
|
+
this.onClose();
|
|
3135
|
+
return Promise.resolve({ value: undefined, done: true });
|
|
3136
|
+
}
|
|
3137
|
+
[Symbol.asyncIterator]() {
|
|
3138
|
+
return this;
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
3141
|
+
|
|
3142
|
+
class TypedEmitter {
|
|
3143
|
+
handlers = new Map;
|
|
3144
|
+
anyHandlers = new Set;
|
|
3145
|
+
streams = new Map;
|
|
3146
|
+
anyStreams = new Set;
|
|
3147
|
+
on(type, handler) {
|
|
3148
|
+
let set = this.handlers.get(type);
|
|
3149
|
+
if (!set) {
|
|
3150
|
+
set = new Set;
|
|
3151
|
+
this.handlers.set(type, set);
|
|
3152
|
+
}
|
|
3153
|
+
set.add(handler);
|
|
3154
|
+
return () => set.delete(handler);
|
|
3155
|
+
}
|
|
3156
|
+
onAny(handler) {
|
|
3157
|
+
this.anyHandlers.add(handler);
|
|
3158
|
+
return () => this.anyHandlers.delete(handler);
|
|
3159
|
+
}
|
|
3160
|
+
stream(type) {
|
|
3161
|
+
let set = this.streams.get(type);
|
|
3162
|
+
if (!set) {
|
|
3163
|
+
set = new Set;
|
|
3164
|
+
this.streams.set(type, set);
|
|
3165
|
+
}
|
|
3166
|
+
const localSet = set;
|
|
3167
|
+
const s = new EventStream(() => localSet.delete(s));
|
|
3168
|
+
localSet.add(s);
|
|
3169
|
+
return s;
|
|
3170
|
+
}
|
|
3171
|
+
anyStream() {
|
|
3172
|
+
const s = new EventStream(() => this.anyStreams.delete(s));
|
|
3173
|
+
this.anyStreams.add(s);
|
|
3174
|
+
return s;
|
|
3175
|
+
}
|
|
3176
|
+
emit(frame) {
|
|
3177
|
+
const set = this.handlers.get(frame.type);
|
|
3178
|
+
if (set)
|
|
3179
|
+
for (const h of [...set])
|
|
3180
|
+
this.safeCall(() => h(frame.payload));
|
|
3181
|
+
for (const h of [...this.anyHandlers])
|
|
3182
|
+
this.safeCall(() => h(frame));
|
|
3183
|
+
const streams = this.streams.get(frame.type);
|
|
3184
|
+
if (streams)
|
|
3185
|
+
for (const s of [...streams])
|
|
3186
|
+
this.safeCall(() => s.push(frame.payload));
|
|
3187
|
+
for (const s of [...this.anyStreams])
|
|
3188
|
+
this.safeCall(() => s.push(frame));
|
|
3189
|
+
}
|
|
3190
|
+
safeCall(fn) {
|
|
3191
|
+
try {
|
|
3192
|
+
fn();
|
|
3193
|
+
} catch (err) {
|
|
3194
|
+
console.warn(`[spacemolt] notification listener threw: ${err}`);
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3197
|
+
closeStreams() {
|
|
3198
|
+
for (const set of this.streams.values())
|
|
3199
|
+
for (const s of set)
|
|
3200
|
+
s.end();
|
|
3201
|
+
for (const s of this.anyStreams)
|
|
3202
|
+
s.end();
|
|
3203
|
+
}
|
|
3204
|
+
}
|
|
3205
|
+
|
|
3206
|
+
// src/state/market.ts
|
|
3207
|
+
class MarketCache {
|
|
3208
|
+
books = new Map;
|
|
3209
|
+
seed(snapshot) {
|
|
3210
|
+
if (!snapshot.base_id)
|
|
3211
|
+
return;
|
|
3212
|
+
const items = new Map;
|
|
3213
|
+
for (const item of snapshot.items ?? []) {
|
|
3214
|
+
if (item.item_id)
|
|
3215
|
+
items.set(item.item_id, item);
|
|
3216
|
+
}
|
|
3217
|
+
this.books.set(snapshot.base_id, {
|
|
3218
|
+
base_id: snapshot.base_id,
|
|
3219
|
+
base_name: snapshot.base_name,
|
|
3220
|
+
tick: 0,
|
|
3221
|
+
items
|
|
3222
|
+
});
|
|
3223
|
+
return snapshot.base_id;
|
|
3224
|
+
}
|
|
3225
|
+
applyUpdate(update) {
|
|
3226
|
+
let book = this.books.get(update.base_id);
|
|
3227
|
+
if (!book) {
|
|
3228
|
+
book = { base_id: update.base_id, base_name: update.base_name, tick: update.tick, items: new Map };
|
|
3229
|
+
this.books.set(update.base_id, book);
|
|
3230
|
+
}
|
|
3231
|
+
book.tick = update.tick;
|
|
3232
|
+
if (update.base_name)
|
|
3233
|
+
book.base_name = update.base_name;
|
|
3234
|
+
for (const item of update.items)
|
|
3235
|
+
book.items.set(item.item_id, item);
|
|
3236
|
+
}
|
|
3237
|
+
book(baseId) {
|
|
3238
|
+
return this.books.get(baseId);
|
|
3239
|
+
}
|
|
3240
|
+
bases() {
|
|
3241
|
+
return [...this.books.keys()];
|
|
3242
|
+
}
|
|
3243
|
+
drop(baseId) {
|
|
3244
|
+
this.books.delete(baseId);
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
|
|
3248
|
+
// src/state/observation.ts
|
|
3249
|
+
function indexBy(items, key) {
|
|
3250
|
+
const map = new Map;
|
|
3251
|
+
for (const item of items ?? []) {
|
|
3252
|
+
const k = key(item);
|
|
3253
|
+
if (k)
|
|
3254
|
+
map.set(k, item);
|
|
3255
|
+
}
|
|
3256
|
+
return map;
|
|
3257
|
+
}
|
|
3258
|
+
|
|
3259
|
+
class ObservationCache {
|
|
3260
|
+
view = null;
|
|
3261
|
+
seed(snapshot) {
|
|
3262
|
+
this.view = {
|
|
3263
|
+
poi_id: snapshot.poi_id,
|
|
3264
|
+
system_id: snapshot.system_id,
|
|
3265
|
+
tick: 0,
|
|
3266
|
+
nearby: indexBy(snapshot.nearby, (p) => p.player_id),
|
|
3267
|
+
system: indexBy(snapshot.system_agents, (p) => p.player_id),
|
|
3268
|
+
cloaked: indexBy(snapshot.cloaked_contacts, (c) => c.target_id),
|
|
3269
|
+
unknownSignature: snapshot.unknown_signature ?? false,
|
|
3270
|
+
activeScan: snapshot.active_scan ?? false
|
|
3271
|
+
};
|
|
3272
|
+
return this.view;
|
|
3273
|
+
}
|
|
3274
|
+
applyUpdate(update) {
|
|
3275
|
+
if (!this.view) {
|
|
3276
|
+
this.view = {
|
|
3277
|
+
poi_id: update.poi_id,
|
|
3278
|
+
system_id: update.system_id,
|
|
3279
|
+
tick: update.tick,
|
|
3280
|
+
nearby: new Map,
|
|
3281
|
+
system: new Map,
|
|
3282
|
+
cloaked: new Map,
|
|
3283
|
+
unknownSignature: false,
|
|
3284
|
+
activeScan: false
|
|
3285
|
+
};
|
|
3286
|
+
}
|
|
3287
|
+
const v = this.view;
|
|
3288
|
+
v.tick = update.tick;
|
|
3289
|
+
for (const p of update.nearby_changed ?? [])
|
|
3290
|
+
if (p.player_id)
|
|
3291
|
+
v.nearby.set(p.player_id, p);
|
|
3292
|
+
for (const id of update.nearby_departed ?? [])
|
|
3293
|
+
v.nearby.delete(id);
|
|
3294
|
+
for (const p of update.system_changed ?? [])
|
|
3295
|
+
if (p.player_id)
|
|
3296
|
+
v.system.set(p.player_id, p);
|
|
3297
|
+
for (const id of update.system_departed ?? [])
|
|
3298
|
+
v.system.delete(id);
|
|
3299
|
+
for (const c of update.cloaked_resolved ?? [])
|
|
3300
|
+
if (c.target_id)
|
|
3301
|
+
v.cloaked.set(c.target_id, c);
|
|
3302
|
+
for (const id of update.cloaked_lost ?? [])
|
|
3303
|
+
v.cloaked.delete(id);
|
|
3304
|
+
if (update.unknown_signature !== undefined)
|
|
3305
|
+
v.unknownSignature = update.unknown_signature;
|
|
3306
|
+
if (update.active_scan !== undefined)
|
|
3307
|
+
v.activeScan = update.active_scan;
|
|
3308
|
+
}
|
|
3309
|
+
current() {
|
|
3310
|
+
return this.view;
|
|
3311
|
+
}
|
|
3312
|
+
clear() {
|
|
3313
|
+
this.view = null;
|
|
3314
|
+
}
|
|
3315
|
+
}
|
|
3316
|
+
|
|
3317
|
+
// src/state/cache.ts
|
|
3318
|
+
function asRecord(value) {
|
|
3319
|
+
return value;
|
|
3320
|
+
}
|
|
3321
|
+
|
|
3322
|
+
class StateCache {
|
|
3323
|
+
state = {};
|
|
3324
|
+
seed(snapshot) {
|
|
3325
|
+
const next = {};
|
|
3326
|
+
const changed = [];
|
|
3327
|
+
const src = asRecord(snapshot);
|
|
3328
|
+
for (const section of STATE_SECTIONS) {
|
|
3329
|
+
if (src[section] !== undefined) {
|
|
3330
|
+
asRecord(next)[section] = src[section];
|
|
3331
|
+
changed.push(section);
|
|
3332
|
+
}
|
|
3333
|
+
}
|
|
3334
|
+
this.state = next;
|
|
3335
|
+
return changed;
|
|
3336
|
+
}
|
|
3337
|
+
applyDelta(delta) {
|
|
3338
|
+
const changed = [];
|
|
3339
|
+
const src = asRecord(delta);
|
|
3340
|
+
for (const section of STATE_SECTIONS) {
|
|
3341
|
+
if (src[section] !== undefined) {
|
|
3342
|
+
asRecord(this.state)[section] = src[section];
|
|
3343
|
+
changed.push(section);
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
return changed;
|
|
3347
|
+
}
|
|
3348
|
+
patchSection(section, patch) {
|
|
3349
|
+
const current = this.state[section];
|
|
3350
|
+
if (current === undefined)
|
|
3351
|
+
return [];
|
|
3352
|
+
asRecord(this.state)[section] = { ...asRecord(current), ...patch };
|
|
3353
|
+
return [section];
|
|
3354
|
+
}
|
|
3355
|
+
snapshot() {
|
|
3356
|
+
return this.state;
|
|
3357
|
+
}
|
|
3358
|
+
get player() {
|
|
3359
|
+
return this.state.player;
|
|
3360
|
+
}
|
|
3361
|
+
get ship() {
|
|
3362
|
+
return this.state.ship;
|
|
3363
|
+
}
|
|
3364
|
+
get location() {
|
|
3365
|
+
return this.state.location;
|
|
3366
|
+
}
|
|
3367
|
+
get cargo() {
|
|
3368
|
+
return this.state.cargo;
|
|
3369
|
+
}
|
|
3370
|
+
get modules() {
|
|
3371
|
+
return this.state.modules;
|
|
3372
|
+
}
|
|
3373
|
+
get missions() {
|
|
3374
|
+
return this.state.missions;
|
|
3375
|
+
}
|
|
3376
|
+
get skills() {
|
|
3377
|
+
return this.state.skills;
|
|
3378
|
+
}
|
|
3379
|
+
get hasPendingAction() {
|
|
3380
|
+
return this.state.queue?.has_pending ?? false;
|
|
3381
|
+
}
|
|
3382
|
+
get credits() {
|
|
3383
|
+
return this.state.player?.credits;
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
|
|
3387
|
+
// src/transport/correlator.ts
|
|
3388
|
+
function extractPendingAck(structured) {
|
|
3389
|
+
if (!structured)
|
|
3390
|
+
return;
|
|
3391
|
+
if (structured["pending"] === true) {
|
|
3392
|
+
return { command: String(structured["command"] ?? ""), message: String(structured["message"] ?? "") };
|
|
3393
|
+
}
|
|
3394
|
+
const details = structured["details"];
|
|
3395
|
+
if (details && typeof details === "object" && details["pending"] === true) {
|
|
3396
|
+
const d = details;
|
|
3397
|
+
return { command: String(d["command"] ?? ""), message: String(d["message"] ?? "") };
|
|
3398
|
+
}
|
|
3399
|
+
return;
|
|
3400
|
+
}
|
|
3401
|
+
|
|
3402
|
+
class Correlator {
|
|
3403
|
+
pending = new Map;
|
|
3404
|
+
awaitQuery(requestId) {
|
|
3405
|
+
return new Promise((resolve, reject) => {
|
|
3406
|
+
this.pending.set(requestId, { kind: "query", resolve, reject });
|
|
3407
|
+
});
|
|
3408
|
+
}
|
|
3409
|
+
awaitMutation(requestId, onAck) {
|
|
3410
|
+
return new Promise((resolve, reject) => {
|
|
3411
|
+
this.pending.set(requestId, { kind: "mutation", resolve, reject, onAck });
|
|
3412
|
+
});
|
|
3413
|
+
}
|
|
3414
|
+
has(requestId) {
|
|
3415
|
+
return this.pending.has(requestId);
|
|
3416
|
+
}
|
|
3417
|
+
cancel(requestId) {
|
|
3418
|
+
this.pending.delete(requestId);
|
|
3419
|
+
}
|
|
3420
|
+
handle(frame) {
|
|
3421
|
+
const requestId = frame.request_id;
|
|
3422
|
+
if (!requestId)
|
|
3423
|
+
return false;
|
|
3424
|
+
const pending = this.pending.get(requestId);
|
|
3425
|
+
if (!pending)
|
|
3426
|
+
return false;
|
|
3427
|
+
switch (frame.type) {
|
|
3428
|
+
case "result": {
|
|
3429
|
+
const payload = frame.payload;
|
|
3430
|
+
if (pending.kind === "query") {
|
|
3431
|
+
this.settle(requestId);
|
|
3432
|
+
pending.resolve({ result: payload.result, structuredContent: payload.structuredContent });
|
|
3433
|
+
return true;
|
|
3434
|
+
}
|
|
3435
|
+
const structured = payload.structuredContent;
|
|
3436
|
+
const ack = extractPendingAck(structured);
|
|
3437
|
+
if (!ack) {
|
|
3438
|
+
this.settle(requestId);
|
|
3439
|
+
pending.resolve({
|
|
3440
|
+
command: String(structured?.["command"] ?? ""),
|
|
3441
|
+
tick: 0,
|
|
3442
|
+
delta: { details: structured }
|
|
3443
|
+
});
|
|
3444
|
+
return true;
|
|
3445
|
+
}
|
|
3446
|
+
pending.ack = ack;
|
|
3447
|
+
pending.onAck?.(ack);
|
|
3448
|
+
return true;
|
|
3449
|
+
}
|
|
3450
|
+
case "action_result": {
|
|
3451
|
+
if (pending.kind !== "mutation")
|
|
3452
|
+
return true;
|
|
3453
|
+
const payload = frame.payload;
|
|
3454
|
+
this.settle(requestId);
|
|
3455
|
+
pending.resolve({
|
|
3456
|
+
command: payload.command,
|
|
3457
|
+
tick: payload.tick,
|
|
3458
|
+
delta: payload.result,
|
|
3459
|
+
autoDocked: payload.auto_docked,
|
|
3460
|
+
autoUndocked: payload.auto_undocked
|
|
3461
|
+
});
|
|
3462
|
+
return true;
|
|
3463
|
+
}
|
|
3464
|
+
case "action_error": {
|
|
3465
|
+
this.settle(requestId);
|
|
3466
|
+
pending.reject(errorFromActionFrame(frame));
|
|
3467
|
+
return true;
|
|
3468
|
+
}
|
|
3469
|
+
case "error": {
|
|
3470
|
+
this.settle(requestId);
|
|
3471
|
+
pending.reject(errorFromFrame(frame));
|
|
3472
|
+
return true;
|
|
3473
|
+
}
|
|
3474
|
+
default:
|
|
3475
|
+
return false;
|
|
3476
|
+
}
|
|
3477
|
+
}
|
|
3478
|
+
rejectAll(err) {
|
|
3479
|
+
for (const pending of this.pending.values())
|
|
3480
|
+
pending.reject(err);
|
|
3481
|
+
this.pending.clear();
|
|
3482
|
+
}
|
|
3483
|
+
settle(requestId) {
|
|
3484
|
+
this.pending.delete(requestId);
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
|
|
3488
|
+
// src/transport/socket.ts
|
|
3489
|
+
var defaultFactory = (url) => new WebSocket(url);
|
|
3490
|
+
|
|
3491
|
+
class Socket {
|
|
3492
|
+
opts;
|
|
3493
|
+
ws = null;
|
|
3494
|
+
opened = false;
|
|
3495
|
+
closed = false;
|
|
3496
|
+
onFrame;
|
|
3497
|
+
onClose;
|
|
3498
|
+
constructor(opts) {
|
|
3499
|
+
this.opts = opts;
|
|
3500
|
+
}
|
|
3501
|
+
connect() {
|
|
3502
|
+
const factory = this.opts.webSocketFactory ?? defaultFactory;
|
|
3503
|
+
const ws = factory(this.opts.url);
|
|
3504
|
+
this.ws = ws;
|
|
3505
|
+
return new Promise((resolve, reject) => {
|
|
3506
|
+
let settled = false;
|
|
3507
|
+
ws.addEventListener("open", () => {
|
|
3508
|
+
this.opened = true;
|
|
3509
|
+
settled = true;
|
|
3510
|
+
resolve();
|
|
3511
|
+
});
|
|
3512
|
+
ws.addEventListener("message", (ev) => this.handleMessage(ev.data));
|
|
3513
|
+
ws.addEventListener("close", (ev) => {
|
|
3514
|
+
const err = new ConnectionClosedError("WebSocket connection closed", ev?.code, ev?.reason);
|
|
3515
|
+
if (!settled) {
|
|
3516
|
+
settled = true;
|
|
3517
|
+
reject(err);
|
|
3518
|
+
}
|
|
3519
|
+
this.markClosed(err);
|
|
3520
|
+
});
|
|
3521
|
+
ws.addEventListener("error", () => {
|
|
3522
|
+
if (!this.opened && !settled) {
|
|
3523
|
+
settled = true;
|
|
3524
|
+
reject(new ConnectionClosedError("WebSocket connection failed before open"));
|
|
3525
|
+
}
|
|
3526
|
+
});
|
|
3527
|
+
});
|
|
3528
|
+
}
|
|
3529
|
+
send(frame) {
|
|
3530
|
+
if (!this.ws || this.closed)
|
|
3531
|
+
throw new ConnectionClosedError("cannot send on a closed socket");
|
|
3532
|
+
this.ws.send(JSON.stringify(frame));
|
|
3533
|
+
}
|
|
3534
|
+
close() {
|
|
3535
|
+
this.ws?.close();
|
|
3536
|
+
}
|
|
3537
|
+
handleMessage(data) {
|
|
3538
|
+
const text = typeof data === "string" ? data : String(data);
|
|
3539
|
+
for (const line of text.split(`
|
|
3540
|
+
`)) {
|
|
3541
|
+
if (!line)
|
|
3542
|
+
continue;
|
|
3543
|
+
let frame;
|
|
3544
|
+
try {
|
|
3545
|
+
frame = JSON.parse(line);
|
|
3546
|
+
} catch (err) {
|
|
3547
|
+
const head = line.slice(0, 200);
|
|
3548
|
+
const tail = line.length > 400 ? line.slice(-200) : "";
|
|
3549
|
+
console.warn(`[spacemolt] dropped unparseable frame (${line.length} bytes): ${err} | head=${JSON.stringify(head)}${tail ? ` tail=${JSON.stringify(tail)}` : ""}`);
|
|
3550
|
+
continue;
|
|
3551
|
+
}
|
|
3552
|
+
this.onFrame?.(frame);
|
|
3553
|
+
}
|
|
3554
|
+
}
|
|
3555
|
+
markClosed(err) {
|
|
3556
|
+
if (this.closed)
|
|
3557
|
+
return;
|
|
3558
|
+
this.closed = true;
|
|
3559
|
+
this.onClose?.(err);
|
|
3560
|
+
}
|
|
3561
|
+
}
|
|
3562
|
+
|
|
3563
|
+
// src/account.ts
|
|
3564
|
+
var TRANSIT_ACTIONS = new Set(["spacemolt/jump", "spacemolt/travel"]);
|
|
3565
|
+
var DEFAULT_URL = "wss://game.spacemolt.com/ws/v2";
|
|
3566
|
+
var DEFAULT_CONNECT_TIMEOUT_MS = 15000;
|
|
3567
|
+
var DEFAULT_MUTATION_TIMEOUT_MS = 600000;
|
|
3568
|
+
var DEFAULT_FAST_MUTATION_TIMEOUT_MS = 180000;
|
|
3569
|
+
var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
3570
|
+
function withTimeout(promise, ms, message) {
|
|
3571
|
+
return new Promise((resolve, reject) => {
|
|
3572
|
+
const timer = setTimeout(() => reject(new SpacemoltError("connect_timeout", message)), ms);
|
|
3573
|
+
promise.then((value) => {
|
|
3574
|
+
clearTimeout(timer);
|
|
3575
|
+
resolve(value);
|
|
3576
|
+
}, (err) => {
|
|
3577
|
+
clearTimeout(timer);
|
|
3578
|
+
reject(err);
|
|
3579
|
+
});
|
|
3580
|
+
});
|
|
3581
|
+
}
|
|
3582
|
+
function normalizeReconnect(opt) {
|
|
3583
|
+
if (!opt)
|
|
3584
|
+
return null;
|
|
3585
|
+
const o = typeof opt === "object" ? opt : {};
|
|
3586
|
+
return {
|
|
3587
|
+
maxRetries: o.maxRetries ?? Number.POSITIVE_INFINITY,
|
|
3588
|
+
baseDelayMs: o.baseDelayMs ?? 1000,
|
|
3589
|
+
maxDelayMs: o.maxDelayMs ?? 30000
|
|
3590
|
+
};
|
|
3591
|
+
}
|
|
3592
|
+
function retryAfterMs(err) {
|
|
3593
|
+
const fromDetails = typeof err.details?.["retry_after"] === "number" ? err.details["retry_after"] : undefined;
|
|
3594
|
+
const match = err.message.match(/retry in (\d+)\s*second/i);
|
|
3595
|
+
const seconds = fromDetails ?? (match ? Number(match[1]) : undefined);
|
|
3596
|
+
return Math.max(250, (seconds ?? 1) * 1000);
|
|
3597
|
+
}
|
|
3598
|
+
|
|
3599
|
+
class Account {
|
|
3600
|
+
socket;
|
|
3601
|
+
correlator = new Correlator;
|
|
3602
|
+
cache = new StateCache;
|
|
3603
|
+
emitter = new TypedEmitter;
|
|
3604
|
+
marketCache = new MarketCache;
|
|
3605
|
+
observationCache = new ObservationCache;
|
|
3606
|
+
seedState;
|
|
3607
|
+
url;
|
|
3608
|
+
webSocketFactory;
|
|
3609
|
+
reconnectConfig;
|
|
3610
|
+
credentialsProvider;
|
|
3611
|
+
fetchImpl;
|
|
3612
|
+
maxRateLimitRetries;
|
|
3613
|
+
connectTimeoutMs;
|
|
3614
|
+
queryTimeoutMs;
|
|
3615
|
+
mutationTimeoutMs;
|
|
3616
|
+
fastMutationTimeoutMs;
|
|
3617
|
+
_id;
|
|
3618
|
+
requestSeq = 0;
|
|
3619
|
+
_welcome = null;
|
|
3620
|
+
_authenticated = false;
|
|
3621
|
+
_loginPayload = null;
|
|
3622
|
+
_commands = null;
|
|
3623
|
+
welcomeWaiter = null;
|
|
3624
|
+
pendingAuth = null;
|
|
3625
|
+
stateListener = null;
|
|
3626
|
+
userClosing = false;
|
|
3627
|
+
reconnecting = false;
|
|
3628
|
+
mutationLane = Promise.resolve();
|
|
3629
|
+
marketSubscribedState = false;
|
|
3630
|
+
subscribedMarketBaseId;
|
|
3631
|
+
observationSubscribedState = false;
|
|
3632
|
+
observationActiveScanState = false;
|
|
3633
|
+
subscribedObservationPoiId;
|
|
3634
|
+
reconnectedListener = null;
|
|
3635
|
+
reconnectingListener = null;
|
|
3636
|
+
disconnectedListener = null;
|
|
3637
|
+
constructor(opts = {}) {
|
|
3638
|
+
this.seedState = opts.seedState ?? true;
|
|
3639
|
+
this.url = opts.url ?? DEFAULT_URL;
|
|
3640
|
+
this.webSocketFactory = opts.webSocketFactory;
|
|
3641
|
+
this.credentialsProvider = opts.credentials;
|
|
3642
|
+
this.fetchImpl = opts.fetchImpl;
|
|
3643
|
+
this.maxRateLimitRetries = opts.maxRateLimitRetries ?? 5;
|
|
3644
|
+
this.connectTimeoutMs = opts.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
|
|
3645
|
+
this.queryTimeoutMs = opts.queryTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
|
|
3646
|
+
this.mutationTimeoutMs = opts.mutationTimeoutMs ?? DEFAULT_MUTATION_TIMEOUT_MS;
|
|
3647
|
+
this.fastMutationTimeoutMs = opts.fastMutationTimeoutMs ?? DEFAULT_FAST_MUTATION_TIMEOUT_MS;
|
|
3648
|
+
this._id = opts.id;
|
|
3649
|
+
this.reconnectConfig = normalizeReconnect(opts.reconnect);
|
|
3650
|
+
this.makeSocket();
|
|
3651
|
+
this.emitter.on("market_update", (p) => this.marketCache.applyUpdate(p));
|
|
3652
|
+
this.emitter.on("observation_update", (p) => {
|
|
3653
|
+
this.observationCache.applyUpdate(p);
|
|
3654
|
+
this.bridgeObservationToLocation();
|
|
3655
|
+
});
|
|
3656
|
+
}
|
|
3657
|
+
makeSocket() {
|
|
3658
|
+
this.socket = new Socket({ url: this.url, webSocketFactory: this.webSocketFactory });
|
|
3659
|
+
this.socket.onFrame = (frame) => this.routeFrame(frame);
|
|
3660
|
+
this.socket.onClose = (err) => this.handleClose(err);
|
|
3661
|
+
}
|
|
3662
|
+
get state() {
|
|
3663
|
+
return this.cache.snapshot();
|
|
3664
|
+
}
|
|
3665
|
+
get id() {
|
|
3666
|
+
return this._id;
|
|
3667
|
+
}
|
|
3668
|
+
get player() {
|
|
3669
|
+
return this.cache.player;
|
|
3670
|
+
}
|
|
3671
|
+
get ship() {
|
|
3672
|
+
return this.cache.ship;
|
|
3673
|
+
}
|
|
3674
|
+
get location() {
|
|
3675
|
+
return this.cache.location;
|
|
3676
|
+
}
|
|
3677
|
+
get cargo() {
|
|
3678
|
+
return this.cache.cargo;
|
|
3679
|
+
}
|
|
3680
|
+
get skills() {
|
|
3681
|
+
return this.cache.skills;
|
|
3682
|
+
}
|
|
3683
|
+
get credits() {
|
|
3684
|
+
return this.cache.credits;
|
|
3685
|
+
}
|
|
3686
|
+
get hasPendingAction() {
|
|
3687
|
+
return this.cache.hasPendingAction;
|
|
3688
|
+
}
|
|
3689
|
+
get loginPayload() {
|
|
3690
|
+
return this._loginPayload;
|
|
3691
|
+
}
|
|
3692
|
+
get commands() {
|
|
3693
|
+
if (!this._commands) {
|
|
3694
|
+
this._commands = buildCommands((tool, action, payload) => this.send(tool, action, payload));
|
|
3695
|
+
}
|
|
3696
|
+
return this._commands;
|
|
3697
|
+
}
|
|
3698
|
+
get welcome() {
|
|
3699
|
+
return this._welcome;
|
|
3700
|
+
}
|
|
3701
|
+
get authenticated() {
|
|
3702
|
+
return this._authenticated;
|
|
3703
|
+
}
|
|
3704
|
+
get marketSubscribed() {
|
|
3705
|
+
return this.marketSubscribedState;
|
|
3706
|
+
}
|
|
3707
|
+
get observationSubscribed() {
|
|
3708
|
+
return this.observationSubscribedState;
|
|
3709
|
+
}
|
|
3710
|
+
get observationActiveScan() {
|
|
3711
|
+
return this.observationActiveScanState;
|
|
3712
|
+
}
|
|
3713
|
+
connect() {
|
|
3714
|
+
return this.open();
|
|
3715
|
+
}
|
|
3716
|
+
async open() {
|
|
3717
|
+
this._welcome = null;
|
|
3718
|
+
await this.socket.connect();
|
|
3719
|
+
if (this._welcome)
|
|
3720
|
+
return this._welcome;
|
|
3721
|
+
const waitForWelcome = new Promise((resolve, reject) => {
|
|
3722
|
+
this.welcomeWaiter = { resolve, reject };
|
|
3723
|
+
});
|
|
3724
|
+
try {
|
|
3725
|
+
return await withTimeout(waitForWelcome, this.connectTimeoutMs, `No welcome frame received within ${this.connectTimeoutMs}ms`);
|
|
3726
|
+
} catch (err) {
|
|
3727
|
+
this.welcomeWaiter = null;
|
|
3728
|
+
this.socket.close();
|
|
3729
|
+
throw err;
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3732
|
+
async register(params) {
|
|
3733
|
+
const registerPromise = new Promise((resolve, reject) => {
|
|
3734
|
+
this.beginAuth((state, registered) => {
|
|
3735
|
+
if (!registered) {
|
|
3736
|
+
reject(new SpacemoltError("missing_credentials", "register succeeded but no credentials frame was received"));
|
|
3737
|
+
return;
|
|
3738
|
+
}
|
|
3739
|
+
resolve({ password: registered.password, player_id: registered.player_id, state });
|
|
3740
|
+
}, reject);
|
|
3741
|
+
this.sendFrame("spacemolt_auth", "register", { ...params }, this.nextRequestId());
|
|
3742
|
+
});
|
|
3743
|
+
let result;
|
|
3744
|
+
try {
|
|
3745
|
+
result = await withTimeout(registerPromise, this.connectTimeoutMs, `No register response received within ${this.connectTimeoutMs}ms`);
|
|
3746
|
+
} catch (err) {
|
|
3747
|
+
this.pendingAuth = null;
|
|
3748
|
+
throw err;
|
|
3749
|
+
}
|
|
3750
|
+
this._loginPayload = result.state;
|
|
3751
|
+
await this.maybeSeedState();
|
|
3752
|
+
return result;
|
|
3753
|
+
}
|
|
3754
|
+
login(params) {
|
|
3755
|
+
return this.authExchange("login", { ...params });
|
|
3756
|
+
}
|
|
3757
|
+
loginToken(token) {
|
|
3758
|
+
return this.authExchange("login_token", { token });
|
|
3759
|
+
}
|
|
3760
|
+
authenticate(creds) {
|
|
3761
|
+
return this.withRateLimitRetry(() => this.authenticateOnce(creds));
|
|
3762
|
+
}
|
|
3763
|
+
async authenticateOnce(creds) {
|
|
3764
|
+
switch (creds.kind) {
|
|
3765
|
+
case "login":
|
|
3766
|
+
await this.login({ username: creds.username, password: creds.password });
|
|
3767
|
+
return;
|
|
3768
|
+
case "login_token":
|
|
3769
|
+
await this.loginToken(creds.token);
|
|
3770
|
+
return;
|
|
3771
|
+
case "clerk": {
|
|
3772
|
+
const token = await mintWsToken({
|
|
3773
|
+
httpBaseUrl: creds.httpBaseUrl,
|
|
3774
|
+
apiKey: creds.apiKey,
|
|
3775
|
+
playerId: creds.playerId,
|
|
3776
|
+
fetchImpl: this.fetchImpl
|
|
3777
|
+
});
|
|
3778
|
+
await this.loginToken(token);
|
|
3779
|
+
return;
|
|
3780
|
+
}
|
|
3781
|
+
case "register":
|
|
3782
|
+
await this.register({
|
|
3783
|
+
username: creds.username,
|
|
3784
|
+
empire: creds.empire,
|
|
3785
|
+
registration_code: creds.registration_code
|
|
3786
|
+
});
|
|
3787
|
+
return;
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
async refresh() {
|
|
3791
|
+
const res = await this.query("spacemolt", "get_status");
|
|
3792
|
+
const snapshot = res.structuredContent;
|
|
3793
|
+
if (snapshot) {
|
|
3794
|
+
const changed = this.cache.seed(snapshot);
|
|
3795
|
+
if (changed.includes("location"))
|
|
3796
|
+
this.checkSubscriptionsAgainstLocation();
|
|
3797
|
+
if (changed.length)
|
|
3798
|
+
this.stateListener?.(changed);
|
|
3799
|
+
}
|
|
3800
|
+
return this.cache.snapshot();
|
|
3801
|
+
}
|
|
3802
|
+
onStateChange(listener) {
|
|
3803
|
+
this.stateListener = listener;
|
|
3804
|
+
}
|
|
3805
|
+
async logout() {
|
|
3806
|
+
await this.query("spacemolt_auth", "logout");
|
|
3807
|
+
this._authenticated = false;
|
|
3808
|
+
}
|
|
3809
|
+
query(tool, action, payload) {
|
|
3810
|
+
return this.withRateLimitRetry(async () => {
|
|
3811
|
+
const requestId = this.nextRequestId();
|
|
3812
|
+
const promise = this.correlator.awaitQuery(requestId);
|
|
3813
|
+
this.sendFrame(tool, action, payload, requestId);
|
|
3814
|
+
try {
|
|
3815
|
+
return await withTimeout(promise, this.queryTimeoutMs, `No response to ${tool}/${action} within ${this.queryTimeoutMs}ms`);
|
|
3816
|
+
} catch (err) {
|
|
3817
|
+
this.correlator.cancel(requestId);
|
|
3818
|
+
throw err;
|
|
3819
|
+
}
|
|
3820
|
+
});
|
|
3821
|
+
}
|
|
3822
|
+
mutate(tool, action, payload, onAck) {
|
|
3823
|
+
const mutationTimeoutMs = TRANSIT_ACTIONS.has(`${tool}/${action}`) ? this.mutationTimeoutMs : this.fastMutationTimeoutMs;
|
|
3824
|
+
return this.enqueueMutation(() => this.withRateLimitRetry(() => {
|
|
3825
|
+
const requestId = this.nextRequestId();
|
|
3826
|
+
const promise = this.awaitMutationWithTimeout(requestId, mutationTimeoutMs, onAck);
|
|
3827
|
+
this.sendFrame(tool, action, payload, requestId);
|
|
3828
|
+
return promise;
|
|
3829
|
+
}));
|
|
3830
|
+
}
|
|
3831
|
+
awaitMutationWithTimeout(requestId, mutationTimeoutMs, onAck) {
|
|
3832
|
+
return new Promise((resolve, reject) => {
|
|
3833
|
+
let timer;
|
|
3834
|
+
const arm = (ms, message) => {
|
|
3835
|
+
if (timer)
|
|
3836
|
+
clearTimeout(timer);
|
|
3837
|
+
timer = setTimeout(() => {
|
|
3838
|
+
this.correlator.cancel(requestId);
|
|
3839
|
+
reject(new SpacemoltError("mutation_timeout", message));
|
|
3840
|
+
}, ms);
|
|
3841
|
+
};
|
|
3842
|
+
arm(this.queryTimeoutMs, `No response to mutation ${requestId} within ${this.queryTimeoutMs}ms`);
|
|
3843
|
+
this.correlator.awaitMutation(requestId, (ack) => {
|
|
3844
|
+
arm(mutationTimeoutMs, `No action_result for mutation ${requestId} within ${mutationTimeoutMs}ms of its ack`);
|
|
3845
|
+
onAck?.(ack);
|
|
3846
|
+
}).then((value) => {
|
|
3847
|
+
if (timer)
|
|
3848
|
+
clearTimeout(timer);
|
|
3849
|
+
resolve(value);
|
|
3850
|
+
}, (err) => {
|
|
3851
|
+
if (timer)
|
|
3852
|
+
clearTimeout(timer);
|
|
3853
|
+
reject(err);
|
|
3854
|
+
});
|
|
3855
|
+
});
|
|
3856
|
+
}
|
|
3857
|
+
send(tool, action, payload) {
|
|
3858
|
+
const def = ACTIONS[`${tool}/${action}`];
|
|
3859
|
+
return def?.kind === "mutation" ? this.mutate(tool, action, payload) : this.query(tool, action, payload);
|
|
3860
|
+
}
|
|
3861
|
+
on(type, handler) {
|
|
3862
|
+
return this.emitter.on(type, handler);
|
|
3863
|
+
}
|
|
3864
|
+
onAny(handler) {
|
|
3865
|
+
return this.emitter.onAny(handler);
|
|
3866
|
+
}
|
|
3867
|
+
events(type) {
|
|
3868
|
+
return this.emitter.stream(type);
|
|
3869
|
+
}
|
|
3870
|
+
anyEvents() {
|
|
3871
|
+
return this.emitter.anyStream();
|
|
3872
|
+
}
|
|
3873
|
+
async subscribeMarket() {
|
|
3874
|
+
const res = await this.query("spacemolt_market", "subscribe_market");
|
|
3875
|
+
const snapshot = res.structuredContent;
|
|
3876
|
+
if (snapshot)
|
|
3877
|
+
this.marketCache.seed(snapshot);
|
|
3878
|
+
this.marketSubscribedState = true;
|
|
3879
|
+
this.subscribedMarketBaseId = snapshot?.base_id;
|
|
3880
|
+
return snapshot ?? {};
|
|
3881
|
+
}
|
|
3882
|
+
async unsubscribeMarket() {
|
|
3883
|
+
const baseId = this.subscribedMarketBaseId ?? this.location?.docked_at ?? this.marketCache.bases()[0];
|
|
3884
|
+
this.marketSubscribedState = false;
|
|
3885
|
+
this.subscribedMarketBaseId = undefined;
|
|
3886
|
+
await this.query("spacemolt_market", "unsubscribe_market");
|
|
3887
|
+
if (baseId)
|
|
3888
|
+
this.marketCache.drop(baseId);
|
|
3889
|
+
}
|
|
3890
|
+
market(baseId) {
|
|
3891
|
+
return this.marketCache.book(baseId);
|
|
3892
|
+
}
|
|
3893
|
+
async subscribeObservation(activeScan = false) {
|
|
3894
|
+
const res = await this.query("spacemolt", "subscribe_observation", activeScan ? { active_scan: true } : undefined);
|
|
3895
|
+
const snapshot = res.structuredContent;
|
|
3896
|
+
if (snapshot) {
|
|
3897
|
+
this.observationCache.seed(snapshot);
|
|
3898
|
+
this.bridgeObservationToLocation();
|
|
3899
|
+
}
|
|
3900
|
+
this.observationSubscribedState = true;
|
|
3901
|
+
this.observationActiveScanState = activeScan;
|
|
3902
|
+
this.subscribedObservationPoiId = snapshot?.poi_id;
|
|
3903
|
+
return snapshot ?? {};
|
|
3904
|
+
}
|
|
3905
|
+
async unsubscribeObservation() {
|
|
3906
|
+
this.observationSubscribedState = false;
|
|
3907
|
+
this.subscribedObservationPoiId = undefined;
|
|
3908
|
+
await this.query("spacemolt", "unsubscribe_observation");
|
|
3909
|
+
this.observationCache.clear();
|
|
3910
|
+
}
|
|
3911
|
+
bridgeObservationToLocation() {
|
|
3912
|
+
const view = this.observationCache.current();
|
|
3913
|
+
if (!view)
|
|
3914
|
+
return;
|
|
3915
|
+
const nearbyPlayers = [...view.nearby.values()];
|
|
3916
|
+
const changed = this.cache.patchSection("location", {
|
|
3917
|
+
nearby_players: nearbyPlayers,
|
|
3918
|
+
nearby_player_count: view.nearby.size
|
|
3919
|
+
});
|
|
3920
|
+
if (changed.length)
|
|
3921
|
+
this.stateListener?.(changed);
|
|
3922
|
+
}
|
|
3923
|
+
observation() {
|
|
3924
|
+
return this.observationCache.current();
|
|
3925
|
+
}
|
|
3926
|
+
checkSubscriptionsAgainstLocation() {
|
|
3927
|
+
if (this.marketSubscribedState && this.subscribedMarketBaseId !== undefined && this.location?.docked_at !== this.subscribedMarketBaseId) {
|
|
3928
|
+
this.marketCache.drop(this.subscribedMarketBaseId);
|
|
3929
|
+
this.marketSubscribedState = false;
|
|
3930
|
+
this.subscribedMarketBaseId = undefined;
|
|
3931
|
+
}
|
|
3932
|
+
if (this.observationSubscribedState && this.subscribedObservationPoiId !== undefined && this.location?.poi_id !== this.subscribedObservationPoiId) {
|
|
3933
|
+
this.observationCache.clear();
|
|
3934
|
+
this.observationSubscribedState = false;
|
|
3935
|
+
this.subscribedObservationPoiId = undefined;
|
|
3936
|
+
}
|
|
3937
|
+
}
|
|
3938
|
+
close() {
|
|
3939
|
+
this.userClosing = true;
|
|
3940
|
+
this.socket.close();
|
|
3941
|
+
}
|
|
3942
|
+
onReconnected(listener) {
|
|
3943
|
+
this.reconnectedListener = listener;
|
|
3944
|
+
}
|
|
3945
|
+
onReconnecting(listener) {
|
|
3946
|
+
this.reconnectingListener = listener;
|
|
3947
|
+
}
|
|
3948
|
+
onDisconnected(listener) {
|
|
3949
|
+
this.disconnectedListener = listener;
|
|
3950
|
+
}
|
|
3951
|
+
async authExchange(action, payload) {
|
|
3952
|
+
const authPromise = new Promise((resolve, reject) => {
|
|
3953
|
+
this.beginAuth(resolve, reject);
|
|
3954
|
+
this.sendFrame("spacemolt_auth", action, payload, this.nextRequestId());
|
|
3955
|
+
});
|
|
3956
|
+
let state;
|
|
3957
|
+
try {
|
|
3958
|
+
state = await withTimeout(authPromise, this.connectTimeoutMs, `No auth response received within ${this.connectTimeoutMs}ms`);
|
|
3959
|
+
} catch (err) {
|
|
3960
|
+
this.pendingAuth = null;
|
|
3961
|
+
throw err;
|
|
3962
|
+
}
|
|
3963
|
+
this._loginPayload = state;
|
|
3964
|
+
await this.maybeSeedState();
|
|
3965
|
+
return state;
|
|
3966
|
+
}
|
|
3967
|
+
async maybeSeedState() {
|
|
3968
|
+
if (!this.seedState)
|
|
3969
|
+
return;
|
|
3970
|
+
try {
|
|
3971
|
+
await this.refresh();
|
|
3972
|
+
} catch {}
|
|
3973
|
+
}
|
|
3974
|
+
beginAuth(onLoggedIn, onError) {
|
|
3975
|
+
if (this.pendingAuth) {
|
|
3976
|
+
onError(new SpacemoltError("auth_in_progress", "another auth exchange is already in flight"));
|
|
3977
|
+
return;
|
|
3978
|
+
}
|
|
3979
|
+
if (this._authenticated) {
|
|
3980
|
+
onError(new SpacemoltError("already_authenticated", "this connection is already authenticated"));
|
|
3981
|
+
return;
|
|
3982
|
+
}
|
|
3983
|
+
this.pendingAuth = { onLoggedIn, onError };
|
|
3984
|
+
}
|
|
3985
|
+
sendFrame(tool, action, payload, requestId) {
|
|
3986
|
+
this.socket.send({ tool, action, ...payload ? { payload } : {}, request_id: requestId });
|
|
3987
|
+
}
|
|
3988
|
+
nextRequestId() {
|
|
3989
|
+
return `r${++this.requestSeq}`;
|
|
3990
|
+
}
|
|
3991
|
+
routeFrame(frame) {
|
|
3992
|
+
switch (frame.type) {
|
|
3993
|
+
case "welcome": {
|
|
3994
|
+
const payload = frame.payload;
|
|
3995
|
+
this._welcome = payload;
|
|
3996
|
+
this.welcomeWaiter?.resolve(payload);
|
|
3997
|
+
this.welcomeWaiter = null;
|
|
3998
|
+
return;
|
|
3999
|
+
}
|
|
4000
|
+
case "registered":
|
|
4001
|
+
if (this.pendingAuth)
|
|
4002
|
+
this.pendingAuth.registered = frame.payload;
|
|
4003
|
+
return;
|
|
4004
|
+
case "logged_in": {
|
|
4005
|
+
const payload = frame.payload;
|
|
4006
|
+
const auth = this.pendingAuth;
|
|
4007
|
+
if (auth) {
|
|
4008
|
+
this.pendingAuth = null;
|
|
4009
|
+
this._authenticated = true;
|
|
4010
|
+
auth.onLoggedIn(payload, auth.registered);
|
|
4011
|
+
} else {
|
|
4012
|
+
this.emitter.emit(frame);
|
|
4013
|
+
}
|
|
4014
|
+
return;
|
|
4015
|
+
}
|
|
4016
|
+
case "action_result": {
|
|
4017
|
+
const delta = frame.payload.result;
|
|
4018
|
+
if (delta) {
|
|
4019
|
+
const changed = this.cache.applyDelta(delta);
|
|
4020
|
+
if (changed.includes("location"))
|
|
4021
|
+
this.checkSubscriptionsAgainstLocation();
|
|
4022
|
+
if (changed.length) {
|
|
4023
|
+
try {
|
|
4024
|
+
this.stateListener?.(changed);
|
|
4025
|
+
} catch (err) {
|
|
4026
|
+
console.warn(`[spacemolt] onStateChange listener threw: ${err}`);
|
|
4027
|
+
}
|
|
4028
|
+
}
|
|
4029
|
+
}
|
|
4030
|
+
if (!this.correlator.handle(frame)) {
|
|
4031
|
+
console.warn(`[spacemolt] action_result for ${frame.request_id ?? "?"} arrived with no matching pending mutation (already timed out?)`);
|
|
4032
|
+
this.emitter.emit(frame);
|
|
4033
|
+
}
|
|
4034
|
+
return;
|
|
4035
|
+
}
|
|
4036
|
+
case "result":
|
|
4037
|
+
case "action_error":
|
|
4038
|
+
if (!this.correlator.handle(frame))
|
|
4039
|
+
this.emitter.emit(frame);
|
|
4040
|
+
return;
|
|
4041
|
+
case "error": {
|
|
4042
|
+
if (this.correlator.handle(frame))
|
|
4043
|
+
return;
|
|
4044
|
+
const auth = this.pendingAuth;
|
|
4045
|
+
if (auth) {
|
|
4046
|
+
this.pendingAuth = null;
|
|
4047
|
+
auth.onError(errorFromFrame(frame));
|
|
4048
|
+
return;
|
|
4049
|
+
}
|
|
4050
|
+
this.emitter.emit(frame);
|
|
4051
|
+
return;
|
|
4052
|
+
}
|
|
4053
|
+
default:
|
|
4054
|
+
this.emitter.emit(frame);
|
|
4055
|
+
}
|
|
4056
|
+
}
|
|
4057
|
+
handleClose(err) {
|
|
4058
|
+
this._authenticated = false;
|
|
4059
|
+
this.correlator.rejectAll(err);
|
|
4060
|
+
this.emitter.closeStreams();
|
|
4061
|
+
if (this.welcomeWaiter) {
|
|
4062
|
+
const waiter = this.welcomeWaiter;
|
|
4063
|
+
this.welcomeWaiter = null;
|
|
4064
|
+
waiter.reject(err);
|
|
4065
|
+
}
|
|
4066
|
+
if (this.pendingAuth) {
|
|
4067
|
+
const auth = this.pendingAuth;
|
|
4068
|
+
this.pendingAuth = null;
|
|
4069
|
+
auth.onError(err);
|
|
4070
|
+
}
|
|
4071
|
+
if (this.shouldReconnect(err)) {
|
|
4072
|
+
this.reconnectLoop(err);
|
|
4073
|
+
} else if (!this.userClosing) {
|
|
4074
|
+
this.disconnectedListener?.(err);
|
|
4075
|
+
}
|
|
4076
|
+
}
|
|
4077
|
+
shouldReconnect(err) {
|
|
4078
|
+
if (!this.reconnectConfig || this.userClosing || this.reconnecting)
|
|
4079
|
+
return false;
|
|
4080
|
+
if (!this.credentialsProvider)
|
|
4081
|
+
return false;
|
|
4082
|
+
if (err.code === CLOSE_CODE.SESSION_REPLACED || err.code === CLOSE_CODE.AUTH_TIMEOUT)
|
|
4083
|
+
return false;
|
|
4084
|
+
return true;
|
|
4085
|
+
}
|
|
4086
|
+
async reconnectLoop(err) {
|
|
4087
|
+
if (this.reconnecting || !this.reconnectConfig || !this.credentialsProvider)
|
|
4088
|
+
return;
|
|
4089
|
+
this.reconnecting = true;
|
|
4090
|
+
const cfg = this.reconnectConfig;
|
|
4091
|
+
const retryAfterMs2 = retryAfterMsFromClose(err);
|
|
4092
|
+
for (let attempt = 1;attempt <= cfg.maxRetries; attempt++) {
|
|
4093
|
+
this.reconnectingListener?.(attempt);
|
|
4094
|
+
const backoff = attempt === 1 && retryAfterMs2 !== undefined ? retryAfterMs2 : Math.min(cfg.baseDelayMs * 2 ** (attempt - 1), cfg.maxDelayMs);
|
|
4095
|
+
await delay(backoff);
|
|
4096
|
+
if (this.userClosing) {
|
|
4097
|
+
this.reconnecting = false;
|
|
4098
|
+
return;
|
|
4099
|
+
}
|
|
4100
|
+
try {
|
|
4101
|
+
await this.reconnectOnce();
|
|
4102
|
+
this.reconnecting = false;
|
|
4103
|
+
this.reconnectedListener?.();
|
|
4104
|
+
return;
|
|
4105
|
+
} catch {}
|
|
4106
|
+
}
|
|
4107
|
+
this.reconnecting = false;
|
|
4108
|
+
this.disconnectedListener?.(err);
|
|
4109
|
+
}
|
|
4110
|
+
async reconnectOnce() {
|
|
4111
|
+
if (!this.credentialsProvider)
|
|
4112
|
+
throw new Error("reconnectOnce requires credentials");
|
|
4113
|
+
this.socket.close();
|
|
4114
|
+
this.makeSocket();
|
|
4115
|
+
await this.open();
|
|
4116
|
+
await this.authenticate(await this.credentialsProvider());
|
|
4117
|
+
await this.resubscribe();
|
|
4118
|
+
}
|
|
4119
|
+
async resubscribe() {
|
|
4120
|
+
if (this.marketSubscribedState) {
|
|
4121
|
+
try {
|
|
4122
|
+
await this.subscribeMarket();
|
|
4123
|
+
} catch {}
|
|
4124
|
+
}
|
|
4125
|
+
if (this.observationSubscribedState) {
|
|
4126
|
+
try {
|
|
4127
|
+
await this.subscribeObservation(this.observationActiveScanState);
|
|
4128
|
+
} catch {}
|
|
4129
|
+
}
|
|
4130
|
+
}
|
|
4131
|
+
async withRateLimitRetry(fn) {
|
|
4132
|
+
for (let attempt = 0;; attempt++) {
|
|
4133
|
+
try {
|
|
4134
|
+
return await fn();
|
|
4135
|
+
} catch (err) {
|
|
4136
|
+
if (err instanceof SpacemoltError && err.code === "rate_limited" && attempt < this.maxRateLimitRetries) {
|
|
4137
|
+
await delay(retryAfterMs(err));
|
|
4138
|
+
continue;
|
|
4139
|
+
}
|
|
4140
|
+
throw err;
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
}
|
|
4144
|
+
enqueueMutation(task) {
|
|
4145
|
+
const run = this.mutationLane.then(task, task);
|
|
4146
|
+
this.mutationLane = run.then(() => {
|
|
4147
|
+
return;
|
|
4148
|
+
}, () => {
|
|
4149
|
+
return;
|
|
4150
|
+
});
|
|
4151
|
+
return run;
|
|
4152
|
+
}
|
|
4153
|
+
}
|
|
4154
|
+
// src/auth/credentials.ts
|
|
4155
|
+
class MemoryCredentialStore {
|
|
4156
|
+
accounts = new Map;
|
|
4157
|
+
list() {
|
|
4158
|
+
return Promise.resolve([...this.accounts.values()]);
|
|
4159
|
+
}
|
|
4160
|
+
get(id) {
|
|
4161
|
+
return Promise.resolve(this.accounts.get(id));
|
|
4162
|
+
}
|
|
4163
|
+
put(account) {
|
|
4164
|
+
this.accounts.set(account.id, account);
|
|
4165
|
+
return Promise.resolve();
|
|
4166
|
+
}
|
|
4167
|
+
remove(id) {
|
|
4168
|
+
this.accounts.delete(id);
|
|
4169
|
+
return Promise.resolve();
|
|
4170
|
+
}
|
|
4171
|
+
}
|
|
4172
|
+
|
|
4173
|
+
// src/data/catalog.ts
|
|
4174
|
+
async function fetchCatalog(httpBaseUrl) {
|
|
4175
|
+
const url = `${httpBaseUrl.replace(/\/$/, "")}/api/catalog.json`;
|
|
4176
|
+
const res = await fetch(url, { headers: { accept: "application/json" } });
|
|
4177
|
+
if (!res.ok)
|
|
4178
|
+
throw new Error(`GET ${url} -> ${res.status} ${res.statusText}`);
|
|
4179
|
+
const data = await res.json();
|
|
4180
|
+
return {
|
|
4181
|
+
version: data.version,
|
|
4182
|
+
ships: data.ships ?? [],
|
|
4183
|
+
items: data.items ?? [],
|
|
4184
|
+
recipes: data.recipes ?? [],
|
|
4185
|
+
skills: data.skills ?? [],
|
|
4186
|
+
facilities: data.facilities ?? []
|
|
4187
|
+
};
|
|
4188
|
+
}
|
|
4189
|
+
|
|
4190
|
+
class CatalogCache {
|
|
4191
|
+
catalog;
|
|
4192
|
+
indexes;
|
|
4193
|
+
constructor(catalog) {
|
|
4194
|
+
this.catalog = catalog;
|
|
4195
|
+
this.indexes = {
|
|
4196
|
+
ships: index(catalog.ships),
|
|
4197
|
+
items: index(catalog.items),
|
|
4198
|
+
recipes: index(catalog.recipes),
|
|
4199
|
+
skills: index(catalog.skills),
|
|
4200
|
+
facilities: index(catalog.facilities)
|
|
4201
|
+
};
|
|
4202
|
+
}
|
|
4203
|
+
static async load(httpBaseUrl) {
|
|
4204
|
+
return new CatalogCache(await fetchCatalog(httpBaseUrl));
|
|
4205
|
+
}
|
|
4206
|
+
get version() {
|
|
4207
|
+
return this.catalog.version;
|
|
4208
|
+
}
|
|
4209
|
+
ship(id) {
|
|
4210
|
+
return this.indexes.ships.get(id);
|
|
4211
|
+
}
|
|
4212
|
+
item(id) {
|
|
4213
|
+
return this.indexes.items.get(id);
|
|
4214
|
+
}
|
|
4215
|
+
recipe(id) {
|
|
4216
|
+
return this.indexes.recipes.get(id);
|
|
4217
|
+
}
|
|
4218
|
+
skill(id) {
|
|
4219
|
+
return this.indexes.skills.get(id);
|
|
4220
|
+
}
|
|
4221
|
+
facility(id) {
|
|
4222
|
+
return this.indexes.facilities.get(id);
|
|
4223
|
+
}
|
|
4224
|
+
get ships() {
|
|
4225
|
+
return this.catalog.ships;
|
|
4226
|
+
}
|
|
4227
|
+
get items() {
|
|
4228
|
+
return this.catalog.items;
|
|
4229
|
+
}
|
|
4230
|
+
get recipes() {
|
|
4231
|
+
return this.catalog.recipes;
|
|
4232
|
+
}
|
|
4233
|
+
get skills() {
|
|
4234
|
+
return this.catalog.skills;
|
|
4235
|
+
}
|
|
4236
|
+
get facilities() {
|
|
4237
|
+
return this.catalog.facilities;
|
|
4238
|
+
}
|
|
4239
|
+
}
|
|
4240
|
+
function index(entries) {
|
|
4241
|
+
const map = new Map;
|
|
4242
|
+
for (const entry of entries) {
|
|
4243
|
+
const id = typeof entry.id === "string" ? entry.id : undefined;
|
|
4244
|
+
if (id)
|
|
4245
|
+
map.set(id, entry);
|
|
4246
|
+
}
|
|
4247
|
+
return map;
|
|
4248
|
+
}
|
|
4249
|
+
|
|
4250
|
+
// src/data/map.ts
|
|
4251
|
+
async function fetchMap(httpBaseUrl) {
|
|
4252
|
+
const url = `${httpBaseUrl.replace(/\/$/, "")}/api/map`;
|
|
4253
|
+
const res = await fetch(url, { headers: { accept: "application/json" } });
|
|
4254
|
+
if (!res.ok)
|
|
4255
|
+
throw new Error(`GET ${url} -> ${res.status} ${res.statusText}`);
|
|
4256
|
+
const data = await res.json();
|
|
4257
|
+
return { systems: data.systems ?? [], empires: data.empires ?? {} };
|
|
4258
|
+
}
|
|
4259
|
+
|
|
4260
|
+
class MapCache {
|
|
4261
|
+
map;
|
|
4262
|
+
bySystemId;
|
|
4263
|
+
constructor(map) {
|
|
4264
|
+
this.map = map;
|
|
4265
|
+
this.bySystemId = new Map;
|
|
4266
|
+
for (const system of map.systems) {
|
|
4267
|
+
const id = typeof system.id === "string" ? system.id : undefined;
|
|
4268
|
+
if (id)
|
|
4269
|
+
this.bySystemId.set(id, system);
|
|
4270
|
+
}
|
|
4271
|
+
}
|
|
4272
|
+
static async load(httpBaseUrl) {
|
|
4273
|
+
return new MapCache(await fetchMap(httpBaseUrl));
|
|
4274
|
+
}
|
|
4275
|
+
system(id) {
|
|
4276
|
+
return this.bySystemId.get(id);
|
|
4277
|
+
}
|
|
4278
|
+
get systems() {
|
|
4279
|
+
return this.map.systems;
|
|
4280
|
+
}
|
|
4281
|
+
get empires() {
|
|
4282
|
+
return this.map.empires;
|
|
4283
|
+
}
|
|
4284
|
+
}
|
|
4285
|
+
function httpBaseFromWs(wsUrl) {
|
|
4286
|
+
try {
|
|
4287
|
+
const u = new URL(wsUrl);
|
|
4288
|
+
const protocol = u.protocol === "wss:" ? "https:" : u.protocol === "ws:" ? "http:" : u.protocol;
|
|
4289
|
+
return `${protocol}//${u.host}`;
|
|
4290
|
+
} catch {
|
|
4291
|
+
return wsUrl;
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
|
|
4295
|
+
// src/client.ts
|
|
4296
|
+
var DEFAULT_HTTP_BASE = "https://game.spacemolt.com";
|
|
4297
|
+
var DEFAULT_CONNECT_RETRY = { maxRetries: 8, baseDelayMs: 2000, maxDelayMs: 60000 };
|
|
4298
|
+
var CONNECT_RATE_WINDOW_MS = 60000;
|
|
4299
|
+
var delay2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
4300
|
+
|
|
4301
|
+
class SpacemoltClient {
|
|
4302
|
+
opts;
|
|
4303
|
+
store;
|
|
4304
|
+
connected = new Map;
|
|
4305
|
+
catalogCache;
|
|
4306
|
+
mapCache;
|
|
4307
|
+
clerkSource;
|
|
4308
|
+
accountConnectedListeners = new Set;
|
|
4309
|
+
accountReconnectedListeners = new Set;
|
|
4310
|
+
accountDisconnectedListeners = new Set;
|
|
4311
|
+
rateLimitedQueue = [];
|
|
4312
|
+
rateLimitedQueueDraining = false;
|
|
4313
|
+
reconnectingIds = new Set;
|
|
4314
|
+
constructor(opts = {}) {
|
|
4315
|
+
this.opts = opts;
|
|
4316
|
+
this.store = opts.store ?? new MemoryCredentialStore;
|
|
4317
|
+
}
|
|
4318
|
+
onAccountConnected(listener) {
|
|
4319
|
+
this.accountConnectedListeners.add(listener);
|
|
4320
|
+
return () => this.accountConnectedListeners.delete(listener);
|
|
4321
|
+
}
|
|
4322
|
+
onAccountReconnected(listener) {
|
|
4323
|
+
this.accountReconnectedListeners.add(listener);
|
|
4324
|
+
return () => this.accountReconnectedListeners.delete(listener);
|
|
4325
|
+
}
|
|
4326
|
+
onAccountDisconnected(listener) {
|
|
4327
|
+
this.accountDisconnectedListeners.add(listener);
|
|
4328
|
+
return () => this.accountDisconnectedListeners.delete(listener);
|
|
4329
|
+
}
|
|
4330
|
+
notifyAccountConnected(account) {
|
|
4331
|
+
for (const listener of this.accountConnectedListeners)
|
|
4332
|
+
listener(account);
|
|
4333
|
+
}
|
|
4334
|
+
notifyAccountReconnected(account) {
|
|
4335
|
+
for (const listener of this.accountReconnectedListeners)
|
|
4336
|
+
listener(account);
|
|
4337
|
+
}
|
|
4338
|
+
notifyAccountDisconnected(id, err) {
|
|
4339
|
+
for (const listener of this.accountDisconnectedListeners)
|
|
4340
|
+
listener(id, err);
|
|
4341
|
+
}
|
|
4342
|
+
get httpBaseUrl() {
|
|
4343
|
+
return this.opts.httpBaseUrl ?? (this.opts.url ? httpBaseFromWs(this.opts.url) : DEFAULT_HTTP_BASE);
|
|
4344
|
+
}
|
|
4345
|
+
async catalog(force = false) {
|
|
4346
|
+
if (force || !this.catalogCache)
|
|
4347
|
+
this.catalogCache = await CatalogCache.load(this.httpBaseUrl);
|
|
4348
|
+
return this.catalogCache;
|
|
4349
|
+
}
|
|
4350
|
+
async map(force = false) {
|
|
4351
|
+
if (force || !this.mapCache)
|
|
4352
|
+
this.mapCache = await MapCache.load(this.httpBaseUrl);
|
|
4353
|
+
return this.mapCache;
|
|
4354
|
+
}
|
|
4355
|
+
get credentialStore() {
|
|
4356
|
+
return this.store;
|
|
4357
|
+
}
|
|
4358
|
+
async addLogin(username, password) {
|
|
4359
|
+
await this.store.put({ id: username, credentials: { kind: "login", username, password } });
|
|
4360
|
+
return username;
|
|
4361
|
+
}
|
|
4362
|
+
async addToken(id, token) {
|
|
4363
|
+
await this.store.put({ id, credentials: { kind: "login_token", token } });
|
|
4364
|
+
return id;
|
|
4365
|
+
}
|
|
4366
|
+
async register(params) {
|
|
4367
|
+
const account = this.createAccount(params.username);
|
|
4368
|
+
this.connected.set(params.username, account);
|
|
4369
|
+
await account.connect();
|
|
4370
|
+
const result = await account.register(params);
|
|
4371
|
+
await this.store.put({
|
|
4372
|
+
id: params.username,
|
|
4373
|
+
credentials: { kind: "login", username: params.username, password: result.password },
|
|
4374
|
+
playerId: result.player_id
|
|
4375
|
+
});
|
|
4376
|
+
account.onDisconnected((err) => this.handleAccountDisconnected(params.username, account, err));
|
|
4377
|
+
this.notifyAccountConnected(account);
|
|
4378
|
+
return { account, result };
|
|
4379
|
+
}
|
|
4380
|
+
connectRetryConfig() {
|
|
4381
|
+
const retryOpt = this.opts.connectRetry ?? true;
|
|
4382
|
+
if (retryOpt === false)
|
|
4383
|
+
return { maxRetries: 0, baseDelayMs: 0, maxDelayMs: 0 };
|
|
4384
|
+
return { ...DEFAULT_CONNECT_RETRY, ...retryOpt === true ? {} : retryOpt };
|
|
4385
|
+
}
|
|
4386
|
+
async withConnectRetry(attempt) {
|
|
4387
|
+
const retry = this.connectRetryConfig();
|
|
4388
|
+
let lastErr;
|
|
4389
|
+
for (let i = 0;i <= retry.maxRetries; i++) {
|
|
4390
|
+
try {
|
|
4391
|
+
return await attempt();
|
|
4392
|
+
} catch (err) {
|
|
4393
|
+
lastErr = err;
|
|
4394
|
+
if (i < retry.maxRetries) {
|
|
4395
|
+
const retryAfterMs2 = retryAfterMsFromClose(err);
|
|
4396
|
+
await delay2(retryAfterMs2 ?? Math.min(retry.baseDelayMs * 2 ** i, retry.maxDelayMs));
|
|
4397
|
+
}
|
|
4398
|
+
}
|
|
4399
|
+
}
|
|
4400
|
+
throw lastErr;
|
|
4401
|
+
}
|
|
4402
|
+
async connect(id) {
|
|
4403
|
+
const existing = this.connected.get(id);
|
|
4404
|
+
if (existing)
|
|
4405
|
+
return existing;
|
|
4406
|
+
const stored = await this.store.get(id);
|
|
4407
|
+
if (!stored)
|
|
4408
|
+
throw new Error(`no stored credentials for account "${id}"`);
|
|
4409
|
+
return this.withConnectRetry(async () => {
|
|
4410
|
+
const account = this.createAccount(id);
|
|
4411
|
+
this.connected.set(id, account);
|
|
4412
|
+
try {
|
|
4413
|
+
await account.connect();
|
|
4414
|
+
await account.authenticate(stored.credentials);
|
|
4415
|
+
await this.capturePlayerId(stored);
|
|
4416
|
+
account.onDisconnected((err) => this.handleAccountDisconnected(id, account, err));
|
|
4417
|
+
this.notifyAccountConnected(account);
|
|
4418
|
+
return account;
|
|
4419
|
+
} catch (err) {
|
|
4420
|
+
this.connected.delete(id);
|
|
4421
|
+
account.close();
|
|
4422
|
+
throw err;
|
|
4423
|
+
}
|
|
4424
|
+
});
|
|
4425
|
+
}
|
|
4426
|
+
async connectAll(opts = {}) {
|
|
4427
|
+
const stored = await this.store.list();
|
|
4428
|
+
return this.connectIds(stored.map((s) => s.id), opts.onConnect);
|
|
4429
|
+
}
|
|
4430
|
+
async listOwnedPlayers() {
|
|
4431
|
+
return this.requireClerkSource().listPlayers();
|
|
4432
|
+
}
|
|
4433
|
+
async connectOwned(opts = {}) {
|
|
4434
|
+
const source = this.requireClerkSource();
|
|
4435
|
+
const players = await source.listPlayers();
|
|
4436
|
+
const selected = opts.filter ? players.filter(opts.filter) : players;
|
|
4437
|
+
const ids = [];
|
|
4438
|
+
for (const player of selected) {
|
|
4439
|
+
await this.store.put({
|
|
4440
|
+
id: player.username,
|
|
4441
|
+
credentials: {
|
|
4442
|
+
kind: "clerk",
|
|
4443
|
+
apiKey: source.apiKey,
|
|
4444
|
+
playerId: player.id,
|
|
4445
|
+
httpBaseUrl: source.httpBaseUrl
|
|
4446
|
+
},
|
|
4447
|
+
playerId: player.id
|
|
4448
|
+
});
|
|
4449
|
+
ids.push(player.username);
|
|
4450
|
+
}
|
|
4451
|
+
return this.connectIds(ids, opts.onConnect);
|
|
4452
|
+
}
|
|
4453
|
+
requireClerkSource() {
|
|
4454
|
+
if (!this.clerkSource) {
|
|
4455
|
+
if (!this.opts.clerkApiKey) {
|
|
4456
|
+
throw new Error("connectOwned/listOwnedPlayers require `clerkApiKey` in SpacemoltClientOptions");
|
|
4457
|
+
}
|
|
4458
|
+
this.clerkSource = new ClerkSource({
|
|
4459
|
+
apiKey: this.opts.clerkApiKey,
|
|
4460
|
+
httpBaseUrl: this.httpBaseUrl,
|
|
4461
|
+
fetchImpl: this.opts.fetchImpl
|
|
4462
|
+
});
|
|
4463
|
+
}
|
|
4464
|
+
return this.clerkSource;
|
|
4465
|
+
}
|
|
4466
|
+
async connectIds(ids, onConnect) {
|
|
4467
|
+
const accounts = [];
|
|
4468
|
+
await Promise.all(ids.map((id) => this.enqueueRateLimited(() => this.connect(id)).then((account) => {
|
|
4469
|
+
accounts.push(account);
|
|
4470
|
+
onConnect?.(account);
|
|
4471
|
+
}).catch((err) => {
|
|
4472
|
+
console.warn(`[spacemolt] failed to connect "${id}": ${err}`);
|
|
4473
|
+
})));
|
|
4474
|
+
return accounts;
|
|
4475
|
+
}
|
|
4476
|
+
enqueueRateLimited(task) {
|
|
4477
|
+
return new Promise((resolve, reject) => {
|
|
4478
|
+
this.rateLimitedQueue.push(async () => {
|
|
4479
|
+
try {
|
|
4480
|
+
resolve(await task());
|
|
4481
|
+
} catch (err) {
|
|
4482
|
+
reject(err);
|
|
4483
|
+
}
|
|
4484
|
+
});
|
|
4485
|
+
this.drainRateLimitedQueue();
|
|
4486
|
+
});
|
|
4487
|
+
}
|
|
4488
|
+
async drainRateLimitedQueue() {
|
|
4489
|
+
if (this.rateLimitedQueueDraining)
|
|
4490
|
+
return;
|
|
4491
|
+
this.rateLimitedQueueDraining = true;
|
|
4492
|
+
const batchSize = this.opts.connectBatchSize ?? 100;
|
|
4493
|
+
const stagger = this.opts.connectStaggerMs ?? (batchSize > 0 ? Math.ceil(CONNECT_RATE_WINDOW_MS / batchSize) : 250);
|
|
4494
|
+
const batchWaitMs = this.opts.connectBatchWaitMs ?? 65000;
|
|
4495
|
+
let ranInCurrentBatch = 0;
|
|
4496
|
+
let isFirst = true;
|
|
4497
|
+
while (this.rateLimitedQueue.length > 0) {
|
|
4498
|
+
if (!isFirst) {
|
|
4499
|
+
if (batchSize > 0 && ranInCurrentBatch >= batchSize) {
|
|
4500
|
+
await delay2(batchWaitMs);
|
|
4501
|
+
ranInCurrentBatch = 0;
|
|
4502
|
+
} else if (stagger > 0) {
|
|
4503
|
+
await delay2(stagger);
|
|
4504
|
+
}
|
|
4505
|
+
}
|
|
4506
|
+
isFirst = false;
|
|
4507
|
+
const task = this.rateLimitedQueue.shift();
|
|
4508
|
+
ranInCurrentBatch++;
|
|
4509
|
+
await task();
|
|
4510
|
+
}
|
|
4511
|
+
this.rateLimitedQueueDraining = false;
|
|
4512
|
+
}
|
|
4513
|
+
account(id) {
|
|
4514
|
+
return this.connected.get(id);
|
|
4515
|
+
}
|
|
4516
|
+
accounts() {
|
|
4517
|
+
return [...this.connected.values()];
|
|
4518
|
+
}
|
|
4519
|
+
ids() {
|
|
4520
|
+
return [...this.connected.keys()];
|
|
4521
|
+
}
|
|
4522
|
+
async remove(id) {
|
|
4523
|
+
this.connected.get(id)?.close();
|
|
4524
|
+
this.connected.delete(id);
|
|
4525
|
+
await this.store.remove(id);
|
|
4526
|
+
}
|
|
4527
|
+
closeAll() {
|
|
4528
|
+
for (const account of this.connected.values())
|
|
4529
|
+
account.close();
|
|
4530
|
+
this.connected.clear();
|
|
4531
|
+
}
|
|
4532
|
+
handleAccountDisconnected(id, account, err) {
|
|
4533
|
+
if (this.connected.get(id) !== account || this.reconnectingIds.has(id)) {
|
|
4534
|
+
return;
|
|
4535
|
+
}
|
|
4536
|
+
const terminal = err.code === CLOSE_CODE.SESSION_REPLACED || err.code === CLOSE_CODE.AUTH_TIMEOUT;
|
|
4537
|
+
if (terminal || this.opts.reconnect === false) {
|
|
4538
|
+
this.connected.delete(id);
|
|
4539
|
+
this.notifyAccountDisconnected(id, err);
|
|
4540
|
+
return;
|
|
4541
|
+
}
|
|
4542
|
+
this.reconnectingIds.add(id);
|
|
4543
|
+
this.enqueueRateLimited(() => this.withConnectRetry(() => account.reconnectOnce())).then(() => this.notifyAccountReconnected(account)).catch((reconnectErr) => {
|
|
4544
|
+
console.warn(`[spacemolt] failed to reconnect "${id}": ${reconnectErr}`);
|
|
4545
|
+
this.connected.delete(id);
|
|
4546
|
+
this.notifyAccountDisconnected(id, err);
|
|
4547
|
+
}).finally(() => this.reconnectingIds.delete(id));
|
|
4548
|
+
}
|
|
4549
|
+
createAccount(id) {
|
|
4550
|
+
const account = new Account({
|
|
4551
|
+
id,
|
|
4552
|
+
url: this.opts.url,
|
|
4553
|
+
webSocketFactory: this.opts.webSocketFactory,
|
|
4554
|
+
seedState: this.opts.seedState,
|
|
4555
|
+
reconnect: false,
|
|
4556
|
+
fetchImpl: this.opts.fetchImpl,
|
|
4557
|
+
connectTimeoutMs: this.opts.connectTimeoutMs,
|
|
4558
|
+
queryTimeoutMs: this.opts.queryTimeoutMs,
|
|
4559
|
+
mutationTimeoutMs: this.opts.mutationTimeoutMs,
|
|
4560
|
+
credentials: async () => {
|
|
4561
|
+
const stored = await this.store.get(id);
|
|
4562
|
+
if (!stored)
|
|
4563
|
+
throw new Error(`no stored credentials for account "${id}"`);
|
|
4564
|
+
return stored.credentials;
|
|
4565
|
+
}
|
|
4566
|
+
});
|
|
4567
|
+
return account;
|
|
4568
|
+
}
|
|
4569
|
+
async capturePlayerId(stored) {
|
|
4570
|
+
const playerId = this.connected.get(stored.id)?.player?.id;
|
|
4571
|
+
if (playerId && playerId !== stored.playerId) {
|
|
4572
|
+
await this.store.put({ ...stored, playerId });
|
|
4573
|
+
}
|
|
4574
|
+
}
|
|
4575
|
+
}
|
|
4576
|
+
// src/generated/notifications.gen.ts
|
|
4577
|
+
var TYPED_NOTIFICATION_TYPES = [
|
|
4578
|
+
"achievement_unlocked",
|
|
4579
|
+
"base_destroyed",
|
|
4580
|
+
"base_raid_update",
|
|
4581
|
+
"battle_alert",
|
|
4582
|
+
"battle_damage",
|
|
4583
|
+
"battle_ended",
|
|
4584
|
+
"battle_joined",
|
|
4585
|
+
"battle_left",
|
|
4586
|
+
"battle_started",
|
|
4587
|
+
"battle_update",
|
|
4588
|
+
"chat_message",
|
|
4589
|
+
"crafting_update",
|
|
4590
|
+
"drone_destroyed",
|
|
4591
|
+
"drone_scan",
|
|
4592
|
+
"drone_survey",
|
|
4593
|
+
"drone_update",
|
|
4594
|
+
"facility_reclaimed",
|
|
4595
|
+
"facility_rent_warning",
|
|
4596
|
+
"market_update",
|
|
4597
|
+
"mining_yield",
|
|
4598
|
+
"observation_update",
|
|
4599
|
+
"pilotless_ship",
|
|
4600
|
+
"pirate_destroyed",
|
|
4601
|
+
"pirate_radio",
|
|
4602
|
+
"player_died",
|
|
4603
|
+
"player_kill",
|
|
4604
|
+
"reconnected",
|
|
4605
|
+
"scan_detected",
|
|
4606
|
+
"skill_level_up",
|
|
4607
|
+
"trade_cancelled",
|
|
4608
|
+
"trade_complete",
|
|
4609
|
+
"trade_declined",
|
|
4610
|
+
"trade_offer_received"
|
|
4611
|
+
];
|
|
4612
|
+
export {
|
|
4613
|
+
retryAfterMsFromClose,
|
|
4614
|
+
mintWsToken,
|
|
4615
|
+
httpBaseFromWs,
|
|
4616
|
+
fetchMap,
|
|
4617
|
+
fetchCatalog,
|
|
4618
|
+
buildCommands,
|
|
4619
|
+
TypedEmitter,
|
|
4620
|
+
TYPED_NOTIFICATION_TYPES,
|
|
4621
|
+
StateCache,
|
|
4622
|
+
SpacemoltError,
|
|
4623
|
+
SpacemoltClient,
|
|
4624
|
+
Socket,
|
|
4625
|
+
STATE_SECTIONS,
|
|
4626
|
+
ObservationCache,
|
|
4627
|
+
MemoryCredentialStore,
|
|
4628
|
+
MarketCache,
|
|
4629
|
+
MapCache,
|
|
4630
|
+
GENERATED_SPEC_VERSION,
|
|
4631
|
+
EventStream,
|
|
4632
|
+
ConnectionClosedError,
|
|
4633
|
+
ClerkSource,
|
|
4634
|
+
CatalogCache,
|
|
4635
|
+
CLOSE_CODE,
|
|
4636
|
+
Account,
|
|
4637
|
+
ACTIONS
|
|
4638
|
+
};
|