letsfg 1.4.2 → 1.7.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # LetsFG — Your AI agent just learned to book flights. (Node.js)
2
2
 
3
- **195 airlines. Real prices. One function call.** Search 400+ airlines at raw airline prices — **$20–$50 cheaper** than Booking.com, Kayak, and other OTAs. Zero dependencies. Built for AI agents.
3
+ **200 airlines. Real prices. One function call.** Search 400+ airlines at raw airline prices — **$20–$50 cheaper** than Booking.com, Kayak, and other OTAs. Zero dependencies. Built for AI agents.
4
4
 
5
5
  > **Don't want to install anything?** [**Try it on Messenger**](https://m.me/61579557368989) — search flights instantly, no setup needed.
6
6
 
@@ -81,7 +81,7 @@ letsfg book off_xxx -p '{"id":"pas_xxx","given_name":"John",...}' -e john@exampl
81
81
 
82
82
  ### `searchLocal(origin, destination, dateFrom, options?)`
83
83
 
84
- Search 195 airline connectors locally (no API key needed). Requires Python + `letsfg` installed.
84
+ Search 200 airline connectors locally (no API key needed). Requires Python + `letsfg` installed.
85
85
 
86
86
  ```typescript
87
87
  import { searchLocal } from 'letsfg';
@@ -106,12 +106,6 @@ var ValidationError = class extends LetsFGError {
106
106
  this.name = "ValidationError";
107
107
  }
108
108
  };
109
- var StarRequiredError = class extends LetsFGError {
110
- constructor(message = "GitHub star required. Run: letsfg star --github <your-username>") {
111
- super(message, 403, {}, "STAR_REQUIRED");
112
- this.name = "StarRequiredError";
113
- }
114
- };
115
109
  function routeStr(route) {
116
110
  if (!route.segments.length) return "";
117
111
  const codes = [route.segments[0].origin, ...route.segments.map((s) => s.destination)];
@@ -162,13 +156,8 @@ async function searchLocal(origin, destination, dateFrom, options = {}) {
162
156
  child.on("close", (code) => {
163
157
  try {
164
158
  const data = JSON.parse(stdout);
165
- if (data.error) {
166
- if (data.error.includes("GitHub star required") || data.error.includes("star required")) {
167
- reject(new StarRequiredError(data.error));
168
- } else {
169
- reject(new LetsFGError(data.error));
170
- }
171
- } else resolve(data);
159
+ if (data.error) reject(new LetsFGError(data.error));
160
+ else resolve(data);
172
161
  } catch {
173
162
  reject(new LetsFGError(
174
163
  `Python search failed (code ${code}): ${stdout || stderr}
@@ -205,7 +194,10 @@ var LetsFG = class {
205
194
  }
206
195
  // ── Core methods ─────────────────────────────────────────────────────
207
196
  /**
208
- * Search for flights — FREE, unlimited.
197
+ * Search for flights — FREE, unlimited, runs locally on your machine.
198
+ *
199
+ * Uses 200 airline connectors via Python subprocess. No backend call.
200
+ * Requires: pip install letsfg && playwright install chromium
209
201
  *
210
202
  * @param origin - IATA code (e.g., "GDN", "LON")
211
203
  * @param destination - IATA code (e.g., "BER", "BCN")
@@ -213,32 +205,44 @@ var LetsFG = class {
213
205
  * @param options - Optional search parameters
214
206
  */
215
207
  async search(origin, destination, dateFrom, options = {}) {
216
- this.requireApiKey();
217
- const body = {
218
- origin: origin.toUpperCase(),
219
- destination: destination.toUpperCase(),
220
- date_from: dateFrom,
221
- adults: options.adults ?? 1,
222
- children: options.children ?? 0,
223
- infants: options.infants ?? 0,
224
- max_stopovers: options.maxStopovers ?? 2,
225
- currency: options.currency ?? "EUR",
226
- limit: options.limit ?? 20,
227
- sort: options.sort ?? "price"
228
- };
229
- if (options.returnDate) body.return_from = options.returnDate;
230
- if (options.cabinClass) body.cabin_class = options.cabinClass;
231
- return this.post("/api/v1/flights/search", body);
208
+ return searchLocal(origin, destination, dateFrom, options);
232
209
  }
233
210
  /**
234
- * Resolve a city/airport name to IATA codes.
211
+ * Resolve a city/airport name to IATA codes — runs locally, no backend call.
235
212
  */
236
213
  async resolveLocation(query) {
237
- this.requireApiKey();
238
- const data = await this.get(`/api/v1/flights/locations/${encodeURIComponent(query)}`);
239
- if (Array.isArray(data)) return data;
240
- if (data && Array.isArray(data.locations)) return data.locations;
241
- return data ? [data] : [];
214
+ const { spawn } = await import("child_process");
215
+ const params = JSON.stringify({ __resolve_location: true, query });
216
+ return new Promise((resolve, reject) => {
217
+ const pythonCmd = process.platform === "win32" ? "python" : "python3";
218
+ const child = spawn(pythonCmd, ["-m", "letsfg.local"], {
219
+ stdio: ["pipe", "pipe", "pipe"]
220
+ });
221
+ let stdout = "";
222
+ let stderr = "";
223
+ child.stdout.on("data", (d) => {
224
+ stdout += d.toString();
225
+ });
226
+ child.stderr.on("data", (d) => {
227
+ stderr += d.toString();
228
+ });
229
+ child.on("close", (code) => {
230
+ try {
231
+ const data = JSON.parse(stdout);
232
+ if (data.error) reject(new LetsFGError(data.error));
233
+ else resolve(Array.isArray(data) ? data : data.locations || [data]);
234
+ } catch {
235
+ reject(new LetsFGError(
236
+ `Location resolution failed (code ${code}): ${stdout || stderr}`
237
+ ));
238
+ }
239
+ });
240
+ child.on("error", (err) => {
241
+ reject(new LetsFGError(`Cannot start Python: ${err.message}`));
242
+ });
243
+ child.stdin.write(params);
244
+ child.stdin.end();
245
+ });
242
246
  }
