letsfg 2026.5.69 → 2026.5.71
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/README.md +1 -1
- package/dist/{chunk-PW775XDB.mjs → chunk-GO3FXQXC.mjs} +35 -13
- package/dist/cli.js +36 -14
- package/dist/cli.mjs +2 -2
- package/dist/index.d.mts +11 -4
- package/dist/index.d.ts +11 -4
- package/dist/index.js +35 -13
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|---|---|---|
|
|
12
12
|
| **Search cost** | Free (Bearer token via `letsfg auth` — zero-amount card setup) | Prepaid credits |
|
|
13
13
|
| **Booking** | `POST /api/agent-book` — confirmed order or a booking link, no LetsFG fee | Direct airline URL (unlock required first) |
|
|
14
|
-
| **Speed** |
|
|
14
|
+
| **Speed** | 8–10 s to first results; longer on a split | 2–5 s (discover) · 8–10 s to first results (full) |
|
|
15
15
|
| **Setup** | `npm install letsfg` then `letsfg auth` | [letsfg.co/developers](https://letsfg.co/developers) |
|
|
16
16
|
|
|
17
17
|
> **Want direct airline URLs without any per-booking fee?** Use the [Developer API](https://letsfg.co/developers) — prepaid credits, results in seconds, no checkout step.
|
|
@@ -1558,8 +1558,12 @@ function cheapestOffer(result) {
|
|
|
1558
1558
|
return result.offers.reduce((min, o) => o.price < min.price ? o : min, result.offers[0]);
|
|
1559
1559
|
}
|
|
1560
1560
|
var DEFAULT_BASE_URL = "https://letsfg.co";
|
|
1561
|
-
var PFS_POLL_INTERVAL_MS =
|
|
1561
|
+
var PFS_POLL_INTERVAL_MS = 2e3;
|
|
1562
1562
|
var PFS_POLL_TIMEOUT_MS = 12e4;
|
|
1563
|
+
var LATE_MERGE_POLL_MS = 3e3;
|
|
1564
|
+
var LATE_MERGE_GRACE_MS = 9e4;
|
|
1565
|
+
var WAIT_FOR_SPLIT = (process.env.LETSFG_WAIT_FOR_SPLIT || "").trim() !== "0";
|
|
1566
|
+
var NON_TERMINAL = ["pending", "searching"];
|
|
1563
1567
|
var LetsFG = class {
|
|
1564
1568
|
bearerToken;
|
|
1565
1569
|
apiKey;
|
|
@@ -1595,7 +1599,7 @@ var LetsFG = class {
|
|
|
1595
1599
|
*
|
|
1596
1600
|
* Uses PFS (Bearer token) or Developer API (X-API-Key) depending on config.
|
|
1597
1601
|
* PFS: async polling (POST /api/search -> poll /api/results/<id> every 10s).
|
|
1598
|
-
* Developer API: synchronous
|
|
1602
|
+
* Developer API: synchronous call.
|
|
1599
1603
|
*
|
|
1600
1604
|
* @param origin - IATA code (e.g., "GDN", "LON")
|
|
1601
1605
|
* @param destination - IATA code (e.g., "BER", "BCN")
|
|
@@ -1627,31 +1631,49 @@ var LetsFG = class {
|
|
|
1627
1631
|
/** PFS path: POST /api/search -> poll /api/results/<id> */
|
|
1628
1632
|
async searchPFS(body) {
|
|
1629
1633
|
const { search_id } = await this.postWithBearer("/api/search", body);
|
|
1634
|
+
const poll = () => this.getNoAuth(`/api/results/${search_id}`);
|
|
1635
|
+
const inbound = (r) => Boolean(r.split_ticket_pending || r.gf_enrich_pending);
|
|
1630
1636
|
const deadline = Date.now() + PFS_POLL_TIMEOUT_MS;
|
|
1637
|
+
let terminal = null;
|
|
1631
1638
|
while (Date.now() < deadline) {
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
if (!["pending", "searching"].includes(result.status)) {
|
|
1637
|
-
return result;
|
|
1639
|
+
const result = await poll();
|
|
1640
|
+
if (!NON_TERMINAL.includes(result.status)) {
|
|
1641
|
+
terminal = result;
|
|
1642
|
+
break;
|
|
1638
1643
|
}
|
|
1644
|
+
await new Promise((r) => setTimeout(r, PFS_POLL_INTERVAL_MS));
|
|
1645
|
+
}
|
|
1646
|
+
if (!terminal) {
|
|
1647
|
+
throw new LetsFGError("Search timed out after 120s. Try polling /api/results/<id> directly.", 504);
|
|
1639
1648
|
}
|
|
1640
|
-
|
|
1649
|
+
const lateDeadline = Date.now() + LATE_MERGE_GRACE_MS;
|
|
1650
|
+
while (WAIT_FOR_SPLIT && inbound(terminal) && Date.now() < lateDeadline) {
|
|
1651
|
+
await new Promise((r) => setTimeout(r, LATE_MERGE_POLL_MS));
|
|
1652
|
+
const merged = await poll();
|
|
1653
|
+
if (!NON_TERMINAL.includes(merged.status)) terminal = merged;
|
|
1654
|
+
}
|
|
1655
|
+
return terminal;
|
|
1641
1656
|
}
|
|
1642
1657
|
/**
|
|
1643
1658
|
* Resolve a city/airport name to IATA codes.
|
|
1659
|
+
*
|
|
1660
|
+
* Developer API key only. There is no location endpoint on the PFS Bearer
|
|
1661
|
+
* lane — the same dead end `unlock()` documents below. This used to send
|
|
1662
|
+
* PFS callers to `/api/locations?q=`, a route that has never existed on
|
|
1663
|
+
* letsfg.co (verified 2026-08-16: 404, text/html), so they got a JSON parse
|
|
1664
|
+
* error off the 404 page instead of an answer. Pass an IATA code directly
|
|
1665
|
+
* on the PFS lane; a city code expands to every airport in that city.
|
|
1644
1666
|
*/
|
|
1645
1667
|
async resolveLocation(query) {
|
|
1646
|
-
this.
|
|
1647
|
-
const path =
|
|
1668
|
+
this.requireApiKey();
|
|
1669
|
+
const path = `/developers/api/v1/flights/locations/${encodeURIComponent(query)}`;
|
|
1648
1670
|
const data = await this.getWithAuth(path);
|
|
1649
1671
|
return Array.isArray(data) ? data : data.locations || [];
|
|
1650
1672
|
}
|
|
1651
1673
|
/**
|
|
1652
1674
|
* Unlock a flight offer — confirms live price, reveals direct airline booking URL.
|
|
1653
|
-
*
|
|
1654
|
-
*
|
|
1675
|
+
* Developer API only, legacy — there is no unlock endpoint on a PFS Bearer
|
|
1676
|
+
* token, so PFS callers use book() directly.
|
|
1655
1677
|
*/
|
|
1656
1678
|
async unlock(offerId) {
|
|
1657
1679
|
this.requireApiKey();
|
package/dist/cli.js
CHANGED
|
@@ -454,8 +454,12 @@ function offerSummary(offer) {
|
|
|
454
454
|
return `${offer.currency} ${offer.price.toFixed(2)} | ${airline} | ${route} | ${dur} | ${offer.outbound.stopovers} stop(s)`;
|
|
455
455
|
}
|
|
456
456
|
var DEFAULT_BASE_URL = "https://letsfg.co";
|
|
457
|
-
var PFS_POLL_INTERVAL_MS =
|
|
457
|
+
var PFS_POLL_INTERVAL_MS = 2e3;
|
|
458
458
|
var PFS_POLL_TIMEOUT_MS = 12e4;
|
|
459
|
+
var LATE_MERGE_POLL_MS = 3e3;
|
|
460
|
+
var LATE_MERGE_GRACE_MS = 9e4;
|
|
461
|
+
var WAIT_FOR_SPLIT = (process.env.LETSFG_WAIT_FOR_SPLIT || "").trim() !== "0";
|
|
462
|
+
var NON_TERMINAL = ["pending", "searching"];
|
|
459
463
|
var LetsFG = class {
|
|
460
464
|
bearerToken;
|
|
461
465
|
apiKey;
|
|
@@ -491,7 +495,7 @@ var LetsFG = class {
|
|
|
491
495
|
*
|
|
492
496
|
* Uses PFS (Bearer token) or Developer API (X-API-Key) depending on config.
|
|
493
497
|
* PFS: async polling (POST /api/search -> poll /api/results/<id> every 10s).
|
|
494
|
-
* Developer API: synchronous
|
|
498
|
+
* Developer API: synchronous call.
|
|
495
499
|
*
|
|
496
500
|
* @param origin - IATA code (e.g., "GDN", "LON")
|
|
497
501
|
* @param destination - IATA code (e.g., "BER", "BCN")
|
|
@@ -523,31 +527,49 @@ var LetsFG = class {
|
|
|
523
527
|
/** PFS path: POST /api/search -> poll /api/results/<id> */
|
|
524
528
|
async searchPFS(body) {
|
|
525
529
|
const { search_id } = await this.postWithBearer("/api/search", body);
|
|
530
|
+
const poll = () => this.getNoAuth(`/api/results/${search_id}`);
|
|
531
|
+
const inbound = (r) => Boolean(r.split_ticket_pending || r.gf_enrich_pending);
|
|
526
532
|
const deadline = Date.now() + PFS_POLL_TIMEOUT_MS;
|
|
533
|
+
let terminal = null;
|
|
527
534
|
while (Date.now() < deadline) {
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
if (!["pending", "searching"].includes(result.status)) {
|
|
533
|
-
return result;
|
|
535
|
+
const result = await poll();
|
|
536
|
+
if (!NON_TERMINAL.includes(result.status)) {
|
|
537
|
+
terminal = result;
|
|
538
|
+
break;
|
|
534
539
|
}
|
|
540
|
+
await new Promise((r) => setTimeout(r, PFS_POLL_INTERVAL_MS));
|
|
541
|
+
}
|
|
542
|
+
if (!terminal) {
|
|
543
|
+
throw new LetsFGError("Search timed out after 120s. Try polling /api/results/<id> directly.", 504);
|
|
535
544
|
}
|
|
536
|
-
|
|
545
|
+
const lateDeadline = Date.now() + LATE_MERGE_GRACE_MS;
|
|
546
|
+
while (WAIT_FOR_SPLIT && inbound(terminal) && Date.now() < lateDeadline) {
|
|
547
|
+
await new Promise((r) => setTimeout(r, LATE_MERGE_POLL_MS));
|
|
548
|
+
const merged = await poll();
|
|
549
|
+
if (!NON_TERMINAL.includes(merged.status)) terminal = merged;
|
|
550
|
+
}
|
|
551
|
+
return terminal;
|
|
537
552
|
}
|
|
538
553
|
/**
|
|
539
554
|
* Resolve a city/airport name to IATA codes.
|
|
555
|
+
*
|
|
556
|
+
* Developer API key only. There is no location endpoint on the PFS Bearer
|
|
557
|
+
* lane — the same dead end `unlock()` documents below. This used to send
|
|
558
|
+
* PFS callers to `/api/locations?q=`, a route that has never existed on
|
|
559
|
+
* letsfg.co (verified 2026-08-16: 404, text/html), so they got a JSON parse
|
|
560
|
+
* error off the 404 page instead of an answer. Pass an IATA code directly
|
|
561
|
+
* on the PFS lane; a city code expands to every airport in that city.
|
|
540
562
|
*/
|
|
541
563
|
async resolveLocation(query) {
|
|
542
|
-
this.
|
|
543
|
-
const path =
|
|
564
|
+
this.requireApiKey();
|
|
565
|
+
const path = `/developers/api/v1/flights/locations/${encodeURIComponent(query)}`;
|
|
544
566
|
const data = await this.getWithAuth(path);
|
|
545
567
|
return Array.isArray(data) ? data : data.locations || [];
|
|
546
568
|
}
|
|
547
569
|
/**
|
|
548
570
|
* Unlock a flight offer — confirms live price, reveals direct airline booking URL.
|
|
549
|
-
*
|
|
550
|
-
*
|
|
571
|
+
* Developer API only, legacy — there is no unlock endpoint on a PFS Bearer
|
|
572
|
+
* token, so PFS callers use book() directly.
|
|
551
573
|
*/
|
|
552
574
|
async unlock(offerId) {
|
|
553
575
|
this.requireApiKey();
|
|
@@ -1184,7 +1206,7 @@ Developer API only (a SEPARATE paid product \u2014 most agents should not use th
|
|
|
1184
1206
|
they create a billing account. Use auth above instead):
|
|
1185
1207
|
register --name ... --email ... Create a paid Developer API account
|
|
1186
1208
|
setup-payment Attach a card to that paid account
|
|
1187
|
-
unlock <offer_id> [Developer API only] Unlock offer
|
|
1209
|
+
unlock <offer_id> [Developer API only] Unlock offer (legacy)
|
|
1188
1210
|
|
|
1189
1211
|
Options:
|
|
1190
1212
|
--json, -j Output raw JSON
|
package/dist/cli.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
LetsFG,
|
|
4
4
|
LetsFGError,
|
|
5
5
|
offerSummary
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-GO3FXQXC.mjs";
|
|
7
7
|
import {
|
|
8
8
|
BearerTokenError,
|
|
9
9
|
getBearerToken,
|
|
@@ -348,7 +348,7 @@ Developer API only (a SEPARATE paid product \u2014 most agents should not use th
|
|
|
348
348
|
they create a billing account. Use auth above instead):
|
|
349
349
|
register --name ... --email ... Create a paid Developer API account
|
|
350
350
|
setup-payment Attach a card to that paid account
|
|
351
|
-
unlock <offer_id> [Developer API only] Unlock offer
|
|
351
|
+
unlock <offer_id> [Developer API only] Unlock offer (legacy)
|
|
352
352
|
|
|
353
353
|
Options:
|
|
354
354
|
--json, -j Output raw JSON
|
package/dist/index.d.mts
CHANGED
|
@@ -283,7 +283,7 @@ declare function getOfferDetailPromptNotes(offer: OfferDetailLike): string[];
|
|
|
283
283
|
* const flights = await bt.search('GDN', 'BER', '2026-03-03');
|
|
284
284
|
*
|
|
285
285
|
* // Developer API (prepaid credits)
|
|
286
|
-
* const bt2 = new LetsFG({ apiKey: '
|
|
286
|
+
* const bt2 = new LetsFG({ apiKey: 'letsfg_...' });
|
|
287
287
|
* const flights2 = await bt2.search('LHR', 'JFK', '2026-04-15');
|
|
288
288
|
* ```
|
|
289
289
|
*/
|
|
@@ -473,7 +473,7 @@ declare class LetsFG {
|
|
|
473
473
|
*
|
|
474
474
|
* Uses PFS (Bearer token) or Developer API (X-API-Key) depending on config.
|
|
475
475
|
* PFS: async polling (POST /api/search -> poll /api/results/<id> every 10s).
|
|
476
|
-
* Developer API: synchronous
|
|
476
|
+
* Developer API: synchronous call.
|
|
477
477
|
*
|
|
478
478
|
* @param origin - IATA code (e.g., "GDN", "LON")
|
|
479
479
|
* @param destination - IATA code (e.g., "BER", "BCN")
|
|
@@ -485,12 +485,19 @@ declare class LetsFG {
|
|
|
485
485
|
private searchPFS;
|
|
486
486
|
/**
|
|
487
487
|
* Resolve a city/airport name to IATA codes.
|
|
488
|
+
*
|
|
489
|
+
* Developer API key only. There is no location endpoint on the PFS Bearer
|
|
490
|
+
* lane — the same dead end `unlock()` documents below. This used to send
|
|
491
|
+
* PFS callers to `/api/locations?q=`, a route that has never existed on
|
|
492
|
+
* letsfg.co (verified 2026-08-16: 404, text/html), so they got a JSON parse
|
|
493
|
+
* error off the 404 page instead of an answer. Pass an IATA code directly
|
|
494
|
+
* on the PFS lane; a city code expands to every airport in that city.
|
|
488
495
|
*/
|
|
489
496
|
resolveLocation(query: string): Promise<Array<Record<string, unknown>>>;
|
|
490
497
|
/**
|
|
491
498
|
* Unlock a flight offer — confirms live price, reveals direct airline booking URL.
|
|
492
|
-
*
|
|
493
|
-
*
|
|
499
|
+
* Developer API only, legacy — there is no unlock endpoint on a PFS Bearer
|
|
500
|
+
* token, so PFS callers use book() directly.
|
|
494
501
|
*/
|
|
495
502
|
unlock(offerId: string): Promise<UnlockResult>;
|
|
496
503
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -283,7 +283,7 @@ declare function getOfferDetailPromptNotes(offer: OfferDetailLike): string[];
|
|
|
283
283
|
* const flights = await bt.search('GDN', 'BER', '2026-03-03');
|
|
284
284
|
*
|
|
285
285
|
* // Developer API (prepaid credits)
|
|
286
|
-
* const bt2 = new LetsFG({ apiKey: '
|
|
286
|
+
* const bt2 = new LetsFG({ apiKey: 'letsfg_...' });
|
|
287
287
|
* const flights2 = await bt2.search('LHR', 'JFK', '2026-04-15');
|
|
288
288
|
* ```
|
|
289
289
|
*/
|
|
@@ -473,7 +473,7 @@ declare class LetsFG {
|
|
|
473
473
|
*
|
|
474
474
|
* Uses PFS (Bearer token) or Developer API (X-API-Key) depending on config.
|
|
475
475
|
* PFS: async polling (POST /api/search -> poll /api/results/<id> every 10s).
|
|
476
|
-
* Developer API: synchronous
|
|
476
|
+
* Developer API: synchronous call.
|
|
477
477
|
*
|
|
478
478
|
* @param origin - IATA code (e.g., "GDN", "LON")
|
|
479
479
|
* @param destination - IATA code (e.g., "BER", "BCN")
|
|
@@ -485,12 +485,19 @@ declare class LetsFG {
|
|
|
485
485
|
private searchPFS;
|
|
486
486
|
/**
|
|
487
487
|
* Resolve a city/airport name to IATA codes.
|
|
488
|
+
*
|
|
489
|
+
* Developer API key only. There is no location endpoint on the PFS Bearer
|
|
490
|
+
* lane — the same dead end `unlock()` documents below. This used to send
|
|
491
|
+
* PFS callers to `/api/locations?q=`, a route that has never existed on
|
|
492
|
+
* letsfg.co (verified 2026-08-16: 404, text/html), so they got a JSON parse
|
|
493
|
+
* error off the 404 page instead of an answer. Pass an IATA code directly
|
|
494
|
+
* on the PFS lane; a city code expands to every airport in that city.
|
|
488
495
|
*/
|
|
489
496
|
resolveLocation(query: string): Promise<Array<Record<string, unknown>>>;
|
|
490
497
|
/**
|
|
491
498
|
* Unlock a flight offer — confirms live price, reveals direct airline booking URL.
|
|
492
|
-
*
|
|
493
|
-
*
|
|
499
|
+
* Developer API only, legacy — there is no unlock endpoint on a PFS Bearer
|
|
500
|
+
* token, so PFS callers use book() directly.
|
|
494
501
|
*/
|
|
495
502
|
unlock(offerId: string): Promise<UnlockResult>;
|
|
496
503
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1606,8 +1606,12 @@ function cheapestOffer(result) {
|
|
|
1606
1606
|
return result.offers.reduce((min, o) => o.price < min.price ? o : min, result.offers[0]);
|
|
1607
1607
|
}
|
|
1608
1608
|
var DEFAULT_BASE_URL = "https://letsfg.co";
|
|
1609
|
-
var PFS_POLL_INTERVAL_MS =
|
|
1609
|
+
var PFS_POLL_INTERVAL_MS = 2e3;
|
|
1610
1610
|
var PFS_POLL_TIMEOUT_MS = 12e4;
|
|
1611
|
+
var LATE_MERGE_POLL_MS = 3e3;
|
|
1612
|
+
var LATE_MERGE_GRACE_MS = 9e4;
|
|
1613
|
+
var WAIT_FOR_SPLIT = (process.env.LETSFG_WAIT_FOR_SPLIT || "").trim() !== "0";
|
|
1614
|
+
var NON_TERMINAL = ["pending", "searching"];
|
|
1611
1615
|
var LetsFG = class {
|
|
1612
1616
|
bearerToken;
|
|
1613
1617
|
apiKey;
|
|
@@ -1643,7 +1647,7 @@ var LetsFG = class {
|
|
|
1643
1647
|
*
|
|
1644
1648
|
* Uses PFS (Bearer token) or Developer API (X-API-Key) depending on config.
|
|
1645
1649
|
* PFS: async polling (POST /api/search -> poll /api/results/<id> every 10s).
|
|
1646
|
-
* Developer API: synchronous
|
|
1650
|
+
* Developer API: synchronous call.
|
|
1647
1651
|
*
|
|
1648
1652
|
* @param origin - IATA code (e.g., "GDN", "LON")
|
|
1649
1653
|
* @param destination - IATA code (e.g., "BER", "BCN")
|
|
@@ -1675,31 +1679,49 @@ var LetsFG = class {
|
|
|
1675
1679
|
/** PFS path: POST /api/search -> poll /api/results/<id> */
|
|
1676
1680
|
async searchPFS(body) {
|
|
1677
1681
|
const { search_id } = await this.postWithBearer("/api/search", body);
|
|
1682
|
+
const poll = () => this.getNoAuth(`/api/results/${search_id}`);
|
|
1683
|
+
const inbound = (r) => Boolean(r.split_ticket_pending || r.gf_enrich_pending);
|
|
1678
1684
|
const deadline = Date.now() + PFS_POLL_TIMEOUT_MS;
|
|
1685
|
+
let terminal = null;
|
|
1679
1686
|
while (Date.now() < deadline) {
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
if (!["pending", "searching"].includes(result.status)) {
|
|
1685
|
-
return result;
|
|
1687
|
+
const result = await poll();
|
|
1688
|
+
if (!NON_TERMINAL.includes(result.status)) {
|
|
1689
|
+
terminal = result;
|
|
1690
|
+
break;
|
|
1686
1691
|
}
|
|
1692
|
+
await new Promise((r) => setTimeout(r, PFS_POLL_INTERVAL_MS));
|
|
1693
|
+
}
|
|
1694
|
+
if (!terminal) {
|
|
1695
|
+
throw new LetsFGError("Search timed out after 120s. Try polling /api/results/<id> directly.", 504);
|
|
1687
1696
|
}
|
|
1688
|
-
|
|
1697
|
+
const lateDeadline = Date.now() + LATE_MERGE_GRACE_MS;
|
|
1698
|
+
while (WAIT_FOR_SPLIT && inbound(terminal) && Date.now() < lateDeadline) {
|
|
1699
|
+
await new Promise((r) => setTimeout(r, LATE_MERGE_POLL_MS));
|
|
1700
|
+
const merged = await poll();
|
|
1701
|
+
if (!NON_TERMINAL.includes(merged.status)) terminal = merged;
|
|
1702
|
+
}
|
|
1703
|
+
return terminal;
|
|
1689
1704
|
}
|
|
1690
1705
|
/**
|
|
1691
1706
|
* Resolve a city/airport name to IATA codes.
|
|
1707
|
+
*
|
|
1708
|
+
* Developer API key only. There is no location endpoint on the PFS Bearer
|
|
1709
|
+
* lane — the same dead end `unlock()` documents below. This used to send
|
|
1710
|
+
* PFS callers to `/api/locations?q=`, a route that has never existed on
|
|
1711
|
+
* letsfg.co (verified 2026-08-16: 404, text/html), so they got a JSON parse
|
|
1712
|
+
* error off the 404 page instead of an answer. Pass an IATA code directly
|
|
1713
|
+
* on the PFS lane; a city code expands to every airport in that city.
|
|
1692
1714
|
*/
|
|
1693
1715
|
async resolveLocation(query) {
|
|
1694
|
-
this.
|
|
1695
|
-
const path =
|
|
1716
|
+
this.requireApiKey();
|
|
1717
|
+
const path = `/developers/api/v1/flights/locations/${encodeURIComponent(query)}`;
|
|
1696
1718
|
const data = await this.getWithAuth(path);
|
|
1697
1719
|
return Array.isArray(data) ? data : data.locations || [];
|
|
1698
1720
|
}
|
|
1699
1721
|
/**
|
|
1700
1722
|
* Unlock a flight offer — confirms live price, reveals direct airline booking URL.
|
|
1701
|
-
*
|
|
1702
|
-
*
|
|
1723
|
+
* Developer API only, legacy — there is no unlock endpoint on a PFS Bearer
|
|
1724
|
+
* token, so PFS callers use book() directly.
|
|
1703
1725
|
*/
|
|
1704
1726
|
async unlock(offerId) {
|
|
1705
1727
|
this.requireApiKey();
|
package/dist/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "letsfg",
|
|
3
|
-
"version": "2026.5.
|
|
3
|
+
"version": "2026.5.71",
|
|
4
4
|
"description": "Flights and hotels for AI agents. Server-side engine covers hundreds of airlines; hotels are real bookable inventory with free cancellation and pay-later terms. Includes open-source ranking engine.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|