@fortrip/agent-contract 0.0.0-stage → 1.0.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/LICENSE +21 -0
- package/README.md +38 -2
- package/dist/chunk-HR6L3RDB.js +18 -0
- package/dist/guest-name.d.ts +71 -0
- package/dist/guest-name.js +14 -0
- package/dist/index.d.ts +422 -0
- package/dist/index.js +1067 -0
- package/package.json +45 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 FortripEngineering
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,39 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @fortrip/agent-contract
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The contract of Fortrip's agent API, `/api/agent/v1`: the limits, endpoint
|
|
4
|
+
paths, error reasons and field rules that the Fortrip CLI (`fortrip`), the
|
|
5
|
+
hosted MCP server and the store listings all share, so they cannot drift
|
|
6
|
+
apart.
|
|
7
|
+
|
|
8
|
+
It has one peer dependency, `zod` (`^4.4.0`), and runs anywhere
|
|
9
|
+
ES2023 does. The package version follows the API: `1.x` serves
|
|
10
|
+
`/api/agent/v1`.
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { API, LIMITS, containsCardNumber } from "@fortrip/agent-contract";
|
|
14
|
+
|
|
15
|
+
API.search; // "/api/agent/v1/hotels/search"
|
|
16
|
+
LIMITS.rooms; // 8
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
It exports:
|
|
20
|
+
|
|
21
|
+
- `API`, `LIMITS`, `BOARD_CODES`, `CURRENCIES`, `SEARCH_SORTS` and
|
|
22
|
+
`ERROR_REASONS`: paths, limits and error reasons;
|
|
23
|
+
- `searchHotelsInput`, `getHotelInput`, `getRatesInput` and
|
|
24
|
+
`createBookingInput`, the zod schemas of the requests, and `bookingResult`,
|
|
25
|
+
`meResult` and `authConfig`, those of responses;
|
|
26
|
+
- `COMMANDS` and `GLOBAL_FLAGS`, the CLI's commands and flags, and `TOOLS`,
|
|
27
|
+
the MCP tools;
|
|
28
|
+
- `renderGuide(surface, topic)`, the agent guide as Markdown for `"mcp"`,
|
|
29
|
+
`"claude-skill"` or `"cli"`;
|
|
30
|
+
- field rules such as `containsCardNumber`, the guest-name rule and the date
|
|
31
|
+
helpers, and `CONTRACT_VERSION`.
|
|
32
|
+
|
|
33
|
+
Browser code that only needs the guest-name rule (`GUEST_NAME_PATTERN`,
|
|
34
|
+
`GUEST_NAME_TITLE`, `GUEST_NAME_MESSAGE`, `guestNameRe`, `normalizeGuestName`)
|
|
35
|
+
can import it from `@fortrip/agent-contract/guest-name`. That module imports
|
|
36
|
+
no package, so it does not pull in `zod` or the schemas the root entry builds
|
|
37
|
+
at load. The root entry exports the same names.
|
|
38
|
+
|
|
39
|
+
MIT licensed. Source: <https://github.com/FortripEngineering/fortrip/tree/main/packages/contract>.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// src/guest-name.ts
|
|
2
|
+
var CURLY_APOSTROPHES = /[‘’]/g;
|
|
3
|
+
var CORE = "[A-Za-z][A-Za-z '\u2018\u2019.\\-]*";
|
|
4
|
+
var GUEST_NAME_PATTERN = `\\s*|\\s*${CORE}\\s*`;
|
|
5
|
+
var guestNameRe = new RegExp(`^${CORE}$`);
|
|
6
|
+
var GUEST_NAME_TITLE = "Latin letters as in the passport \u2014 start with a letter; spaces, hyphens, apostrophes and periods are fine after that. Transliterate accents (M\xFCller \u2192 Mueller).";
|
|
7
|
+
var GUEST_NAME_MESSAGE = "Guest names must use Latin letters as on the passport (e.g. ZHANG, Wei; M\xFCller \u2192 Mueller).";
|
|
8
|
+
function normalizeGuestName(value) {
|
|
9
|
+
return value.replace(CURLY_APOSTROPHES, "'").replace(/\s+/g, " ").trim();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
GUEST_NAME_PATTERN,
|
|
14
|
+
guestNameRe,
|
|
15
|
+
GUEST_NAME_TITLE,
|
|
16
|
+
GUEST_NAME_MESSAGE,
|
|
17
|
+
normalizeGuestName
|
|
18
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a guest name may contain.
|
|
3
|
+
*
|
|
4
|
+
* The old rule was `[A-Za-z\s-]+`, carrying the comment "the supplier only
|
|
5
|
+
* accepts Latin guest names". The supplier's booking endpoint actually types
|
|
6
|
+
* `firstName` / `lastName` as bare strings with NO character constraint —
|
|
7
|
+
* Latin is a convention its examples follow, not a documented rule. Evidence
|
|
8
|
+
* the old rule was never verified: `contactName`, which travels the same
|
|
9
|
+
* request, has never been pattern-checked at all.
|
|
10
|
+
*
|
|
11
|
+
* So the apostrophe and the period are back: `O'Brien`, `D'Angelo`,
|
|
12
|
+
* `St. John`, `Jr.` are ordinary passport spellings that we were rejecting
|
|
13
|
+
* before the supplier ever saw them. Curly apostrophes come in from phone
|
|
14
|
+
* keyboards and normalize to straight ones rather than being refused.
|
|
15
|
+
*
|
|
16
|
+
* Still refused: CJK, other non-Latin scripts, and accented Latin (José,
|
|
17
|
+
* Müller). Be clear about whose rule this is — **ours**. The supplier accepts
|
|
18
|
+
* CJK names outright: `伟`/`张` booked successfully against its sandbox on
|
|
19
|
+
* 2026-08-07. The old comment here claimed "the supplier only accepts Latin
|
|
20
|
+
* guest names" and that was simply false, in whole, not just in the
|
|
21
|
+
* apostrophe it also got wrong.
|
|
22
|
+
*
|
|
23
|
+
* We keep the restriction for a reason the supplier API can't speak to: the
|
|
24
|
+
* property reads these names at check-in, most of them can't render CJK, and
|
|
25
|
+
* passports carry an ASCII transliteration in the machine-readable zone (JOSE,
|
|
26
|
+
* MUELLER) — that is the spelling a front desk matches. So the model
|
|
27
|
+
* transliterates and the form asks the user to. If that trade is ever
|
|
28
|
+
* revisited, revisit it as a product decision about hotels, not as "restoring
|
|
29
|
+
* supplier parity" — parity already permits CJK.
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* HTML `pattern` body — implicitly anchored by the browser, and applied to the
|
|
33
|
+
* RAW field value, which is why it is looser than `guestNameRe` rather than
|
|
34
|
+
* identical to it.
|
|
35
|
+
*
|
|
36
|
+
* Two things force the slack. Browsers skip `pattern` only when a value is the
|
|
37
|
+
* empty string, so `" "` is validated — and a `CORE`-only pattern would
|
|
38
|
+
* refuse it. That breaks the reveal affordance outright: a user who opens a
|
|
39
|
+
* row, types a name, then deletes it leaving one stray space has no Remove
|
|
40
|
+
* button and no way forward, stranded on the payment screen by the very
|
|
41
|
+
* mechanism that was supposed to be the undo. Allowing an all-blank value
|
|
42
|
+
* hands that row to `collectGuestsFromForm`, which drops it — the undo works.
|
|
43
|
+
* Surrounding whitespace is allowed for the same reason a pasted `" Wei"`
|
|
44
|
+
* should not be rejected in the browser when `normalizeGuestName` would trim it
|
|
45
|
+
* server-side anyway (the old `[A-Za-z\s\-]+` accepted it; refusing it here
|
|
46
|
+
* would be a regression).
|
|
47
|
+
*
|
|
48
|
+
* The cost is deliberate: a required lead row holding only spaces passes the
|
|
49
|
+
* browser and is caught server-side ("Room 1 needs at least one guest name.").
|
|
50
|
+
* That trade favours the common case — stray whitespace while erasing — over
|
|
51
|
+
* the rare one, someone typing nothing but spaces into a required field.
|
|
52
|
+
*/
|
|
53
|
+
declare const GUEST_NAME_PATTERN = "\\s*|\\s*[A-Za-z][A-Za-z '\u2018\u2019.\\-]*\\s*";
|
|
54
|
+
/** Server-side check, applied AFTER `normalizeGuestName`, so it sees no
|
|
55
|
+
* surrounding whitespace and can be strict. Deliberately not derived from
|
|
56
|
+
* `GUEST_NAME_PATTERN` — they validate different values at different stages.
|
|
57
|
+
* `guest-name.test.ts` pins the relationship that actually matters: anything
|
|
58
|
+
* the browser accepts is, once normalized, either empty or accepted here. */
|
|
59
|
+
declare const guestNameRe: RegExp;
|
|
60
|
+
/** Shown as the `title` on every guest-name input, so a rejected value
|
|
61
|
+
* explains itself. Names the first-character rule because the pattern enforces
|
|
62
|
+
* it and a Dutch `'t Hooft` would otherwise be refused with no explanation. */
|
|
63
|
+
declare const GUEST_NAME_TITLE = "Latin letters as in the passport \u2014 start with a letter; spaces, hyphens, apostrophes and periods are fine after that. Transliterate accents (M\u00FCller \u2192 Mueller).";
|
|
64
|
+
/** The server's answer to a name `guestNameRe` refuses: what to type
|
|
65
|
+
* instead, for a caller that can't see the form's `title`. */
|
|
66
|
+
declare const GUEST_NAME_MESSAGE = "Guest names must use Latin letters as on the passport (e.g. ZHANG, Wei; M\u00FCller \u2192 Mueller).";
|
|
67
|
+
/** Collapse whitespace and straighten curly apostrophes. Applied server-side so
|
|
68
|
+
* both booking surfaces store one spelling of the same name. */
|
|
69
|
+
declare function normalizeGuestName(value: string): string;
|
|
70
|
+
|
|
71
|
+
export { GUEST_NAME_MESSAGE, GUEST_NAME_PATTERN, GUEST_NAME_TITLE, guestNameRe, normalizeGuestName };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
GUEST_NAME_MESSAGE,
|
|
3
|
+
GUEST_NAME_PATTERN,
|
|
4
|
+
GUEST_NAME_TITLE,
|
|
5
|
+
guestNameRe,
|
|
6
|
+
normalizeGuestName
|
|
7
|
+
} from "./chunk-HR6L3RDB.js";
|
|
8
|
+
export {
|
|
9
|
+
GUEST_NAME_MESSAGE,
|
|
10
|
+
GUEST_NAME_PATTERN,
|
|
11
|
+
GUEST_NAME_TITLE,
|
|
12
|
+
guestNameRe,
|
|
13
|
+
normalizeGuestName
|
|
14
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export { GUEST_NAME_MESSAGE, GUEST_NAME_PATTERN, GUEST_NAME_TITLE, guestNameRe, normalizeGuestName } from './guest-name.js';
|
|
3
|
+
|
|
4
|
+
/** Shown on a booking field that seems to hold a payment card number. */
|
|
5
|
+
declare const CARD_NUMBER_MESSAGE = "This looks like a payment card number. Payment happens only on the payment page; if it isn't a card number, rewrite it.";
|
|
6
|
+
/**
|
|
7
|
+
* Whether `text` holds a payment card number in one of the two ways people
|
|
8
|
+
* type one: 13–19 digits in a single run, or groups of four digits (the last
|
|
9
|
+
* 1–4) split by one space or one hyphen. It must start like a card (Mir
|
|
10
|
+
* 2200–2204, Mastercard 2221–2720, 3, 4, 5, 6, RuPay 81/82) and pass the
|
|
11
|
+
* Luhn check. Full-width digits count (NFKC).
|
|
12
|
+
*
|
|
13
|
+
* Deliberately narrow, because every looser rule flagged ordinary text: no
|
|
14
|
+
* joining across `/`, `.`, `_`, line breaks or double spaces (two phone
|
|
15
|
+
* numbers, a list of room numbers, one number per line), and no Amex-style
|
|
16
|
+
* 4-6-5 grouping (missed; a 15-digit Amex in one run is still caught). A
|
|
17
|
+
* card may start at any digit group, so a room number or date in front of it
|
|
18
|
+
* can't hide it. Right after `+` come a phone number's country code and
|
|
19
|
+
* number, so digits there count only from 16 on, too many for a phone.
|
|
20
|
+
*
|
|
21
|
+
* `minDigits` is the shortest card looked for. A phone field passes 16, so a
|
|
22
|
+
* phone isn't mistaken for a card there, at the cost of missing cards of
|
|
23
|
+
* 13–15 digits (Amex).
|
|
24
|
+
*/
|
|
25
|
+
declare function containsCardNumber(text: string, minDigits?: number): boolean;
|
|
26
|
+
|
|
27
|
+
type ToolName = "get_planning_guide" | "search_hotels" | "get_hotel" | "get_rates" | "create_booking";
|
|
28
|
+
interface ToolSpec {
|
|
29
|
+
name: ToolName;
|
|
30
|
+
title: string;
|
|
31
|
+
description: string;
|
|
32
|
+
annotations: {
|
|
33
|
+
readOnlyHint: boolean;
|
|
34
|
+
idempotentHint: boolean;
|
|
35
|
+
openWorldHint: boolean;
|
|
36
|
+
destructiveHint: boolean;
|
|
37
|
+
};
|
|
38
|
+
/** What the tool takes; `null` for a tool without arguments. */
|
|
39
|
+
input: z.ZodType | null;
|
|
40
|
+
/** What it answers with, when that is a schema; `null` otherwise. */
|
|
41
|
+
output: z.ZodType | null;
|
|
42
|
+
}
|
|
43
|
+
/** `getRatesInput` as the MCP tool takes it: the same, with its own default
|
|
44
|
+
* for `maxRoomsPerHotel`. */
|
|
45
|
+
declare const MCP_RATES_INPUT: z.ZodObject<{
|
|
46
|
+
hotelIds: z.ZodArray<z.ZodNumber>;
|
|
47
|
+
checkIn: z.ZodString;
|
|
48
|
+
checkOut: z.ZodString;
|
|
49
|
+
rooms: z.ZodNumber;
|
|
50
|
+
adults: z.ZodNumber;
|
|
51
|
+
childrenAges: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
52
|
+
nationality: z.ZodOptional<z.ZodString>;
|
|
53
|
+
currency: z.ZodDefault<z.ZodEnum<{
|
|
54
|
+
CNY: "CNY";
|
|
55
|
+
USD: "USD";
|
|
56
|
+
}>>;
|
|
57
|
+
bookableOnly: z.ZodDefault<z.ZodBoolean>;
|
|
58
|
+
boardCodes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
59
|
+
RO: "RO";
|
|
60
|
+
BB: "BB";
|
|
61
|
+
HB: "HB";
|
|
62
|
+
FB: "FB";
|
|
63
|
+
BL: "BL";
|
|
64
|
+
BD: "BD";
|
|
65
|
+
HBBL: "HBBL";
|
|
66
|
+
HBLD: "HBLD";
|
|
67
|
+
AI: "AI";
|
|
68
|
+
}>>>;
|
|
69
|
+
freeCancellation: z.ZodOptional<z.ZodBoolean>;
|
|
70
|
+
maxRoomsPerHotel: z.ZodDefault<z.ZodNumber>;
|
|
71
|
+
}, z.core.$strict>;
|
|
72
|
+
/** Rides on every create_booking answer: some clients never show the model
|
|
73
|
+
* the server instructions. */
|
|
74
|
+
declare const BOOKING_NEXT_STEP = "Show the user amount, currency, the stay (hotel, room, dates, rooms, guests per room) and every item in terms (meals, cancellation, confirmation, pay-at-hotel fees), then end your reply by asking them to confirm. Only after they say yes, give them paymentUrl exactly as returned.";
|
|
75
|
+
/** create_booking's answer over MCP: the booking result plus `nextStep`. */
|
|
76
|
+
declare const MCP_BOOKING_RESULT: z.ZodObject<{
|
|
77
|
+
agentOrderId: z.ZodString;
|
|
78
|
+
paymentUrl: z.ZodString;
|
|
79
|
+
amount: z.ZodNullable<z.ZodNumber>;
|
|
80
|
+
currency: z.ZodString;
|
|
81
|
+
orderUrl: z.ZodString;
|
|
82
|
+
statusUrl: z.ZodOptional<z.ZodString>;
|
|
83
|
+
stay: z.ZodObject<{
|
|
84
|
+
hotelId: z.ZodNumber;
|
|
85
|
+
hotelName: z.ZodNullable<z.ZodString>;
|
|
86
|
+
roomName: z.ZodNullable<z.ZodString>;
|
|
87
|
+
checkIn: z.ZodString;
|
|
88
|
+
checkOut: z.ZodString;
|
|
89
|
+
nights: z.ZodNumber;
|
|
90
|
+
rooms: z.ZodNumber;
|
|
91
|
+
adultsPerRoom: z.ZodNumber;
|
|
92
|
+
childrenAgesPerRoom: z.ZodArray<z.ZodNumber>;
|
|
93
|
+
}, z.core.$strip>;
|
|
94
|
+
terms: z.ZodNullable<z.ZodObject<{
|
|
95
|
+
boardCode: z.ZodString;
|
|
96
|
+
boardCount: z.ZodNumber;
|
|
97
|
+
refundable: z.ZodBoolean;
|
|
98
|
+
freeCancelUntil: z.ZodNullable<z.ZodString>;
|
|
99
|
+
cancellation: z.ZodArray<z.ZodObject<{
|
|
100
|
+
amount: z.ZodNumber;
|
|
101
|
+
from: z.ZodString;
|
|
102
|
+
}, z.core.$strip>>;
|
|
103
|
+
isInstantConfirm: z.ZodBoolean;
|
|
104
|
+
confirmByMins: z.ZodNullable<z.ZodNumber>;
|
|
105
|
+
payAtHotelFees: z.ZodArray<z.ZodObject<{
|
|
106
|
+
type: z.ZodString;
|
|
107
|
+
isMandatory: z.ZodBoolean;
|
|
108
|
+
amount: z.ZodNumber;
|
|
109
|
+
currency: z.ZodString;
|
|
110
|
+
}, z.core.$strip>>;
|
|
111
|
+
}, z.core.$strip>>;
|
|
112
|
+
nextStep: z.ZodString;
|
|
113
|
+
}, z.core.$strip>;
|
|
114
|
+
declare const TOOLS: readonly ToolSpec[];
|
|
115
|
+
|
|
116
|
+
/** A flag that sets one field of a tool's input. */
|
|
117
|
+
interface FlagSpec {
|
|
118
|
+
/** The long name without dashes: `check-in` for `--check-in`. */
|
|
119
|
+
flag: string;
|
|
120
|
+
/** The one-letter name without its dash: `q` for `-q`. */
|
|
121
|
+
short?: string;
|
|
122
|
+
/** The input field it sets; dots step into an object (`near.lat`). */
|
|
123
|
+
field: string;
|
|
124
|
+
/**
|
|
125
|
+
* How the CLI reads the value. `id` is digits only, a positive whole
|
|
126
|
+
* number. A `boolean` flag sets its field to true, except where the field
|
|
127
|
+
* defaults to true (`bookableOnly`): `--include-unbookable` sets it to
|
|
128
|
+
* false. A `string` flag whose field is an object (`near`, read as
|
|
129
|
+
* `lat,lng,radiusKm`) or a list of objects (`guests`, read as
|
|
130
|
+
* `First Last[:roomNo]`) is one value the CLI parses into that shape.
|
|
131
|
+
*/
|
|
132
|
+
type: "string" | "number" | "boolean" | "id";
|
|
133
|
+
/** Given once per item; its field is a list. */
|
|
134
|
+
multiple?: boolean;
|
|
135
|
+
/**
|
|
136
|
+
* What the value looks like in help, as it follows the flag name: `<n>`,
|
|
137
|
+
* `YYYY-MM-DD`, `CNY|USD` (an enum's values), `"First Last[:roomNo]"`.
|
|
138
|
+
* Every flag that takes a value has one.
|
|
139
|
+
*/
|
|
140
|
+
placeholder?: string;
|
|
141
|
+
description: string;
|
|
142
|
+
}
|
|
143
|
+
/** An option of the CLI itself that no tool takes: not a field of any input. */
|
|
144
|
+
interface OptionSpec {
|
|
145
|
+
/** The long name without dashes. */
|
|
146
|
+
flag: string;
|
|
147
|
+
/** The one-letter name without its dash. */
|
|
148
|
+
short?: string;
|
|
149
|
+
type: "string" | "boolean";
|
|
150
|
+
/** As on `FlagSpec`; every option that takes a value has one. */
|
|
151
|
+
placeholder?: string;
|
|
152
|
+
description: string;
|
|
153
|
+
}
|
|
154
|
+
interface CommandSpec {
|
|
155
|
+
name: "search" | "hotel" | "rates" | "book" | "login" | "logout" | "whoami" | "guide" | "mcp";
|
|
156
|
+
summary: string;
|
|
157
|
+
/** The MCP tool the command mirrors; `null` for one that is the CLI's own. */
|
|
158
|
+
tool: ToolName | null;
|
|
159
|
+
/** The tool's input fields the command sets. */
|
|
160
|
+
flags: readonly FlagSpec[];
|
|
161
|
+
/** Named after the input field they set, except `guide`'s `topic`. */
|
|
162
|
+
positionals?: readonly string[];
|
|
163
|
+
/** Options that are not input fields, such as `--body`. */
|
|
164
|
+
options?: readonly OptionSpec[];
|
|
165
|
+
}
|
|
166
|
+
declare const COMMANDS: readonly CommandSpec[];
|
|
167
|
+
/** Options of the CLI itself: `--help` after any command, `--version` on its own. */
|
|
168
|
+
declare const GLOBAL_FLAGS: readonly OptionSpec[];
|
|
169
|
+
|
|
170
|
+
/** True only for a real Gregorian calendar date in canonical yyyy-MM-dd form. */
|
|
171
|
+
declare function isIsoDate(value: string): boolean;
|
|
172
|
+
/** Signed number of UTC calendar days in the half-open range [start, end). */
|
|
173
|
+
declare function countIsoDateDays(start: string, end: string): number;
|
|
174
|
+
declare function addIsoDateDays(date: string, days: number): string;
|
|
175
|
+
|
|
176
|
+
/** An object that names the keys it doesn't know instead of dropping them. */
|
|
177
|
+
declare function strictInput<T extends z.core.$ZodLooseShape>(shape: T): z.ZodObject<{ -readonly [P in keyof T]: T[P]; }, z.core.$strict>;
|
|
178
|
+
declare function wholeNumber(name: string, min: number, max: number): z.ZodNumber;
|
|
179
|
+
declare function positiveId(name: string): z.ZodNumber;
|
|
180
|
+
declare function isoDate(name: string): z.ZodString;
|
|
181
|
+
declare function nonEmpty(name: string): z.ZodString;
|
|
182
|
+
declare function text(name: string, min: number, max: number): z.ZodString;
|
|
183
|
+
declare function oneOf<const T extends readonly [string, ...string[]]>(name: string, values: T): z.ZodEnum<{ [k_1 in T[number]]: k_1; } extends infer T_1 ? { [k in keyof T_1]: T_1[k]; } : never>;
|
|
184
|
+
declare function flag(name: string): z.ZodBoolean;
|
|
185
|
+
/** Each child's age in one room, up to `max` children. */
|
|
186
|
+
declare function childrenAges(max: number): z.ZodArray<z.ZodNumber>;
|
|
187
|
+
|
|
188
|
+
type GuideSurface = "mcp" | "claude-skill" | "cli";
|
|
189
|
+
type GuideTopic = "main" | "planning";
|
|
190
|
+
/** The guide on `topic` as `surface` states it, in Markdown. */
|
|
191
|
+
declare function renderGuide(surface: GuideSurface, topic: GuideTopic): string;
|
|
192
|
+
|
|
193
|
+
/** `POST /api/agent/v1/hotels/search` and the MCP `search_hotels` arguments. */
|
|
194
|
+
declare const searchHotelsInput: z.ZodObject<{
|
|
195
|
+
query: z.ZodOptional<z.ZodString>;
|
|
196
|
+
cityCode: z.ZodOptional<z.ZodNumber>;
|
|
197
|
+
near: z.ZodOptional<z.ZodObject<{
|
|
198
|
+
lat: z.ZodNumber;
|
|
199
|
+
lng: z.ZodNumber;
|
|
200
|
+
radiusKm: z.ZodNumber;
|
|
201
|
+
}, z.core.$strict>>;
|
|
202
|
+
starMin: z.ZodOptional<z.ZodNumber>;
|
|
203
|
+
starMax: z.ZodOptional<z.ZodNumber>;
|
|
204
|
+
facilities: z.ZodOptional<z.ZodPipe<z.ZodArray<z.ZodString>, z.ZodTransform<string[], string[]>>>;
|
|
205
|
+
checkIn: z.ZodString;
|
|
206
|
+
checkOut: z.ZodString;
|
|
207
|
+
currency: z.ZodDefault<z.ZodEnum<{
|
|
208
|
+
CNY: "CNY";
|
|
209
|
+
USD: "USD";
|
|
210
|
+
}>>;
|
|
211
|
+
priceMin: z.ZodOptional<z.ZodNumber>;
|
|
212
|
+
priceMax: z.ZodOptional<z.ZodNumber>;
|
|
213
|
+
sortBy: z.ZodDefault<z.ZodEnum<{
|
|
214
|
+
recommended: "recommended";
|
|
215
|
+
"price-asc": "price-asc";
|
|
216
|
+
"price-desc": "price-desc";
|
|
217
|
+
"star-desc": "star-desc";
|
|
218
|
+
distance: "distance";
|
|
219
|
+
}>>;
|
|
220
|
+
page: z.ZodDefault<z.ZodNumber>;
|
|
221
|
+
pageSize: z.ZodDefault<z.ZodNumber>;
|
|
222
|
+
}, z.core.$strict>;
|
|
223
|
+
/**
|
|
224
|
+
* `GET /api/agent/v1/hotels/{id}` and the MCP `get_hotel` arguments. Not
|
|
225
|
+
* strict: it has always dropped a key it doesn't know. `hotelId` is coerced,
|
|
226
|
+
* so the numeric string a URL path gives parses as the number.
|
|
227
|
+
*/
|
|
228
|
+
declare const getHotelInput: z.ZodObject<{
|
|
229
|
+
hotelId: z.ZodCoercedNumber<unknown>;
|
|
230
|
+
includeRooms: z.ZodOptional<z.ZodBoolean>;
|
|
231
|
+
}, z.core.$strip>;
|
|
232
|
+
/** `POST /api/agent/v1/hotels/rates` and the MCP `get_rates` arguments. */
|
|
233
|
+
declare const getRatesInput: z.ZodObject<{
|
|
234
|
+
hotelIds: z.ZodArray<z.ZodNumber>;
|
|
235
|
+
checkIn: z.ZodString;
|
|
236
|
+
checkOut: z.ZodString;
|
|
237
|
+
rooms: z.ZodNumber;
|
|
238
|
+
adults: z.ZodNumber;
|
|
239
|
+
childrenAges: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
240
|
+
nationality: z.ZodOptional<z.ZodString>;
|
|
241
|
+
currency: z.ZodDefault<z.ZodEnum<{
|
|
242
|
+
CNY: "CNY";
|
|
243
|
+
USD: "USD";
|
|
244
|
+
}>>;
|
|
245
|
+
bookableOnly: z.ZodDefault<z.ZodBoolean>;
|
|
246
|
+
boardCodes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
247
|
+
RO: "RO";
|
|
248
|
+
BB: "BB";
|
|
249
|
+
HB: "HB";
|
|
250
|
+
FB: "FB";
|
|
251
|
+
BL: "BL";
|
|
252
|
+
BD: "BD";
|
|
253
|
+
HBBL: "HBBL";
|
|
254
|
+
HBLD: "HBLD";
|
|
255
|
+
AI: "AI";
|
|
256
|
+
}>>>;
|
|
257
|
+
freeCancellation: z.ZodOptional<z.ZodBoolean>;
|
|
258
|
+
maxRoomsPerHotel: z.ZodDefault<z.ZodNumber>;
|
|
259
|
+
}, z.core.$strict>;
|
|
260
|
+
/**
|
|
261
|
+
* `POST /api/agent/v1/hotels/bookings` and the MCP `create_booking`
|
|
262
|
+
* arguments. The booking action validates guest names (Latin letters) and
|
|
263
|
+
* the room assignment again. Occupancy is per room, every room the same.
|
|
264
|
+
* Unknown keys are refused, not dropped: a misspelled `children` would
|
|
265
|
+
* otherwise book the default `childrenAges: []`, a different party.
|
|
266
|
+
*/
|
|
267
|
+
declare const createBookingInput: z.ZodObject<{
|
|
268
|
+
hotelId: z.ZodNumber;
|
|
269
|
+
roomId: z.ZodString;
|
|
270
|
+
ratePlanCode: z.ZodString;
|
|
271
|
+
checkIn: z.ZodString;
|
|
272
|
+
checkOut: z.ZodString;
|
|
273
|
+
rooms: z.ZodNumber;
|
|
274
|
+
adults: z.ZodNumber;
|
|
275
|
+
childrenAges: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
|
|
276
|
+
guests: z.ZodArray<z.ZodObject<{
|
|
277
|
+
firstName: z.ZodString;
|
|
278
|
+
lastName: z.ZodString;
|
|
279
|
+
roomNo: z.ZodCoercedNumber<unknown>;
|
|
280
|
+
}, z.core.$strict>>;
|
|
281
|
+
contactName: z.ZodString;
|
|
282
|
+
contactPhone: z.ZodString;
|
|
283
|
+
contactEmail: z.ZodOptional<z.ZodString>;
|
|
284
|
+
specialRequests: z.ZodOptional<z.ZodString>;
|
|
285
|
+
}, z.core.$strict>;
|
|
286
|
+
/** What a caller sends (defaults optional), and what a handler gets once parsed. */
|
|
287
|
+
type SearchHotelsInput = z.input<typeof searchHotelsInput>;
|
|
288
|
+
type SearchHotelsParsed = z.output<typeof searchHotelsInput>;
|
|
289
|
+
type GetHotelInput = z.input<typeof getHotelInput>;
|
|
290
|
+
type GetHotelParsed = z.output<typeof getHotelInput>;
|
|
291
|
+
type GetRatesInput = z.input<typeof getRatesInput>;
|
|
292
|
+
type GetRatesParsed = z.output<typeof getRatesInput>;
|
|
293
|
+
type CreateBookingInput = z.input<typeof createBookingInput>;
|
|
294
|
+
type CreateBookingParsed = z.output<typeof createBookingInput>;
|
|
295
|
+
|
|
296
|
+
/** The ceilings and floors every agent surface enforces and documents. */
|
|
297
|
+
declare const LIMITS: {
|
|
298
|
+
readonly rooms: 8;
|
|
299
|
+
readonly adultsPerRoom: 8;
|
|
300
|
+
readonly childrenPerRoom: 8;
|
|
301
|
+
readonly childAgeMax: 17;
|
|
302
|
+
readonly nights: 15;
|
|
303
|
+
readonly ratesHotels: 15;
|
|
304
|
+
readonly facilities: 10;
|
|
305
|
+
readonly queryLength: 128;
|
|
306
|
+
readonly nearRadiusKm: 200;
|
|
307
|
+
readonly page: 100;
|
|
308
|
+
readonly pageSize: 50;
|
|
309
|
+
readonly guestName: 60;
|
|
310
|
+
readonly contactName: 120;
|
|
311
|
+
readonly contactPhoneMin: 3;
|
|
312
|
+
readonly contactPhoneMax: 40;
|
|
313
|
+
readonly specialRequests: 500;
|
|
314
|
+
readonly maxRoomsPerHotel: 200;
|
|
315
|
+
};
|
|
316
|
+
/** Board (meal plan) codes a rate can carry and a search can ask for. */
|
|
317
|
+
declare const BOARD_CODES: readonly ["RO", "BB", "HB", "FB", "BL", "BD", "HBBL", "HBLD", "AI"];
|
|
318
|
+
/** Currencies prices can be shown and paid in. */
|
|
319
|
+
declare const CURRENCIES: readonly ["CNY", "USD"];
|
|
320
|
+
/** Orders a hotel search can be sorted in. */
|
|
321
|
+
declare const SEARCH_SORTS: readonly ["recommended", "price-asc", "price-desc", "star-desc", "distance"];
|
|
322
|
+
/** Where a person goes when an agent gets stuck. */
|
|
323
|
+
declare const SUPPORT_EMAIL = "info@fortrip.ai";
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* What `create_booking` / `POST /api/agent/v1/hotels/bookings` answers with.
|
|
327
|
+
* Its JSON Schema allows no other keys, so a caller that parses its answer
|
|
328
|
+
* through this schema drops a field the server adds until it is added here,
|
|
329
|
+
* rather than failing a strict client's validation. The MCP tool adds a
|
|
330
|
+
* `nextStep` to it; the HTTP answer has none.
|
|
331
|
+
*/
|
|
332
|
+
declare const bookingResult: z.ZodObject<{
|
|
333
|
+
agentOrderId: z.ZodString;
|
|
334
|
+
paymentUrl: z.ZodString;
|
|
335
|
+
amount: z.ZodNullable<z.ZodNumber>;
|
|
336
|
+
currency: z.ZodString;
|
|
337
|
+
orderUrl: z.ZodString;
|
|
338
|
+
statusUrl: z.ZodOptional<z.ZodString>;
|
|
339
|
+
stay: z.ZodObject<{
|
|
340
|
+
hotelId: z.ZodNumber;
|
|
341
|
+
hotelName: z.ZodNullable<z.ZodString>;
|
|
342
|
+
roomName: z.ZodNullable<z.ZodString>;
|
|
343
|
+
checkIn: z.ZodString;
|
|
344
|
+
checkOut: z.ZodString;
|
|
345
|
+
nights: z.ZodNumber;
|
|
346
|
+
rooms: z.ZodNumber;
|
|
347
|
+
adultsPerRoom: z.ZodNumber;
|
|
348
|
+
childrenAgesPerRoom: z.ZodArray<z.ZodNumber>;
|
|
349
|
+
}, z.core.$strip>;
|
|
350
|
+
terms: z.ZodNullable<z.ZodObject<{
|
|
351
|
+
boardCode: z.ZodString;
|
|
352
|
+
boardCount: z.ZodNumber;
|
|
353
|
+
refundable: z.ZodBoolean;
|
|
354
|
+
freeCancelUntil: z.ZodNullable<z.ZodString>;
|
|
355
|
+
cancellation: z.ZodArray<z.ZodObject<{
|
|
356
|
+
amount: z.ZodNumber;
|
|
357
|
+
from: z.ZodString;
|
|
358
|
+
}, z.core.$strip>>;
|
|
359
|
+
isInstantConfirm: z.ZodBoolean;
|
|
360
|
+
confirmByMins: z.ZodNullable<z.ZodNumber>;
|
|
361
|
+
payAtHotelFees: z.ZodArray<z.ZodObject<{
|
|
362
|
+
type: z.ZodString;
|
|
363
|
+
isMandatory: z.ZodBoolean;
|
|
364
|
+
amount: z.ZodNumber;
|
|
365
|
+
currency: z.ZodString;
|
|
366
|
+
}, z.core.$strip>>;
|
|
367
|
+
}, z.core.$strip>>;
|
|
368
|
+
}, z.core.$strip>;
|
|
369
|
+
type BookingResult = z.infer<typeof bookingResult>;
|
|
370
|
+
/** `GET /api/agent/v1/me`: the signed-in user. */
|
|
371
|
+
declare const meResult: z.ZodObject<{
|
|
372
|
+
userId: z.ZodString;
|
|
373
|
+
email: z.ZodNullable<z.ZodString>;
|
|
374
|
+
name: z.ZodNullable<z.ZodString>;
|
|
375
|
+
}, z.core.$strip>;
|
|
376
|
+
type MeResult = z.infer<typeof meResult>;
|
|
377
|
+
/**
|
|
378
|
+
* `GET /api/agent/v1/auth/config` (public): where and how the CLI signs in
|
|
379
|
+
* with the device flow. `scopes` is one space-separated string.
|
|
380
|
+
*/
|
|
381
|
+
declare const authConfig: z.ZodObject<{
|
|
382
|
+
issuer: z.ZodString;
|
|
383
|
+
clientId: z.ZodString;
|
|
384
|
+
scopes: z.ZodString;
|
|
385
|
+
deviceAuthorizationEndpoint: z.ZodString;
|
|
386
|
+
tokenEndpoint: z.ZodString;
|
|
387
|
+
revocationEndpoint: z.ZodString;
|
|
388
|
+
}, z.core.$strip>;
|
|
389
|
+
type AuthConfig = z.infer<typeof authConfig>;
|
|
390
|
+
|
|
391
|
+
/** Where the agent API lives. Paths only: the origin is the caller's to choose. */
|
|
392
|
+
declare const API: {
|
|
393
|
+
readonly me: "/api/agent/v1/me";
|
|
394
|
+
readonly authConfig: "/api/agent/v1/auth/config";
|
|
395
|
+
readonly search: "/api/agent/v1/hotels/search";
|
|
396
|
+
readonly rates: "/api/agent/v1/hotels/rates";
|
|
397
|
+
readonly bookings: "/api/agent/v1/hotels/bookings";
|
|
398
|
+
readonly mcp: "/api/mcp";
|
|
399
|
+
readonly hotel: (id: number, includeRooms?: boolean) => string;
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
declare const reasonStatuses: {
|
|
403
|
+
readonly already_booked: readonly [409];
|
|
404
|
+
readonly price_or_terms_changed: readonly [409];
|
|
405
|
+
readonly rate_unavailable: readonly [422];
|
|
406
|
+
readonly refused: readonly [422];
|
|
407
|
+
readonly supplier_unavailable: readonly [503, 504];
|
|
408
|
+
};
|
|
409
|
+
type ErrorReason = keyof typeof reasonStatuses;
|
|
410
|
+
/**
|
|
411
|
+
* The `reason` an agent error can carry, each with the HTTP statuses it is
|
|
412
|
+
* sent with. Typed as plain number lists so `ERROR_REASONS[reason].includes(status)`
|
|
413
|
+
* type-checks for a status read off a response.
|
|
414
|
+
*/
|
|
415
|
+
declare const ERROR_REASONS: {
|
|
416
|
+
readonly [R in ErrorReason]: readonly number[];
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
/** The contract's version, from package.json, the one place a release bumps it. */
|
|
420
|
+
declare const CONTRACT_VERSION: string;
|
|
421
|
+
|
|
422
|
+
export { API, type AuthConfig, BOARD_CODES, BOOKING_NEXT_STEP, type BookingResult, CARD_NUMBER_MESSAGE, COMMANDS, CONTRACT_VERSION, CURRENCIES, type CommandSpec, type CreateBookingInput, type CreateBookingParsed, ERROR_REASONS, type ErrorReason, type FlagSpec, GLOBAL_FLAGS, type GetHotelInput, type GetHotelParsed, type GetRatesInput, type GetRatesParsed, type GuideSurface, type GuideTopic, LIMITS, MCP_BOOKING_RESULT, MCP_RATES_INPUT, type MeResult, type OptionSpec, SEARCH_SORTS, SUPPORT_EMAIL, type SearchHotelsInput, type SearchHotelsParsed, TOOLS, type ToolName, type ToolSpec, addIsoDateDays, authConfig, bookingResult, childrenAges, containsCardNumber, countIsoDateDays, createBookingInput, flag, getHotelInput, getRatesInput, isIsoDate, isoDate, meResult, nonEmpty, oneOf, positiveId, renderGuide, searchHotelsInput, strictInput, text, wholeNumber };
|