243
247
  /**
244
248
  * Unlock a flight offer — FREE with GitHub star.
@@ -402,6 +406,7 @@ var LetsFG = class {
402
406
  "Content-Type": "application/json",
403
407
  "X-API-Key": this.apiKey,
404
408
  "User-Agent": "LetsFG-js/0.1.0",
409
+ "X-Client-Type": "js-sdk",
405
410
  ...init.headers || {}
406
411
  },
407
412
  signal: controller.signal
@@ -470,7 +475,6 @@ export {
470
475
  PaymentRequiredError,
471
476
  OfferExpiredError,
472
477
  ValidationError,
473
- StarRequiredError,
474
478
  offerSummary,
475
479
  cheapestOffer,
476
480
  searchLocal,
package/dist/cli.js CHANGED
@@ -131,12 +131,6 @@ var ValidationError = class extends LetsFGError {
131
131
  this.name = "ValidationError";
132
132
  }
133
133
  };
134
- var StarRequiredError = class extends LetsFGError {
135
- constructor(message = "GitHub star required. Run: letsfg star --github <your-username>") {
136
- super(message, 403, {}, "STAR_REQUIRED");
137
- this.name = "StarRequiredError";
138
- }
139
- };
140
134
  function routeStr(route) {
141
135
  if (!route.segments.length) return "";
142
136
  const codes = [route.segments[0].origin, ...route.segments.map((s) => s.destination)];
@@ -153,6 +147,55 @@ function offerSummary(offer) {
153
147
  const airline = offer.owner_airline || offer.airlines[0] || "?";
154
148
  return `${offer.currency} ${offer.price.toFixed(2)} | ${airline} | ${route} | ${dur} | ${offer.outbound.stopovers} stop(s)`;
155
149
  }
150
+ async function searchLocal(origin, destination, dateFrom, options = {}) {
151
+ const { spawn } = await import("child_process");
152
+ const params = JSON.stringify({
153
+ origin: origin.toUpperCase(),
154
+ destination: destination.toUpperCase(),
155
+ date_from: dateFrom,
156
+ adults: options.adults ?? 1,
157
+ children: options.children ?? 0,
158
+ currency: options.currency ?? "EUR",
159
+ limit: options.limit ?? 50,
160
+ return_date: options.returnDate,
161
+ cabin_class: options.cabinClass,
162
+ ...options.maxBrowsers != null && { max_browsers: options.maxBrowsers }
163
+ });
164
+ return new Promise((resolve, reject) => {
165
+ const pythonCmd = process.platform === "win32" ? "python" : "python3";
166
+ const child = spawn(pythonCmd, ["-m", "letsfg.local"], {
167
+ stdio: ["pipe", "pipe", "pipe"]
168
+ });
169
+ let stdout = "";
170
+ let stderr = "";
171
+ child.stdout.on("data", (d) => {
172
+ stdout += d.toString();
173
+ });
174
+ child.stderr.on("data", (d) => {
175
+ stderr += d.toString();
176
+ });
177
+ child.on("close", (code) => {
178
+ try {
179
+ const data = JSON.parse(stdout);
180
+ if (data.error) reject(new LetsFGError(data.error));
181
+ else resolve(data);
182
+ } catch {
183
+ reject(new LetsFGError(
184
+ `Python search failed (code ${code}): ${stdout || stderr}
185
+ Make sure LetsFG is installed: pip install letsfg && playwright install chromium`
186
+ ));
187
+ }
188
+ });
189
+ child.on("error", (err) => {
190
+ reject(new LetsFGError(
191
+ `Cannot start Python: ${err.message}
192
+ Install: pip install letsfg && playwright install chromium`
193
+ ));
194
+ });
195
+ child.stdin.write(params);
196
+ child.stdin.end();
197
+ });
198
+ }
156
199
  var DEFAULT_BASE_URL = "https://api.letsfg.co";
157
200
  var LetsFG = class {
158
201
  apiKey;
@@ -172,7 +215,10 @@ var LetsFG = class {
172
215
  }
173
216
  // ── Core methods ─────────────────────────────────────────────────────
174
217
  /**
175
- * Search for flights — FREE, unlimited.
218
+ * Search for flights — FREE, unlimited, runs locally on your machine.
219
+ *
220
+ * Uses 200 airline connectors via Python subprocess. No backend call.
221
+ * Requires: pip install letsfg && playwright install chromium
176
222
  *
177
223
  * @param origin - IATA code (e.g., "GDN", "LON")
178
224
  * @param destination - IATA code (e.g., "BER", "BCN")
@@ -180,32 +226,44 @@ var LetsFG = class {
180
226
  * @param options - Optional search parameters
181
227
  */
