Skip to content

Fleet Operations ​

Needs an access token

Demonstrates fleet management: checking a character's current fleet, fetching fleet details, listing members, and inspecting wing/squad structure.

REQUIRES AUTHENTICATION — set ESI_ACCESS_TOKEN in your environment.

ESI Scopes Required:

  • esi-fleets.read_fleet.v1 (read fleet info, members, wings)
  • esi-fleets.write_fleet.v1 (create wings/squads, invite members — shown but not executed)

Calls: fleets.getCharacterFleetInfo · fleets.getFleetInformation · fleets.getFleetMembers · fleets.getFleetWings

Run it ​

bash
git clone https://github.com/lgriffin/ESI.ts.git && cd ESI.ts
npm ci
npm run example:fleet

examples/fleet-operations.ts on GitHub. The imports below are written as a consumer of the package writes them; the file in the repository imports from ../src.

Source ​

ts
import { EsiClient } from '@lgriffin/esi.ts';
import { EsiError } from '@lgriffin/esi.ts/errors';
import { createConsoleLogger } from '@lgriffin/esi.ts';

// The program's own output. The client logs through the same console sink
// at ESI_LOG_LEVEL (default warn), so its diagnostics stay out of the way.
const log = createConsoleLogger('info');
const esiLog = createConsoleLogger();

const CHARACTER_ID = 1689391488;

async function main() {
  const client = new EsiClient({
    logger: esiLog,
    clientId: 'esi-ts-fleet-demo',
  });

  try {
    log.info('Fleet Operations\n');

    // Step 1: Check if the character is in a fleet
    // Scope: esi-fleets.read_fleet.v1
    log.info('Checking fleet membership...');
    let fleetInfo;
    try {
      fleetInfo = await client.fleets.getCharacterFleetInfo(CHARACTER_ID);
    } catch (err) {
      if (err instanceof EsiError && err.statusCode === 404) {
        log.info('Character is not currently in a fleet.\n');
        log.info(
          'To see fleet operations in action, join a fleet in-game and re-run this example.',
        );
        return;
      }
      throw err;
    }

    log.info('Fleet Membership');
    log.info('-'.repeat(40));
    log.info(`  Fleet ID: ${fleetInfo.fleet_id}`);
    log.info(`  Role:     ${fleetInfo.role}`);
    log.info(`  Wing:     ${fleetInfo.wing_id}`);
    log.info(`  Squad:    ${fleetInfo.squad_id}`);

    // Step 2: Fetch fleet details, members, and wings in parallel
    // Scope: esi-fleets.read_fleet.v1
    log.info('\nFetching fleet details...');
    const [fleet, members, wings] = await Promise.all([
      client.fleets.getFleetInformation(fleetInfo.fleet_id),
      client.fleets.getFleetMembers(fleetInfo.fleet_id),
      client.fleets.getFleetWings(fleetInfo.fleet_id),
    ]);

    // Fleet overview
    log.info('\nFleet Details');
    log.info('-'.repeat(40));
    log.info(`  MOTD:       ${fleet.motd || '(none)'}`);
    log.info(`  Free move:  ${fleet.is_free_move ? 'yes' : 'no'}`);

    // Members
    log.info(`\nFleet Members (${members.length})`);
    log.info('-'.repeat(40));
    for (const member of members.slice(0, 10)) {
      const role = member.role.padEnd(12);
      log.info(
        `  ${role} | Character ${member.character_id} | Ship type ${member.ship_type_id} | System ${member.solar_system_id}`,
      );
    }
    if (members.length > 10) {
      log.info(`  ... and ${members.length - 10} more members`);
    }

    // Ship composition
    const shipTypes = new Map<number, number>();
    for (const m of members) {
      shipTypes.set(m.ship_type_id, (shipTypes.get(m.ship_type_id) || 0) + 1);
    }
    log.info('\nShip Composition');
    log.info('-'.repeat(40));
    for (const [typeId, count] of [...shipTypes.entries()]
      .sort((a, b) => b[1] - a[1])
      .slice(0, 5)) {
      log.info(`  Type ${typeId}: ${count} pilot${count > 1 ? 's' : ''}`);
    }

    // Wing/squad structure
    log.info(`\nFleet Structure (${wings.length} wings)`);
    log.info('-'.repeat(40));
    for (const wing of wings) {
      const squadCount = wing.squads?.length || 0;
      log.info(`  Wing ${wing.id} "${wing.name}" (${squadCount} squads)`);
      if (wing.squads) {
        for (const squad of wing.squads) {
          log.info(`    Squad ${squad.id} "${squad.name}"`);
        }
      }
    }

    // Write operations are available but not demonstrated to avoid side effects:
    // client.fleets.updateFleet(fleetId, { motd: 'New MOTD' })       — Scope: esi-fleets.write_fleet.v1
    // client.fleets.createFleetWing(fleetId, {})                     — Scope: esi-fleets.write_fleet.v1
    // client.fleets.createFleetInvitation(fleetId, { character_id }) — Scope: esi-fleets.write_fleet.v1
    log.info(
      '\nNote: write operations (updateFleet, createFleetWing, createFleetInvitation) require scope esi-fleets.write_fleet.v1',
    );
  } catch (err) {
    if (
      err instanceof EsiError &&
      (err.statusCode === 401 || err.statusCode === 403)
    ) {
      log.error(
        'Authentication required. Set ESI_ACCESS_TOKEN with scope esi-fleets.read_fleet.v1',
      );
    } else {
      log.error('Request failed', { error: err });
    }
    process.exit(1);
  } finally {
    await client.shutdown();
  }
}

main();

Released under the GPL-3.0-or-later license.