@spacemolt/lib 14.2.1 → 15.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -78,7 +78,7 @@ var STATE_SECTIONS = [
78
78
  "prize_recoveries"
79
79
  ];
80
80
  // src/generated/actions.gen.ts
81
- var GENERATED_SPEC_VERSION = "v0.593.0";
81
+ var GENERATED_SPEC_VERSION = "v0.609.4";
82
82
  var ACTIONS = {
83
83
  "spacemolt_arena/accept": {
84
84
  tool: "spacemolt_arena",
@@ -246,10 +246,11 @@ var ACTIONS = {
246
246
  action: "reload",
247
247
  kind: "mutation",
248
248
  summary: "Reload a weapon's magazine from ammo in cargo",
249
- detailsType: "ReloadResponse",
249
+ detailsType: "ReloadResponse | BulkReloadResponse",
250
250
  params: [
251
- { 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 },
252
- { 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 }
251
+ { name: "id", type: "string", required: false, description: "Instance ID of the fitted weapon to reload (use get_ship to see weapon instance IDs)", positional: 0 },
252
+ { 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 },
253
+ { name: "weapons", type: "string[]", required: false, description: "Bulk reload: array of {weapon_instance_id, ammo_item_id?} entries loaded in a single action for one tick, however many weapons. Omit weapon_instance_id/ammo_item_id when using this. Entries are independent — the response reports per-weapon success/failure. Maximum 50 entries." }
253
254
  ]
254
255
  },
255
256
  "spacemolt_battle/retreat": {
@@ -506,7 +507,7 @@ var ACTIONS = {
506
507
  summary: "License a specific ship design so your faction can build it at its own stations",
507
508
  detailsType: "ShipLicenseResponse",
508
509
  params: [
509
- { name: "ship_class", type: "string", required: true, description: "Ship class id to license (from ship_catalog), e.g. solarian_frigate" }
510
+ { name: "ship_class", type: "string", required: true, description: "Ship class id to license (from catalog type=ships), e.g. solarian_frigate" }
510
511
  ]
511
512
  },
512
513
  "spacemolt_facility/cancel_listing": {
@@ -1025,8 +1026,8 @@ var ACTIONS = {
1025
1026
  { name: "expiration_hours", type: "number", required: false, description: "Hours before mission expires (default 72, max 720)" },
1026
1027
  { name: "giver_name", type: "string", required: false, description: "Optional: NPC name who gives the mission" },
1027
1028
  { name: "giver_title", type: "string", required: false, description: "Optional: NPC title" },
1028
- { name: "objectives", type: "{ description: string; item_id?: string; quantity?: number; system_id?: string; target_base_id?: string; target_id?: string; type: string }[]", required: true, description: "List of mission objectives" },
1029
- { name: "rewards", type: "{ credits?: number; items?: Record<string, unknown>[] }", required: true, description: "Mission rewards (credits, items, reputation)" },
1029
+ { name: "objectives", type: "{ description: string; item_id?: string; pirate_tier?: string; quantity?: number; system_id?: string; target_base_id?: string; target_id?: string; type: string }[]", required: true, description: "List of mission objectives" },
1030
+ { name: "rewards", type: "{ credits?: number; items?: Record<string, unknown> }", required: true, description: "Mission rewards (credits, items, reputation)" },
1030
1031
  { name: "title", type: "string", required: true, description: "Mission title" },
1031
1032
  { name: "triggers", type: "string[]", required: false, description: "Optional: triggers like 'open_to_all' to allow non-members" },
1032
1033
  { name: "type", type: "string", required: true, description: "Mission type (delivery, combat, exploration, bounty, etc.)" }
@@ -1511,7 +1512,7 @@ var ACTIONS = {
1511
1512
  summary: "Submit system intel to your faction's shared map",
1512
1513
  detailsType: "FactionSubmitIntelResponse",
1513
1514
  params: [
1514
- { 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}]})" }
1515
+ { 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, deep_core, resources:[{resource_id, richness, remaining, max_remaining}]}). deep_core marks a hidden deep core POI, where the mining too-sparse cutoff never applies" }
1515
1516
  ]
1516
1517
  },
1517
1518
  "spacemolt_intel/submit_trade_intel": {
@@ -1676,7 +1677,7 @@ var ACTIONS = {
1676
1677
  params: [
1677
1678
  { 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 },
1678
1679
  { 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)." },
1679
- { 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)." },
1680
+ { name: "module_id", type: "string", required: false, description: "Module instance ID to loot into your cargo hold. Get module IDs from get_wrecks; fit it later at a station with install_mod." },
1680
1681
  { name: "quantity", type: "number", required: false, description: "Quantity of cargo item to loot (only used with item_id)" }
1681
1682
  ]
1682
1683
  },
@@ -1728,7 +1729,8 @@ var ACTIONS = {
1728
1729
  detailsType: "PrizeServiceResponse",
1729
1730
  params: [
1730
1731
  { name: "id", type: "string", required: true, description: "Claimed intact prize record ID at your current POI", positional: 0 },
1731
- { name: "quantity", type: "number", required: false, description: "Optional quantity. For refuel, zero or omission transfers the safe maximum; for repair, zero or omission uses one repair kit." },
1732
+ { name: "item_id", type: "string", required: false, description: "Optional repair item to spend on repair. Omit to use the cheapest repair item in your cargo." },
1733
+ { name: "quantity", type: "number", required: false, description: "Optional quantity. For refuel, zero or omission transfers the safe maximum; for repair, zero or omission uses one repair item." },
1732
1734
  { name: "service_action", type: '"stop" | "resume" | "redirect" | "refuel" | "repair"', required: true, description: "Physical recovery action to perform", enumValues: ["stop", "resume", "redirect", "refuel", "repair"] },
1733
1735
  { name: "target", type: "string", required: false, description: "Accessible replacement station for redirect" }
1734
1736
  ]
@@ -1834,7 +1836,7 @@ var ACTIONS = {
1834
1836
  params: [
1835
1837
  { name: "bare_hull", type: "boolean", required: false, description: "If true, commission only the hull without its default module loadout. Defaults to false so ships arrive fitted." },
1836
1838
  { name: "fund_from_faction", type: "boolean", required: false, description: "At your own faction's station: build from faction storage and treasury (requires ManageTreasury). Required there; credits-only and provide_materials are rejected." },
1837
- { name: "id", type: "string", required: true, description: "Ship class ID to commission (use ship_catalog to see options)", positional: 0 },
1839
+ { name: "id", type: "string", required: true, description: "Ship class ID to commission (use catalog type=ships to see options)", positional: 0 },
1838
1840
  { name: "provide_materials", type: "boolean", required: false, description: "At an empire/NPC shipyard: if true, supply build materials from cargo/storage (cheaper); if false, pay credits for everything (default)." },
1839
1841
  { name: "source_missing_materials", type: "boolean", required: false, description: "At an empire/NPC shipyard: take available requirements from cargo then station storage and charge only to source the deficit. Do not combine with provide_materials." }
1840
1842
  ]
@@ -2479,7 +2481,7 @@ var ACTIONS = {
2479
2481
  detailsType: "LootWreckCommandResponse",
2480
2482
  params: [
2481
2483
  { 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 },
2482
- { 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)." },
2484
+ { name: "module_id", type: "string", required: false, description: "Module instance ID to loot into your cargo hold. Get module IDs from get_wrecks; fit it later at a station with install_mod." },
2483
2485
  { name: "quantity", type: "number", required: false, description: "Quantity of cargo item to loot (only used with item_id)", positional: 2 },
2484
2486
  { 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." }
2485
2487
  ]
@@ -2656,7 +2658,7 @@ var ACTIONS = {
2656
2658
  { name: "output_package_label", type: "string", required: false, description: "Bundle the outputs into one package: instead of depositing outputs loose per run, the job holds them and seals them into a single new package with this label in the destination on completion, consuming one cargo_container. Requires an accessible Logistics facility, and the total output size across all runs must fit one package (<= 100). Cancelling refunds the inputs and produces no package." },
2657
2659
  { name: "package_id", type: "string", required: false, description: "For unpack_package: package instance ID to unpack." },
2658
2660
  { name: "package_ids", type: "string[]", required: false, description: "Source this craft's inputs from these packages (raw id or 'package:<id>' form) instead of loose storage items. The packages must all sit in the resolved source location, and their pooled contents must equal the recipe inputs (× quantity) EXACTLY — any shortage or overage is rejected before anything is consumed (no storage/cargo backfill). Their empty cargo_containers are reclaimed only when an accessible Logistics facility is present." },
2659
- { name: "preset", type: '"fast" | "cheap" | "prefer_own" | "workshop"', required: false, description: "Auto-routing preset: 'fast' (fewest ticks, default) picks the best facility globally, so a busy own facility may route to an idle public rental. 'cheap' picks the lowest fee you would actually pay — your own and your faction's facilities are free to you, so they always win. Use 'prefer_own' to keep the job on your own (then faction, then ally-granted) facility and only rent a public one when you have none that can run it. Auto-routing otherwise prefers your own facility, then your faction's, then one an allied faction has granted you access to (free to you, but queued at external priority), 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", "prefer_own", "workshop"] },
2661
+ { name: "preset", type: '"fast" | "cheap" | "prefer_own" | "workshop"', required: false, description: "Auto-routing preset. 'fast' (default) picks the soonest finish across your own, faction, ally-granted, and (for facility-only recipes) public facilities; ownership only breaks ties, so it can pick another player's public facility over your own idle one, and a public route prepays that facility's per-run rental fee. 'cheap' picks the lowest fee you would actually pay — your own and your faction's facilities are free to you, so they always win. 'prefer_own' keeps the job on your own (then faction, then ally-granted) facility and only rents a public one when you have none that can run it. With no facility at all, jobs hand-craft at the Station Workshop; 'workshop' forces hand-crafting even when you have a facility.", enumValues: ["fast", "cheap", "prefer_own", "workshop"] },
2660
2662
  { 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 },
2661
2663
  { 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." },
2662
2664
  { name: "target", type: "string", required: false, description: "For package recipes: output destination (storage, cargo, faction, or faction:<bucket>); defaults to source. deliver_to is accepted as an alias." }
@@ -2893,7 +2895,7 @@ var ACTIONS = {
2893
2895
  tool: "spacemolt",
2894
2896
  action: "get_tax_estimate",
2895
2897
  kind: "query",
2896
- summary: "Preview what taxes you'd owe right now",
2898
+ summary: "See current tax estimates, missed-tax payment guidance, and your latest weekly statement",
2897
2899
  responseType: "TaxEstimateResponse",
2898
2900
  params: []
2899
2901
  },
@@ -3000,7 +3002,7 @@ var ACTIONS = {
3000
3002
  tool: "spacemolt",
3001
3003
  action: "pay_bounty",
3002
3004
  kind: "mutation",
3003
- summary: "Settle your outstanding bounty with an empire from anywhere",
3005
+ summary: "Pay missed taxes and other outstanding bounties with an empire from anywhere",
3004
3006
  detailsType: "PayBountyResponse",
3005
3007
  params: [
3006
3008
  { name: "id", type: "string", required: false, description: "Empire whose bounty to clear: solarian, voidborn, crimson, nebula, outerrim. Omit when you owe exactly one empire.", positional: 0 },
@@ -3031,7 +3033,7 @@ var ACTIONS = {
3031
3033
  { name: "job_id", type: "string", required: false, description: "Act on this queued job instead of recycling. job_id alone cancels it (refunding its unconsumed escrow); job_id together with deliver_to instead redirects the job's REMAINING output to that destination, keeping everything else about the job unchanged." },
3032
3034
  { 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." },
3033
3035
  { name: "jobs", type: "Record<string, unknown>[]", required: false, description: "Bulk mode: recycle many recipes 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 processed independently (partial success). Max 50." },
3034
- { name: "preset", type: '"fast" | "cheap" | "prefer_own"', required: false, description: "Auto-routing preset: 'fast' (fewest ticks, default) picks the best eligible recycler globally, so a busy own recycler may route to an idle public rental. 'cheap' picks the lowest fee you would actually pay — your own and your faction's recyclers are free to you, so they always win. Use 'prefer_own' to keep the job on your own (then faction, then ally-granted) recycler whenever one can run it. Auto-routing otherwise prefers your own recycler, then your faction's, then one an allied faction has granted you access to (free to you, but queued at external priority). 'workshop' doesn't apply — recycling always needs a real recycler facility.", enumValues: ["fast", "cheap", "prefer_own"] },
3036
+ { name: "preset", type: '"fast" | "cheap" | "prefer_own"', required: false, description: "Auto-routing preset. 'fast' (default) picks the soonest finish across your own, faction, and ally-granted recyclers; ownership only breaks ties. 'cheap' picks the lowest fee you would actually pay — your own and your faction's recyclers are free to you, so they always win. 'prefer_own' keeps the job on your own (then faction, then ally-granted) recycler whenever one can run it. 'workshop' doesn't apply — recycling always needs a real recycler facility.", enumValues: ["fast", "cheap", "prefer_own"] },
3035
3037
  { 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 },
3036
3038
  { 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." }
3037
3039
  ]
@@ -4306,9 +4308,16 @@ class Account {
4306
4308
  });
4307
4309
  }
4308
4310
  makeSocket() {
4309
- this.socket = new Socket({ url: this.url, webSocketFactory: this.webSocketFactory });
4310
- this.socket.onFrame = (frame) => this.routeFrame(frame);
4311
- this.socket.onClose = (err) => this.handleClose(err);
4311
+ const socket = new Socket({ url: this.url, webSocketFactory: this.webSocketFactory });
4312
+ socket.onFrame = (frame) => {
4313
+ if (this.socket === socket)
4314
+ this.routeFrame(frame);
4315
+ };
4316
+ socket.onClose = (err) => {
4317
+ if (this.socket === socket)
4318
+ this.handleClose(err);
4319
+ };
4320
+ this.socket = socket;
4312
4321
  }
4313
4322
  get state() {
4314
4323
  return this.cache.snapshot();
@@ -4449,8 +4458,11 @@ class Account {
4449
4458
  }
4450
4459
  }
4451
4460
  async refresh() {
4461
+ const revision = this.stateRevision;
4452
4462
  const result = await this.commands.spacemolt.get_status();
4453
4463
  const snapshot = requireStructuredContent(result, "spacemolt/get_status");
4464
+ if (revision !== this.stateRevision)
4465
+ return this.cache.snapshot();
4454
4466
  const changed = this.cache.seed(snapshot);
4455
4467
  if (changed.length)
4456
4468
  this.stateRevision++;
@@ -4981,10 +4993,19 @@ class Account {
4981
4993
  async reconnectOnce() {
4982
4994
  if (!this.credentialsProvider)
4983
4995
  throw new Error("reconnectOnce requires credentials");
4996
+ const stopIfClosed = () => {
4997
+ if (!this.userClosing)
4998
+ return;
4999
+ this.socket.close();
5000
+ throw new ConnectionClosedError("account was closed");
5001
+ };
5002
+ stopIfClosed();
4984
5003
  this.socket.close();
4985
5004
  this.makeSocket();
4986
5005
  await this.open();
5006
+ stopIfClosed();
4987
5007
  await this.authenticate(await this.credentialsProvider());
5008
+ stopIfClosed();
4988
5009
  await this.resubscribe();
4989
5010
  }
4990
5011
  async resubscribe() {
@@ -5049,6 +5070,7 @@ function normalizeCatalog(value) {
5049
5070
  const data = requireRecord(value, "catalog response");
5050
5071
  return {
5051
5072
  version: typeof data.version === "string" ? data.version : "",
5073
+ mining: isRecord(data.mining) ? data.mining : {},
5052
5074
  ships: catalogEntries(data.ships),
5053
5075
  items: catalogEntries(data.items),
5054
5076
  recipes: catalogEntries(data.recipes),
@@ -5481,7 +5503,13 @@ class SpacemoltClient {
5481
5503
  return;
5482
5504
  }
5483
5505
  this.reconnectingIds.add(id);
5484
- this.enqueueRateLimited(() => this.withConnectRetry(() => account.reconnectOnce())).then(() => this.notifyAccountReconnected(account)).catch((reconnectErr) => {
5506
+ const wanted = () => this.connected.get(id) === account;
5507
+ this.enqueueRateLimited(() => this.withConnectRetry(async () => wanted() ? account.reconnectOnce() : undefined)).then(() => {
5508
+ if (wanted())
5509
+ this.notifyAccountReconnected(account);
5510
+ }).catch((reconnectErr) => {
5511
+ if (!wanted())
5512
+ return;
5485
5513
  console.warn(`[spacemolt] failed to reconnect "${id}": ${reconnectErr}`);
5486
5514
  this.connected.delete(id);
5487
5515
  this.notifyAccountDisconnected(id, err);
@@ -5542,6 +5570,7 @@ var TYPED_NOTIFICATION_TYPES = [
5542
5570
  "action_error",
5543
5571
  "action_result",
5544
5572
  "arena_challenge",
5573
+ "arena_objective",
5545
5574
  "base_destroyed",
5546
5575
  "base_raid_update",
5547
5576
  "battle_alert",
@@ -5552,12 +5581,15 @@ var TYPED_NOTIFICATION_TYPES = [
5552
5581
  "battle_started",
5553
5582
  "battle_update",
5554
5583
  "chat_message",
5584
+ "cloak",
5585
+ "complete_mission",
5555
5586
  "crafting_update",
5556
5587
  "drone_adrift",
5557
5588
  "drone_destroyed",
5558
5589
  "drone_scan",
5559
5590
  "drone_survey",
5560
5591
  "drone_update",
5592
+ "error",
5561
5593
  "facility_reclaimed",
5562
5594
  "facility_rent_warning",
5563
5595
  "faction_alliance_broken",
@@ -5566,9 +5598,11 @@ var TYPED_NOTIFICATION_TYPES = [
5566
5598
  "faction_peace_accepted",
5567
5599
  "faction_peace_proposal",
5568
5600
  "faction_war_declared",
5601
+ "fleet",
5569
5602
  "market_update",
5570
5603
  "mining_yield",
5571
5604
  "observation_update",
5605
+ "ok",
5572
5606
  "personnel_update",
5573
5607
  "pilotless_ship",
5574
5608
  "pirate_destroyed",
@@ -5578,6 +5612,8 @@ var TYPED_NOTIFICATION_TYPES = [
5578
5612
  "prize_update",
5579
5613
  "ranch_poached",
5580
5614
  "reconnected",
5615
+ "refueled_by",
5616
+ "repaired_by",
5581
5617
  "scan_detected",
5582
5618
  "server_restart_warning",
5583
5619
  "ship_captured",
@@ -3,15 +3,16 @@
3
3
  *
4
4
  * Every other push frame is typed from the server's published
5
5
  * `Notification_<msg_type>` schema, and the generated `NotificationPayloads`
6
- * map carries it. These two families publish no schema at all — neither
7
- * appears in the spec's notification union — so without this file they reach a
8
- * consumer as `Record<string, unknown>`.
6
+ * map carries it. These two families publish only a single flat object each —
7
+ * optional fields and a free-form `action` string, covering a few variants — so
8
+ * the generated type cannot narrow to a variant's fields.
9
9
  *
10
10
  * They are hand-written rather than published because splitting them into
11
11
  * distinct `msg_type`s is the fix, and that breaks any v1 client switching on
12
12
  * `type === "ok"`. Until v1 is retired the shapes live here.
13
- * `tests/push-frames.test.ts` fails the moment the server does publish either
14
- * schema, which is the signal to delete this file and let codegen take over.
13
+ * `tests/push-frames.test.ts` fails the moment the server publishes either
14
+ * schema as a union, which is the signal to delete this file and let codegen
15
+ * take over.
15
16
  *
16
17
  * **Two discriminators.** Most `ok` variants key on `action`; seven key on
17
18
  * `type` instead. That split is the server's, not ours, so a bare `p.action`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spacemolt/lib",
3
- "version": "14.2.1",
3
+ "version": "15.0.1",
4
4
  "description": "The definitive TypeScript library for SpaceMolt. WebSocket-v2-first, multi-account, with local state caches. Internals are regenerated from the server's OpenAPI spec.",
5
5
  "type": "module",
6
6
  "module": "./dist/index.js",