182
228
  async search(origin, destination, dateFrom, options = {}) {
183
- this.requireApiKey();
184
- const body = {
185
- origin: origin.toUpperCase(),
186
- destination: destination.toUpperCase(),
187
- date_from: dateFrom,
188
- adults: options.adults ?? 1,
189
- children: options.children ?? 0,
190
- infants: options.infants ?? 0,
191
- max_stopovers: options.maxStopovers ?? 2,
192
- currency: options.currency ?? "EUR",
193
- limit: options.limit ?? 20,
194
- sort: options.sort ?? "price"
195
- };
196
- if (options.returnDate) body.return_from = options.returnDate;
197
- if (options.cabinClass) body.cabin_class = options.cabinClass;
198
- return this.post("/api/v1/flights/search", body);
229
+ return searchLocal(origin, destination, dateFrom, options);
199
230
  }
200
231
  /**
201
- * Resolve a city/airport name to IATA codes.
232
+ * Resolve a city/airport name to IATA codes — runs locally, no backend call.
202
233
  */
203
234
  async resolveLocation(query) {
204
- this.requireApiKey();
205
- const data = await this.get(`/api/v1/flights/locations/${encodeURIComponent(query)}`);
206
- if (Array.isArray(data)) return data;
207
- if (data && Array.isArray(data.locations)) return data.locations;
208
- return data ? [data] : [];
235
+ const { spawn } = await import("child_process");
236
+ const params = JSON.stringify({ __resolve_location: true, query });
237
+ return new Promise((resolve, reject) => {
238
+ const pythonCmd = process.platform === "win32" ? "python" : "python3";
239
+ const child = spawn(pythonCmd, ["-m", "letsfg.local"], {
240
+ stdio: ["pipe", "pipe", "pipe"]
241
+ });
242
+ let stdout = "";
243
+ let stderr = "";
244
+ child.stdout.on("data", (d) => {
245
+ stdout += d.toString();
246
+ });
247
+ child.stderr.on("data", (d) => {
248
+ stderr += d.toString();
249
+ });
250
+ child.on("close", (code) => {
251
+ try {
252
+ const data = JSON.parse(stdout);
253
+ if (data.error) reject(new LetsFGError(data.error));
254
+ else resolve(Array.isArray(data) ? data : data.locations || [data]);
255
+ } catch {
256
+ reject(new LetsFGError(
257
+ `Location resolution failed (code ${code}): ${stdout || stderr}`
258
+ ));
259
+ }
260
+ });
261
+ child.on("error", (err) => {
262
+ reject(new LetsFGError(`Cannot start Python: ${err.message}`));
263
+ });
264
+ child.stdin.write(params);
265
+ child.stdin.end();
266
+ });
209
267
  }
