fortrip 0.2.1 → 0.3.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/AGENT_GUIDE.md +14 -9
- package/README.md +3 -0
- package/dist/cli.js +28 -8
- package/package.json +1 -1
package/AGENT_GUIDE.md
CHANGED
|
@@ -58,9 +58,12 @@ token into the chat.
|
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
Use `--near lat,lng,radiusKm` for "near X", `--city-code` when known.
|
|
61
|
+
Pass `--currency` (`CNY`, the default, or `USD`) to suit the user, inferred
|
|
62
|
+
from their language or location, on both `search` and `rates`;
|
|
63
|
+
`--price-min` / `--price-max` are in that currency.
|
|
61
64
|
`--facility` names must match the catalogue exactly (e.g. `Swimming pool`,
|
|
62
65
|
`Parking`), otherwise nothing matches. Prices here (`price.nightlyFrom`,
|
|
63
|
-
`totalFrom
|
|
66
|
+
`totalFrom`) are **indicative floors**, not quotes — say so.
|
|
64
67
|
|
|
65
68
|
2. **Hotel detail** (optional) — description, policies, facilities, photos:
|
|
66
69
|
|
|
@@ -73,11 +76,13 @@ token into the chat.
|
|
|
73
76
|
```bash
|
|
74
77
|
fortrip rates --hotel 266000 --hotel 266001 --check-in 2026-11-10 \
|
|
75
78
|
--check-out 2026-11-12 --rooms 1 --adults 2 [--child-age 6] \
|
|
76
|
-
[--nationality CN] [--board BB] [--free-cancellation]
|
|
79
|
+
[--nationality CN] [--board BB] [--free-cancellation] [--currency USD]
|
|
77
80
|
```
|
|
78
81
|
|
|
79
|
-
This call takes about 15 seconds.
|
|
80
|
-
rate (refreshed hourly)
|
|
82
|
+
This call takes about 15 seconds. Prices are in the response's `currency`,
|
|
83
|
+
converted at Stripe's exchange rate (refreshed hourly), or a fixed fallback
|
|
84
|
+
rate when Stripe is unavailable; `fx` says which, and is `null` when
|
|
85
|
+
nothing needed converting. Present each option with meal plan (`boardCode`: RO room only,
|
|
81
86
|
BB breakfast, HB half board, FB full board, AI all inclusive),
|
|
82
87
|
`freeCancelUntil` / `refundable`, `isInstantConfirm`, and any `fees`
|
|
83
88
|
(taxes paid at the hotel, in their own currency).
|
|
@@ -106,11 +111,11 @@ token into the chat.
|
|
|
106
111
|
(`boardCode`, `boardCount`), cancellation (`refundable`, `freeCancelUntil`,
|
|
107
112
|
the `cancellation` ladder), confirmation (`isInstantConfirm`,
|
|
108
113
|
`confirmByMins`) and `payAtHotelFees`.
|
|
109
|
-
2. **Explain the amount.** `rates` showed
|
|
110
|
-
`currency` (usually USD), and the Stripe page may display
|
|
111
|
-
currency at Stripe's rate. The
|
|
112
|
-
usually close, but the amount on the payment page
|
|
113
|
-
the
|
|
114
|
+
2. **Explain the amount.** `rates` showed a converted figure, but the charge
|
|
115
|
+
is `amount` in `currency` (usually USD), and the Stripe page may display
|
|
116
|
+
the user's local currency at Stripe's rate. The `rates` figure used a
|
|
117
|
+
Stripe rate too, so it is usually close, but the amount on the payment page
|
|
118
|
+
is what will be charged; the `rates` figure was a reference.
|
|
114
119
|
3. The link is for **this user only**: whoever has it can pay. Don't post it
|
|
115
120
|
in shared channels, logs or tickets. It expires after about 3 hours 45
|
|
116
121
|
minutes; call `book` again with the same details for a fresh one.
|
package/README.md
CHANGED
|
@@ -48,6 +48,9 @@ Output is JSON on stdout. Errors are JSON on stderr, with fixed exit codes:
|
|
|
48
48
|
- `FORTRIP_TOKEN` overrides the saved login (CI, remote sandboxes).
|
|
49
49
|
- `FORTRIP_API_URL` overrides the base URL (default `https://fortrip.ai`).
|
|
50
50
|
Plain `http` is only accepted for `localhost`.
|
|
51
|
+
- `FORTRIP_CURRENCY` sets the default `--currency` for `search` and `rates`
|
|
52
|
+
(`CNY` or `USD`; the API defaults to `CNY`). Display only: `book`
|
|
53
|
+
charges the currency it returns.
|
|
51
54
|
|
|
52
55
|
Tokens don't expire; `logout` revokes them.
|
|
53
56
|
|
package/dist/cli.js
CHANGED
|
@@ -102,6 +102,7 @@ function unwrap(response) {
|
|
|
102
102
|
import { readFileSync as readFileSync2 } from "fs";
|
|
103
103
|
import { parseArgs } from "util";
|
|
104
104
|
var BODY_OPTION = { body: { type: "string" } };
|
|
105
|
+
var CURRENCY_OPTION = { currency: { type: "string" } };
|
|
105
106
|
function parse(argv, options) {
|
|
106
107
|
try {
|
|
107
108
|
return parseArgs({ args: argv, options, allowPositionals: true, strict: true });
|
|
@@ -147,6 +148,13 @@ function str(value) {
|
|
|
147
148
|
function list(value) {
|
|
148
149
|
return Array.isArray(value) && value.length ? value : void 0;
|
|
149
150
|
}
|
|
151
|
+
function currency(values, base, env) {
|
|
152
|
+
const flag = str(values.currency)?.trim().toUpperCase();
|
|
153
|
+
if (flag === "") throw usageError("--currency must not be empty");
|
|
154
|
+
if (flag) return flag;
|
|
155
|
+
if (base.currency !== void 0) return void 0;
|
|
156
|
+
return env.FORTRIP_CURRENCY?.trim().toUpperCase() || void 0;
|
|
157
|
+
}
|
|
150
158
|
function merge(base, overrides) {
|
|
151
159
|
const body = { ...base };
|
|
152
160
|
for (const [key, value] of Object.entries(overrides)) {
|
|
@@ -156,6 +164,7 @@ function merge(base, overrides) {
|
|
|
156
164
|
}
|
|
157
165
|
var SEARCH_OPTIONS = {
|
|
158
166
|
...BODY_OPTION,
|
|
167
|
+
...CURRENCY_OPTION,
|
|
159
168
|
query: { type: "string", short: "q" },
|
|
160
169
|
"city-code": { type: "string" },
|
|
161
170
|
near: { type: "string" },
|
|
@@ -170,7 +179,8 @@ var SEARCH_OPTIONS = {
|
|
|
170
179
|
page: { type: "string" },
|
|
171
180
|
"page-size": { type: "string" }
|
|
172
181
|
};
|
|
173
|
-
function searchBody(values) {
|
|
182
|
+
function searchBody(values, env = process.env) {
|
|
183
|
+
const base = baseBody(values);
|
|
174
184
|
let near;
|
|
175
185
|
const nearRaw = str(values.near);
|
|
176
186
|
if (nearRaw !== void 0) {
|
|
@@ -184,7 +194,7 @@ function searchBody(values) {
|
|
|
184
194
|
radiusKm: num("near", radiusKm)
|
|
185
195
|
};
|
|
186
196
|
}
|
|
187
|
-
return merge(
|
|
197
|
+
return merge(base, {
|
|
188
198
|
query: str(values.query),
|
|
189
199
|
cityCode: num("city-code", str(values["city-code"])),
|
|
190
200
|
near,
|
|
@@ -197,7 +207,8 @@ function searchBody(values) {
|
|
|
197
207
|
priceMax: num("price-max", str(values["price-max"])),
|
|
198
208
|
sortBy: str(values.sort),
|
|
199
209
|
page: num("page", str(values.page)),
|
|
200
|
-
pageSize: num("page-size", str(values["page-size"]))
|
|
210
|
+
pageSize: num("page-size", str(values["page-size"])),
|
|
211
|
+
currency: currency(values, base, env)
|
|
201
212
|
});
|
|
202
213
|
}
|
|
203
214
|
var HOTEL_OPTIONS = {
|
|
@@ -228,6 +239,7 @@ function stayFields(values, base) {
|
|
|
228
239
|
}
|
|
229
240
|
var RATES_OPTIONS = {
|
|
230
241
|
...BODY_OPTION,
|
|
242
|
+
...CURRENCY_OPTION,
|
|
231
243
|
...STAY_OPTIONS,
|
|
232
244
|
hotel: { type: "string", multiple: true },
|
|
233
245
|
nationality: { type: "string" },
|
|
@@ -236,7 +248,7 @@ var RATES_OPTIONS = {
|
|
|
236
248
|
"include-unbookable": { type: "boolean" },
|
|
237
249
|
"max-rooms": { type: "string" }
|
|
238
250
|
};
|
|
239
|
-
function ratesBody(values) {
|
|
251
|
+
function ratesBody(values, env = process.env) {
|
|
240
252
|
const base = baseBody(values);
|
|
241
253
|
return merge(base, {
|
|
242
254
|
hotelIds: nums("hotel", list(values.hotel)),
|
|
@@ -247,7 +259,8 @@ function ratesBody(values) {
|
|
|
247
259
|
),
|
|
248
260
|
freeCancellation: values["free-cancellation"] ? true : void 0,
|
|
249
261
|
bookableOnly: values["include-unbookable"] ? false : void 0,
|
|
250
|
-
maxRoomsPerHotel: num("max-rooms", str(values["max-rooms"]))
|
|
262
|
+
maxRoomsPerHotel: num("max-rooms", str(values["max-rooms"])),
|
|
263
|
+
currency: currency(values, base, env)
|
|
251
264
|
});
|
|
252
265
|
}
|
|
253
266
|
var BOOK_OPTIONS = {
|
|
@@ -538,9 +551,9 @@ Commands:
|
|
|
538
551
|
login [--no-browser] Sign in through the browser (device code)
|
|
539
552
|
logout Revoke and forget the saved token
|
|
540
553
|
whoami Show the signed-in account
|
|
541
|
-
search Find hotels with indicative prices
|
|
554
|
+
search Find hotels with indicative prices
|
|
542
555
|
hotel <hotelId> Static hotel detail [--with-rooms]
|
|
543
|
-
rates Live rooms and rates
|
|
556
|
+
rates Live rooms and rates for up to 15 hotels
|
|
544
557
|
book Create an order and get a payment link
|
|
545
558
|
skill [planning] Print the guide for AI agents (Markdown)
|
|
546
559
|
|
|
@@ -548,10 +561,11 @@ search: --query <text> | --city-code <n> | --near <lat,lng,km>
|
|
|
548
561
|
--check-in YYYY-MM-DD --check-out YYYY-MM-DD
|
|
549
562
|
[--star-min n] [--star-max n] [--facility <name>]... [--price-min n]
|
|
550
563
|
[--price-max n] [--sort recommended|price-asc|price-desc|star-desc|distance]
|
|
551
|
-
[--page n] [--page-size n]
|
|
564
|
+
[--page n] [--page-size n] [--currency CNY|USD]
|
|
552
565
|
rates: --hotel <id>... --check-in --check-out [--rooms 1] [--adults 2]
|
|
553
566
|
[--child-age n]... [--nationality CN] [--board BB]...
|
|
554
567
|
[--free-cancellation] [--include-unbookable] [--max-rooms n]
|
|
568
|
+
[--currency CNY|USD]
|
|
555
569
|
book: --hotel <id> --room-id <id> --rate-plan <code> --check-in --check-out
|
|
556
570
|
[--rooms 1] [--adults 2] [--child-age n]...
|
|
557
571
|
--guest "First Last[:roomNo]"... --contact-name <name>
|
|
@@ -561,12 +575,18 @@ book: --hotel <id> --room-id <id> --rate-plan <code> --check-in --check-out
|
|
|
561
575
|
search, rates and book also take --body '<json>' (or --body @file.json) with
|
|
562
576
|
the raw request body; flags override its fields.
|
|
563
577
|
|
|
578
|
+
search and rates ask for prices in --currency (CNY by default, or USD),
|
|
579
|
+
converted at Stripe's rate or a fixed fallback rate; the response's currency
|
|
580
|
+
and fx fields say which. book still charges the currency it returns (usually
|
|
581
|
+
USD).
|
|
582
|
+
|
|
564
583
|
Output is JSON on stdout; errors are JSON on stderr.
|
|
565
584
|
Exit codes: 0 ok, 1 error, 2 invalid arguments, 3 not logged in, 4 rate limited.
|
|
566
585
|
|
|
567
586
|
Environment:
|
|
568
587
|
FORTRIP_TOKEN Use this token instead of the saved login (CI, sandboxes)
|
|
569
588
|
FORTRIP_API_URL API base URL (default https://fortrip.ai)
|
|
589
|
+
FORTRIP_CURRENCY Default --currency for search and rates
|
|
570
590
|
`;
|
|
571
591
|
async function run(argv) {
|
|
572
592
|
const [command, ...rest] = argv;
|