210
268
  /**
211
269
  * Unlock a flight offer — FREE with GitHub star.
@@ -369,6 +427,7 @@ var LetsFG = class {
369
427
  "Content-Type": "application/json",
370
428
  "X-API-Key": this.apiKey,
371
429
  "User-Agent": "LetsFG-js/0.1.0",
430
+ "X-Client-Type": "js-sdk",
372
431
  ...init.headers || {}
373
432
  },
374
433
  signal: controller.signal
@@ -610,44 +669,42 @@ async function cmdRegister(args) {
610
669
  }
611
670
  async function cmdStar(args) {
612
671
  const jsonOut = hasFlag(args, "--json") || hasFlag(args, "-j");
672
+ const apiKey = getFlag(args, "--api-key", "-k");
673
+ const baseUrl = getFlag(args, "--base-url");
613
674
  const github = getFlag(args, "--github", "-g");
614
675
  if (!github) {
615
676
  console.error("Usage: letsfg star --github <your-github-username>");
616
677
  process.exit(1);
617
678
  }
618
- const { spawn } = await import("child_process");
619
- const pythonCmd = process.platform === "win32" ? "python" : "python3";
620
- return new Promise((resolve, reject) => {
621
- const child = spawn(pythonCmd, ["-m", "letsfg.cli", "star", "--github", github], {
622
- stdio: ["inherit", "pipe", "pipe"]
623
- });
624
- let stdout = "";
625
- let stderr = "";
626
- child.stdout.on("data", (d) => {
627
- stdout += d.toString();
628
- });
629
- child.stderr.on("data", (d) => {
630
- stderr += d.toString();
631
- });
632
- child.on("close", (code) => {
633
- if (jsonOut) {
634
- const verified = code === 0;
635
- console.log(JSON.stringify({ status: verified ? "verified" : "star_required", github_username: github }));
636
- } else {
637
- if (stdout) console.log(stdout);
638
- if (stderr && code !== 0) console.error(stderr);
639
- }
640
- if (code !== 0) {
641
- process.exit(1);
642
- }
643
- resolve();
644
- });
645
- child.on("error", (err) => {
646
- console.error(`Cannot start Python: ${err.message}`);
647
- console.error("Install: pip install letsfg");
648
- process.exit(1);
649
- });
650
- });
679
+ const bt = new LetsFG({ apiKey, baseUrl });
680
+ const result = await bt.linkGithub(github);
681
+ if (jsonOut) {
682
+ console.log(JSON.stringify(result, null, 2));
683
+ return;
684
+ }
685
+ const status = result.status;
686
+ if (status === "verified") {
687
+ console.log(`
688
+ \u2713 GitHub star verified! Unlimited access granted.`);
689
+ console.log(` Username: ${result.github_username}`);
690
+ console.log(`
691
+ You're all set \u2014 search, unlock, and book for free.
692
+ `);
693
+ } else if (status === "already_verified") {
694
+ console.log(`
695
+ \u2713 Already verified! You have unlimited access.`);
696
+ console.log(` Username: ${result.github_username}
697
+ `);
698
+ } else if (status === "star_required") {
699
+ console.log(`
700
+ \u2717 Star not found for '${github}'.`);
701
+ console.log(` 1. Star the repo: https://github.com/LetsFG/LetsFG`);
702
+ console.log(` 2. Run this command again.
703
+ `);
704
+ } else {
705
+ console.error(` \u2717 Unexpected status: ${status}`);
706
+ process.exit(1);
707
+ }
651
708
  }
652
709
  async function cmdSetupPayment(args) {
653
710
  const jsonOut = hasFlag(args, "--json") || hasFlag(args, "-j");
@@ -705,19 +762,16 @@ var HELP = `
705
762
  LetsFG \u2014 Agent-native flight search & booking.
706
763
 
707
764
  Search 400+ airlines at raw airline prices \u2014 $20-50 cheaper than OTAs.
708
-
709
- **GitHub Star Required!**
710
- Before using, you must:
711
- 1. Star https://github.com/LetsFG/LetsFG
712
- 2. Run: letsfg star --github <your-username>
765
+ 100% FREE \u2014 just star our GitHub repo for unlimited access.
713
766
 
714
767
  Commands:
715
- star --github <username> Verify GitHub star (required first!)
716
- search <origin> <dest> <date> Search for flights (FREE with star)
768
+ search <origin> <dest> <date> Search for flights (FREE)
717
769
  locations <query> Resolve city name to IATA codes
718
- unlock <offer_id> Unlock offer (hosted mode only)
719
- book <offer_id> --passenger ... Book flight
770
+ star --github <username> Link GitHub \u2014 star repo for free access
771
+ unlock <offer_id> Unlock offer (FREE with GitHub star)
772
+ book <offer_id> --passenger ... Book flight (FREE after unlock)
720
773
  register --name ... --email ... Register new agent
774
+ setup-payment Legacy payment setup
721
775
  me Show agent profile
722
776
 
723
777
  Options:
@@ -726,6 +780,7 @@ Options:
726
780
  --base-url API URL (default: https://api.letsfg.co)
727
781
 
728
782
  Examples:
783
+ letsfg register --name my-agent --email me@example.com
729
784
  letsfg star --github octocat
730
785
  letsfg search GDN BER 2026-03-03 --sort price
731
786
  letsfg unlock off_xxx
@@ -772,17 +827,6 @@ async function main() {
772
827
  process.exit(1);
773
828
  }
774
829
  } catch (e) {
775
- if (e instanceof StarRequiredError) {
776
- console.error(`
777
- \u2B50 GitHub star required to use LetsFG!
778
- `);
779
- console.error(` 1. Star the repo: https://github.com/LetsFG/LetsFG`);
780
- console.error(` 2. Run: letsfg star --github <your-username>
781
- `);
782
- console.error(` This is completely FREE \u2014 just a star to help us grow!
783
- `);
784
- process.exit(1);
785
- }
786
830
  if (e instanceof LetsFGError) {
787
831
  console.error(`Error: ${e.message}`);
788
832
  process.exit(1);
package/dist/cli.mjs CHANGED
@@ -2,9 +2,8 @@
2
2
  import {
3
3
  LetsFG,
4
4
  LetsFGError,
5
- StarRequiredError,
6
5
  offerSummary
7
- } from "./chunk-7LYR3CHE.mjs";
6
+ } from "./chunk-RLP7MSF6.mjs";
8
7
 
9
8
  // src/cli.ts
10
9
  function getFlag(args, flag, alias) {
@@ -226,44 +225,42 @@ async function cmdRegister(args) {
226
225
  }
227
226
  async function cmdStar(args) {
228
227
  const jsonOut = hasFlag(args, "--json") || hasFlag(args, "-j");
228
+ const apiKey = getFlag(args, "--api-key", "-k");
229
+ const baseUrl = getFlag(args, "--base-url");
229
230
  const github = getFlag(args, "--github", "-g");
230
231
  if (!github) {
231
232
  console.error("Usage: letsfg star --github <your-github-username>");
232
233
  process.exit(1);
233
234
  }
234
- const { spawn } = await import("child_process");
235
- const pythonCmd = process.platform === "win32" ? "python" : "python3";
236
- return new Promise((resolve, reject) => {
237
- const child = spawn(pythonCmd, ["-m", "letsfg.cli", "star", "--github", github], {
238
- stdio: ["inherit", "pipe", "pipe"]
239
- });
240
- let stdout = "";
241
- let stderr = "";
242
- child.stdout.on("data", (d) => {
243
- stdout += d.toString();
244
- });
245
- child.stderr.on("data", (d) => {
246
- stderr += d.toString();
247
- });
248
- child.on("close", (code) => {
249
- if (jsonOut) {
250
- const verified = code === 0;
251
- console.log(JSON.stringify({ status: verified ? "verified" : "star_required", github_username: github }));
252
- } else {
253
- if (stdout) console.log(stdout);
254
- if (stderr && code !== 0) console.error(stderr);
255
- }
256
- if (code !== 0) {
257
- process.exit(1);
258
- }
259
- resolve();
260
- });
261
- child.on("error", (err) => {
262
- console.error(`Cannot start Python: ${err.message}`);
263
- console.error("Install: pip install letsfg");
264
- process.exit(1);
265
- });
266
- });
235
+ const bt = new LetsFG({ apiKey, baseUrl });
236
+ const result = await bt.linkGithub(github);
237
+ if (jsonOut) {
238
+ console.log(JSON.stringify(result, null, 2));
239
+ return;
240
+ }
241
+ const status = result.status;
242
+ if (status === "verified") {
243
+ console.log(`
244
+ \u2713 GitHub star verified! Unlimited access granted.`);
245
+ console.log(` Username: ${result.github_username}`);
246
+ console.log(`
247
+ You're all set \u2014 search, unlock, and book for free.
248
+ `);
249
+ } else if (status === "already_verified") {
250
+ console.log(`
251
+ \u2713 Already verified! You have unlimited access.`);
252
+ console.log(` Username: ${result.github_username}
253
+ `);
254
+ } else if (status === "star_required") {
255
+ console.log(`
256
+ \u2717 Star not found for '${github}'.`);
257
+ console.log(` 1. Star the repo: https://github.com/LetsFG/LetsFG`);
258
+ console.log(` 2. Run this command again.
259
+ `);
260
+ } else {
261
+ console.error(` \u2717 Unexpected status: ${status}`);
262
+ process.exit(1);
263
+ }
267
264
  }
268
265
  async function cmdSetupPayment(args) {
269
266
  const jsonOut = hasFlag(args, "--json") || hasFlag(args, "-j");
@@ -321,19 +318,16 @@ var HELP = `
321
318
  LetsFG \u2014 Agent-native flight search & booking.
322
319
 
323
320
  Search 400+ airlines at raw airline prices \u2014 $20-50 cheaper than OTAs.
324
-
325
- **GitHub Star Required!**
326
- Before using, you must:
327
- 1. Star https://github.com/LetsFG/LetsFG
328
- 2. Run: letsfg star --github <your-username>
321
+ 100% FREE \u2014 just star our GitHub repo for unlimited access.
329
322
 
330
323
  Commands:
331
- star --github <username> Verify GitHub star (required first!)
332
- search <origin> <dest> <date> Search for flights (FREE with star)
324
+ search <origin> <dest> <date> Search for flights (FREE)
333
325
  locations <query> Resolve city name to IATA codes
334
- unlock <offer_id> Unlock offer (hosted mode only)
335
- book <offer_id> --passenger ... Book flight
326
+ star --github <username> Link GitHub \u2014 star repo for free access
327
+ unlock <offer_id> Unlock offer (FREE with GitHub star)
328
+ book <offer_id> --passenger ... Book flight (FREE after unlock)
336
329
  register --name ... --email ... Register new agent
330
+ setup-payment Legacy payment setup
337
331
  me Show agent profile
338
332
 
339
333
  Options:
@@ -342,6 +336,7 @@ Options:
342
336
  --base-url API URL (default: https://api.letsfg.co)
343
337
 
344
338
  Examples:
339
+ letsfg register --name my-agent --email me@example.com
345
340
  letsfg star --github octocat
346
341
  letsfg search GDN BER 2026-03-03 --sort price
347
342
  letsfg unlock off_xxx
@@ -388,17 +383,6 @@ async function main() {
388
383
  process.exit(1);
389
384
  }
390
385
  } catch (e) {
391
- if (e instanceof StarRequiredError) {
392
- console.error(`
393
- \u2B50 GitHub star required to use LetsFG!
394
- `);
395
- console.error(` 1. Star the repo: https://github.com/LetsFG/LetsFG`);
396
- console.error(` 2. Run: letsfg star --github <your-username>
397
- `);
398
- console.error(` This is completely FREE \u2014 just a star to help us grow!
399
- `);
400
- process.exit(1);
401
- }
402
386
  if (e instanceof LetsFGError) {
403
387
  console.error(`Error: ${e.message}`);
404
388
  process.exit(1);
package/dist/index.d.mts CHANGED
@@ -1,19 +1,14 @@
1
1
  /**
2
2
  * LetsFG — Agent-native flight search & booking SDK for Node.js/TypeScript.
3
3
  *
4
- * 195 airline connectors run locally via Python + backend API for enterprise GDS/NDC sources.
4
+ * 75 airline connectors run locally via Python + backend API for enterprise GDS/NDC sources.
5
5
  * Zero external JS dependencies. Uses native fetch (Node 18+).
6
6
  *
7
- * **GitHub Star Required!**
8
- * Before using, you must:
9
- * 1. Star https://github.com/LetsFG/LetsFG
10
- * 2. Run: letsfg star --github <your-username>
11
- *
12
7
  * @example
13
8
  * ```ts
14
9
  * import { LetsFG, searchLocal } from 'letsfg';
15
10
  *
16
- * // Local search — FREE with GitHub star
11
+ * // Local search — FREE, no API key
17
12
  * const local = await searchLocal('SHA', 'CTU', '2026-03-20');
18
13
  *
19
14
  * // Full API — search + unlock + book
@@ -188,25 +183,12 @@ declare class OfferExpiredError extends LetsFGError {
188
183
  declare class ValidationError extends LetsFGError {
189
184
  constructor(message: string, statusCode?: number, response?: Record<string, unknown>, errorCode?: string);
190
185
  }
191
- /**
192
- * Thrown when GitHub star verification is required.
193
- * Users must star https://github.com/LetsFG/LetsFG and run:
194
- * letsfg star --github <username>
195
- */
196
- declare class StarRequiredError extends LetsFGError {
197
- constructor(message?: string);
198
- }
199
186
  /** One-line offer summary */
200
187
  declare function offerSummary(offer: FlightOffer): string;
201
188
  /** Get cheapest offer from search results */
202
189
  declare function cheapestOffer(result: FlightSearchResult): FlightOffer | null;
203
190
  /**
204
- * Search flights using 195 local airline connectors — FREE with GitHub star!
205
- *
206
- * **GitHub Star Required!**
207
- * Before using, you must:
208
- * 1. Star https://github.com/LetsFG/LetsFG
209
- * 2. Run: letsfg star --github <your-username>
191
+ * Search flights using 73 local airline connectors — FREE, no API key needed.
210
192
  *
211
193
  * Requires: pip install letsfg && playwright install chromium
212
194
  *
@@ -214,7 +196,6 @@ declare function cheapestOffer(result: FlightSearchResult): FlightOffer | null;
214
196
  * @param destination - IATA code (e.g., "CTU")
215
197
  * @param dateFrom - Departure date "YYYY-MM-DD"
216
198
  * @param options - Optional: currency, adults, limit, etc.
217
- * @throws {StarRequiredError} If GitHub star not verified
218
199
  */
219
200
  declare function searchLocal(origin: string, destination: string, dateFrom: string, options?: Partial<SearchOptions>): Promise<FlightSearchResult>;
220
201
  declare class LetsFG {
@@ -224,7 +205,10 @@ declare class LetsFG {
224
205
  constructor(config?: LetsFGConfig);
225
206
  private requireApiKey;
226
207
  /**
227
- * Search for flights — FREE, unlimited.
208
+ * Search for flights — FREE, unlimited, runs locally on your machine.
209
+ *
210
+ * Uses 200 airline connectors via Python subprocess. No backend call.
211
+ * Requires: pip install letsfg && playwright install chromium
228
212
  *
229
213
  * @param origin - IATA code (e.g., "GDN", "LON")
230
214
  * @param destination - IATA code (e.g., "BER", "BCN")
@@ -233,7 +217,7 @@ declare class LetsFG {
233
217
  */
234
218
  search(origin: string, destination: string, dateFrom: string, options?: SearchOptions): Promise<FlightSearchResult>;
235
219
  /**
236
- * Resolve a city/airport name to IATA codes.
220
+ * Resolve a city/airport name to IATA codes — runs locally, no backend call.
237
221
  */
238
222
  resolveLocation(query: string): Promise<Array<Record<string, unknown>>>;
239
223
  /**
@@ -301,4 +285,4 @@ declare const BoostedTravel: typeof LetsFG;
301
285
  declare const BoostedTravelError: typeof LetsFGError;
302
286
  type BoostedTravelConfig = LetsFGConfig;
303
287
 
304
- export { AuthenticationError, type BookingResult, BoostedTravel, type BoostedTravelConfig, BoostedTravelError, type CheckoutProgress, ErrorCategory, type ErrorCategoryType, ErrorCode, type ErrorCodeType, type FlightOffer, type FlightRoute, type FlightSearchResult, type FlightSegment, LetsFG, type LetsFGConfig, LetsFGError, OfferExpiredError, type Passenger, PaymentRequiredError, type SearchOptions, StarRequiredError, type UnlockResult, ValidationError, cheapestOffer, LetsFG as default, systemInfo as getSystemInfo, searchLocal as localSearch, offerSummary, searchLocal, systemInfo };
288
+ export { AuthenticationError, type BookingResult, BoostedTravel, type BoostedTravelConfig, BoostedTravelError, type CheckoutProgress, ErrorCategory, type ErrorCategoryType, ErrorCode, type ErrorCodeType, type FlightOffer, type FlightRoute, type FlightSearchResult, type FlightSegment, LetsFG, type LetsFGConfig, LetsFGError, OfferExpiredError, type Passenger, PaymentRequiredError, type SearchOptions, type UnlockResult, ValidationError, cheapestOffer, LetsFG as default, systemInfo as getSystemInfo, searchLocal as localSearch, offerSummary, searchLocal, systemInfo };
package/dist/index.d.ts CHANGED
@@ -1,19 +1,14 @@
1
1
  /**
2
2
  * LetsFG — Agent-native flight search & booking SDK for Node.js/TypeScript.
3
3
  *
4
- * 195 airline connectors run locally via Python + backend API for enterprise GDS/NDC sources.
4
+ * 75 airline connectors run locally via Python + backend API for enterprise GDS/NDC sources.
5
5
  * Zero external JS dependencies. Uses native fetch (Node 18+).
6
6
  *
7
- * **GitHub Star Required!**
8
- * Before using, you must:
9
- * 1. Star https://github.com/LetsFG/LetsFG
10
- * 2. Run: letsfg star --github <your-username>
11
- *
12
7
  * @example
13
8
  * ```ts
14
9
  * import { LetsFG, searchLocal } from 'letsfg';
15
10
  *
16
- * // Local search — FREE with GitHub star
11
+ * // Local search — FREE, no API key
17
12
  * const local = await searchLocal('SHA', 'CTU', '2026-03-20');
18
13
  *
19
14
  * // Full API — search + unlock + book
@@ -188,25 +183,12 @@ declare class OfferExpiredError extends LetsFGError {
188
183
  declare class ValidationError extends LetsFGError {
189
184
  constructor(message: string, statusCode?: number, response?: Record<string, unknown>, errorCode?: string);
190
185
  }
191
- /**
192
- * Thrown when GitHub star verification is required.
193
- * Users must star https://github.com/LetsFG/LetsFG and run:
194
- * letsfg star --github <username>
195
- */
196
- declare class StarRequiredError extends LetsFGError {
197
- constructor(message?: string);
198
- }
199
186
  /** One-line offer summary */
200
187
  declare function offerSummary(offer: FlightOffer): string;
201
188
  /** Get cheapest offer from search results */
202
189
  declare function cheapestOffer(result: FlightSearchResult): FlightOffer | null;
203
190
  /**
204
- * Search flights using 195 local airline connectors — FREE with GitHub star!
205
- *
206
- * **GitHub Star Required!**
207
- * Before using, you must:
208
- * 1. Star https://github.com/LetsFG/LetsFG
209
- * 2. Run: letsfg star --github <your-username>
191
+ * Search flights using 73 local airline connectors — FREE, no API key needed.
210
192
  *
211
193
  * Requires: pip install letsfg && playwright install chromium
212
194
  *
@@ -214,7 +196,6 @@ declare function cheapestOffer(result: FlightSearchResult): FlightOffer | null;
214
196
  * @param destination - IATA code (e.g., "CTU")
215
197
  * @param dateFrom - Departure date "YYYY-MM-DD"
216
198
  * @param options - Optional: currency, adults, limit, etc.
217
- * @throws {StarRequiredError} If GitHub star not verified
218
199
  */
219
200
  declare function searchLocal(origin: string, destination: string, dateFrom: string, options?: Partial<SearchOptions>): Promise<FlightSearchResult>;
220
201
  declare class LetsFG {
@@ -224,7 +205,10 @@ declare class LetsFG {
224
205
  constructor(config?: LetsFGConfig);
225
206
  private requireApiKey;
226
207
  /**
227
- * Search for flights — FREE, unlimited.
208
+ * Search for flights — FREE, unlimited, runs locally on your machine.
209
+ *
210
+ * Uses 200 airline connectors via Python subprocess. No backend call.
211
+ * Requires: pip install letsfg && playwright install chromium
228
212
  *
229
213
  * @param origin - IATA code (e.g., "GDN", "LON")
230
214
  * @param destination - IATA code (e.g., "BER", "BCN")
@@ -233,7 +217,7 @@ declare class LetsFG {
233
217
  */
234
218
  search(origin: string, destination: string, dateFrom: string, options?: SearchOptions): Promise<FlightSearchResult>;
235
219
  /**
236
- * Resolve a city/airport name to IATA codes.
220
+ * Resolve a city/airport name to IATA codes — runs locally, no backend call.
237
221
  */
238
222
  resolveLocation(query: string): Promise<Array<Record<string, unknown>>>;
239
223
  /**
@@ -301,4 +285,4 @@ declare const BoostedTravel: typeof LetsFG;
301
285
  declare const BoostedTravelError: typeof LetsFGError;
302
286
  type BoostedTravelConfig = LetsFGConfig;
303
287
 
304
- export { AuthenticationError, type BookingResult, BoostedTravel, type BoostedTravelConfig, BoostedTravelError, type CheckoutProgress, ErrorCategory, type ErrorCategoryType, ErrorCode, type ErrorCodeType, type FlightOffer, type FlightRoute, type FlightSearchResult, type FlightSegment, LetsFG, type LetsFGConfig, LetsFGError, OfferExpiredError, type Passenger, PaymentRequiredError, type SearchOptions, StarRequiredError, type UnlockResult, ValidationError, cheapestOffer, LetsFG as default, systemInfo as getSystemInfo, searchLocal as localSearch, offerSummary, searchLocal, systemInfo };
288
+ export { AuthenticationError, type BookingResult, BoostedTravel, type BoostedTravelConfig, BoostedTravelError, type CheckoutProgress, ErrorCategory, type ErrorCategoryType, ErrorCode, type ErrorCodeType, type FlightOffer, type FlightRoute, type FlightSearchResult, type FlightSegment, LetsFG, type LetsFGConfig, LetsFGError, OfferExpiredError, type Passenger, PaymentRequiredError, type SearchOptions, type UnlockResult, ValidationError, cheapestOffer, LetsFG as default, systemInfo as getSystemInfo, searchLocal as localSearch, offerSummary, searchLocal, systemInfo };
package/dist/index.js CHANGED
@@ -39,7 +39,6 @@ __export(index_exports, {
39
39
  LetsFGError: () => LetsFGError,
40
40
  OfferExpiredError: () => OfferExpiredError,
41
41
  PaymentRequiredError: () => PaymentRequiredError,
42
- StarRequiredError: () => StarRequiredError,
43
42
  ValidationError: () => ValidationError,
44
43
  cheapestOffer: () => cheapestOffer,
45
44
  default: () => index_default,
@@ -157,12 +156,6 @@ var ValidationError = class extends LetsFGError {
157
156
  this.name = "ValidationError";
158
157
  }
159
158
  };
160
- var StarRequiredError = class extends LetsFGError {
161
- constructor(message = "GitHub star required. Run: letsfg star --github <your-username>") {
162
- super(message, 403, {}, "STAR_REQUIRED");
163
- this.name = "StarRequiredError";
164
- }
165
- };
166
159
  function routeStr(route) {
167
160
  if (!route.segments.length) return "";
168
161
  const codes = [route.segments[0].origin, ...route.segments.map((s) => s.destination)];
@@ -213,13 +206,8 @@ async function searchLocal(origin, destination, dateFrom, options = {}) {
213
206
  child.on("close", (code) => {
214
207
  try {
215
208
  const data = JSON.parse(stdout);
216
- if (data.error) {
217
- if (data.error.includes("GitHub star required") || data.error.includes("star required")) {
218
- reject(new StarRequiredError(data.error));
219
- } else {
220
- reject(new LetsFGError(data.error));
221
- }
222
- } else resolve(data);
209
+ if (data.error) reject(new LetsFGError(data.error));
210
+ else resolve(data);
223
211
  } catch {
224
212
  reject(new LetsFGError(
225
213
  `Python search failed (code ${code}): ${stdout || stderr}
@@ -256,7 +244,10 @@ var LetsFG = class {
256
244
  }
257
245
  // ── Core methods ─────────────────────────────────────────────────────
258
246
  /**
259
- * Search for flights — FREE, unlimited.
247
+ * Search for flights — FREE, unlimited, runs locally on your machine.
248
+ *
249
+ * Uses 200 airline connectors via Python subprocess. No backend call.
250
+ * Requires: pip install letsfg && playwright install chromium
260
251
  *
261
252
  * @param origin - IATA code (e.g., "GDN", "LON")
262
253
  * @param destination - IATA code (e.g., "BER", "BCN")
@@ -264,32 +255,44 @@ var LetsFG = class {
264
255
  * @param options - Optional search parameters
265
256
  */
266
257
  async search(origin, destination, dateFrom, options = {}) {
267
- this.requireApiKey();
268
- const body = {
269
- origin: origin.toUpperCase(),
270
- destination: destination.toUpperCase(),
271
- date_from: dateFrom,
272
- adults: options.adults ?? 1,
273
- children: options.children ?? 0,
274
- infants: options.infants ?? 0,
275
- max_stopovers: options.maxStopovers ?? 2,
276
- currency: options.currency ?? "EUR",
277
- limit: options.limit ?? 20,
278
- sort: options.sort ?? "price"
279
- };
280
- if (options.returnDate) body.return_from = options.returnDate;
281
- if (options.cabinClass) body.cabin_class = options.cabinClass;
282
- return this.post("/api/v1/flights/search", body);
258
+ return searchLocal(origin, destination, dateFrom, options);
283
259
  }
284
260
  /**
285
- * Resolve a city/airport name to IATA codes.
261
+ * Resolve a city/airport name to IATA codes — runs locally, no backend call.
286
262
  */
287
263
  async resolveLocation(query) {
288
- this.requireApiKey();
289
- const data = await this.get(`/api/v1/flights/locations/${encodeURIComponent(query)}`);
290
- if (Array.isArray(data)) return data;
291
- if (data && Array.isArray(data.locations)) return data.locations;
292
- return data ? [data] : [];
264
+ const { spawn } = await import("child_process");
265
+ const params = JSON.stringify({ __resolve_location: true, query });
266
+ return new Promise((resolve, reject) => {
267
+ const pythonCmd = process.platform === "win32" ? "python" : "python3";
268
+ const child = spawn(pythonCmd, ["-m", "letsfg.local"], {
269
+ stdio: ["pipe", "pipe", "pipe"]
270
+ });
271
+ let stdout = "";
272
+ let stderr = "";
273
+ child.stdout.on("data", (d) => {
274
+ stdout += d.toString();
275
+ });
276
+ child.stderr.on("data", (d) => {
277
+ stderr += d.toString();
278
+ });
279
+ child.on("close", (code) => {
280
+ try {
281
+ const data = JSON.parse(stdout);
282
+ if (data.error) reject(new LetsFGError(data.error));
283
+ else resolve(Array.isArray(data) ? data : data.locations || [data]);
284
+ } catch {
285
+ reject(new LetsFGError(
286
+ `Location resolution failed (code ${code}): ${stdout || stderr}`
287
+ ));
288
+ }
289
+ });
290
+ child.on("error", (err) => {
291
+ reject(new LetsFGError(`Cannot start Python: ${err.message}`));
292
+ });
293
+ child.stdin.write(params);
294
+ child.stdin.end();
295
+ });
293
296
  }
294
297
  /**
295
298
  * Unlock a flight offer — FREE with GitHub star.
@@ -453,6 +456,7 @@ var LetsFG = class {
453
456
  "Content-Type": "application/json",
454
457
  "X-API-Key": this.apiKey,
455
458
  "User-Agent": "LetsFG-js/0.1.0",
459
+ "X-Client-Type": "js-sdk",
456
460
  ...init.headers || {}
457
461
  },
458
462
  signal: controller.signal
@@ -523,7 +527,6 @@ var BoostedTravelError = LetsFGError;
523
527
  LetsFGError,
524
528
  OfferExpiredError,
525
529
  PaymentRequiredError,
526
- StarRequiredError,
527
530
  ValidationError,
528
531
  cheapestOffer,
529
532
  getSystemInfo,
package/dist/index.mjs CHANGED
@@ -8,14 +8,13 @@ import {
8
8
  LetsFGError,
9
9
  OfferExpiredError,
10
10
  PaymentRequiredError,
11
- StarRequiredError,
12
11
  ValidationError,
13
12
  cheapestOffer,
14
13
  index_default,
15
14
  offerSummary,
16
15
  searchLocal,
17
16
  systemInfo
18
- } from "./chunk-7LYR3CHE.mjs";
17
+ } from "./chunk-RLP7MSF6.mjs";
19
18
  export {
20
19
  AuthenticationError,
21
20
  BoostedTravel,
@@ -26,7 +25,6 @@ export {
26
25
  LetsFGError,
27
26
  OfferExpiredError,
28
27
  PaymentRequiredError,
29
- StarRequiredError,
30
28
  ValidationError,
31
29
  cheapestOffer,
32
30
  index_default as default,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "letsfg",
3
- "version": "1.4.2",
4
- "description": "Agent-native flight search & booking. 195 airline connectors run locally + enterprise GDS/NDC APIs. Built for autonomous AI agents.",
3
+ "version": "1.7.0",
4
+ "description": "Agent-native flight search & booking. 200 airline connectors run locally + enterprise GDS/NDC APIs. Built for autonomous AI agents.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
7
7
  "types": "dist/index.d.ts",