@gojinko/cli 2.28.0 → 2.29.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 +34 -8
- package/SKILL.md +21 -1
- package/dist/commands/cars/car-cancel.d.ts +46 -0
- package/dist/commands/cars/car-cancel.d.ts.map +1 -0
- package/dist/commands/cars/car-cancel.js +136 -0
- package/dist/commands/cars/car-cancel.js.map +1 -0
- package/dist/commands/cars/car-search.d.ts +42 -0
- package/dist/commands/cars/car-search.d.ts.map +1 -0
- package/dist/commands/cars/car-search.js +184 -0
- package/dist/commands/cars/car-search.js.map +1 -0
- package/dist/commands/cars/car-shared.d.ts +54 -0
- package/dist/commands/cars/car-shared.d.ts.map +1 -0
- package/dist/commands/cars/car-shared.js +89 -0
- package/dist/commands/cars/car-shared.js.map +1 -0
- package/dist/commands/exchange.d.ts.map +1 -1
- package/dist/commands/exchange.js +16 -13
- package/dist/commands/exchange.js.map +1 -1
- package/dist/commands/get-booking.d.ts +4 -2
- package/dist/commands/get-booking.d.ts.map +1 -1
- package/dist/commands/get-booking.js +27 -8
- package/dist/commands/get-booking.js.map +1 -1
- package/dist/commands/hotel-cancel.d.ts +0 -15
- package/dist/commands/hotel-cancel.d.ts.map +1 -1
- package/dist/commands/hotel-cancel.js +17 -45
- package/dist/commands/hotel-cancel.js.map +1 -1
- package/dist/commands/refund.d.ts +7 -9
- package/dist/commands/refund.d.ts.map +1 -1
- package/dist/commands/refund.js +26 -17
- package/dist/commands/refund.js.map +1 -1
- package/dist/commands/schema.d.ts.map +1 -1
- package/dist/commands/schema.js +61 -5
- package/dist/commands/schema.js.map +1 -1
- package/dist/commands/trip-status.d.ts.map +1 -1
- package/dist/commands/trip-status.js +4 -2
- package/dist/commands/trip-status.js.map +1 -1
- package/dist/commands/trip.d.ts.map +1 -1
- package/dist/commands/trip.js +3 -3
- package/dist/commands/trip.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/output/servicing.d.ts +2 -0
- package/dist/output/servicing.d.ts.map +1 -0
- package/dist/output/servicing.js +81 -0
- package/dist/output/servicing.js.map +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @gojinko/cli
|
|
2
2
|
|
|
3
|
-
Command-line tool for the Jinko developer platform. Search flights and
|
|
3
|
+
Command-line tool for the Jinko developer platform. Search flights, hotels and rental cars, build trips (multi-domain — flights, hotels and cars share one cart), and hand users off to a Jinko-hosted checkout page.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -68,7 +68,8 @@ Prod and sandbox keys are stored separately in `~/.jinko/config.yaml`. Resolutio
|
|
|
68
68
|
| `hotel-search` | Live hotel search by city / query / geo / hotel-ids. Returns hotels with rooms, rates, `htl_*` offer tokens. |
|
|
69
69
|
| `hotel-details` | Lazy lookup for a specific hotel — images, amenities, location detail. Pairs with `hotel-search` when the user wants the full picture before booking. |
|
|
70
70
|
| `ground-search` | Live rail / coach / ferry search. Each result's `id` is itself the `trip_item_token` — no prefix. |
|
|
71
|
-
| `
|
|
71
|
+
| `car-search` | Live car-rental search by pick-up airport, free-text place or coordinates, a branch-local date-time window (no timezone) and the driver's age and residence. Each offer's `offer_id` (`car_*`) is the `trip_item_token`. An empty `cancellation_fees` means the fee is unknown, never free. |
|
|
72
|
+
| `trip` | Create/update a trip, add flight, hotel, ground or car items, set travelers + contact (a car rental needs `contact.title`, the honorific) |
|
|
72
73
|
|
|
73
74
|
### Legacy
|
|
74
75
|
|
|
@@ -83,7 +84,7 @@ Prod and sandbox keys are stored separately in `~/.jinko/config.yaml`. Resolutio
|
|
|
83
84
|
| `agent-pay submit` | Pay programmatically with a Shared Payment Token: present `--trip-id` + `--token`; the BFF schedules the agent fulfillment and redeems the SPT. Returns the Jinko `booking_ref` (`JNK-…`) once authorized. Falls back to `checkout_url` on a 3DS step-up. |
|
|
84
85
|
| `get-ancillaries` | List a trip's purchasable ancillaries (bags / seats / meals) without generating a checkout URL |
|
|
85
86
|
| `select-ancillaries` | Optional: preselect bags / seats / meals before handoff |
|
|
86
|
-
| `trip-status` | Full lifecycle state: cart, quote, fulfillment, bookings — plus the Jinko `booking_ref` once fulfillment has been scheduled (it can be present before payment completes — read `fulfillment.status` for paid), the value `get-booking` / `hotel-cancel` / `refund` / `exchange` take as `--booking-ref` |
|
|
87
|
+
| `trip-status` | Full lifecycle state: cart, quote, fulfillment, bookings — plus the Jinko `booking_ref` once fulfillment has been scheduled (it can be present before payment completes — read `fulfillment.status` for paid), the value `get-booking` / `hotel-cancel` / `car-cancel` / `refund` / `exchange` take as `--booking-ref` |
|
|
87
88
|
|
|
88
89
|
### Post-booking
|
|
89
90
|
|
|
@@ -97,7 +98,10 @@ Prod and sandbox keys are stored separately in `~/.jinko/config.yaml`. Resolutio
|
|
|
97
98
|
| `exchange price` | Price a selected exchange option |
|
|
98
99
|
| `exchange commit` | Commit a flight exchange |
|
|
99
100
|
| `exchange status` | Check the status of a previously committed exchange |
|
|
100
|
-
| `hotel-cancel` | Cancel a hotel booking (idempotent — safe to retry).
|
|
101
|
+
| `hotel-cancel` | Cancel a hotel booking (idempotent — safe to retry). Use `--booking-ref` and the hotel `--item-id` from `get-booking`; guests also supply `--last-name`. |
|
|
102
|
+
| `car-cancel preview` | Quote the cancellation fee of a car rental and mint the `cancellation_id` a commit is bound to. `fee_known: false` is an unknown fee, not a free one; `manual_required: true` means a Jinko agent has to complete it. |
|
|
103
|
+
| `car-cancel commit` | Cancel the rental against a `--cancellation-id` from `preview` (safe to repeat). `--manual-ok` hands a `manual_required` preview to a Jinko agent — only once the customer has agreed. |
|
|
104
|
+
| `car-cancel status` | Read the latest cancellation attempt. There is no `car-exchange`: modifying a rental is not offered — cancel and book again. |
|
|
101
105
|
|
|
102
106
|
### Utility
|
|
103
107
|
|
|
@@ -167,13 +171,29 @@ jinko select-ancillaries \
|
|
|
167
171
|
jinko trip-status --trip-id <trip_id>
|
|
168
172
|
|
|
169
173
|
# Refunds
|
|
170
|
-
jinko
|
|
171
|
-
jinko refund
|
|
174
|
+
jinko get-booking --booking-ref <ref> --last-name <name>
|
|
175
|
+
jinko refund check --booking-ref <ref> --item-id <item_id> --last-name <name>
|
|
176
|
+
jinko refund commit --booking-ref <ref> --item-id <item_id> --last-name <name>
|
|
172
177
|
|
|
173
178
|
# Hotel cancellations (idempotent — safe to retry)
|
|
174
|
-
jinko hotel-cancel --booking-ref <booking_ref> --last-name <name>
|
|
179
|
+
jinko hotel-cancel --booking-ref <booking_ref> --item-id <item_id> --last-name <name>
|
|
175
180
|
# Or via the authenticated shortcut:
|
|
176
|
-
jinko hotel-cancel --
|
|
181
|
+
jinko hotel-cancel --booking-ref <booking_ref> --item-id <item_id>
|
|
182
|
+
|
|
183
|
+
# Car rental — times are BRANCH-LOCAL with no timezone; driver age and residence change the price.
|
|
184
|
+
jinko car-search --pick-up-airport LYS --pick-up-date-time 2026-10-25T10:00:00 \
|
|
185
|
+
--drop-off-date-time 2026-10-28T10:00:00 --driver-age 30 --residence-country FR --currency EUR
|
|
186
|
+
# A free-text place that is ambiguous comes back as candidates, not offers — retry with one of them:
|
|
187
|
+
jinko car-search --pick-up-place "Lyon Part-Dieu" …
|
|
188
|
+
# Each offer_id (car_*) is the trip item token; a car booking needs the contact's honorific:
|
|
189
|
+
jinko trip --trip-item-token <car_offer_id> \
|
|
190
|
+
--travelers '[{"first_name":"Jane","last_name":"Doe","passenger_type":"ADULT"}]' \
|
|
191
|
+
--contact '{"email":"jane@example.com","phone":"+33612345678","title":"ms"}'
|
|
192
|
+
|
|
193
|
+
# Car cancellation — preview quotes the fee and mints the cancellation_id the commit is bound to
|
|
194
|
+
jinko car-cancel preview --booking-ref <booking_ref> --item-id <item_id> --last-name <name>
|
|
195
|
+
jinko car-cancel commit --booking-ref <booking_ref> --item-id <item_id> --last-name <name> --cancellation-id <ccl_…>
|
|
196
|
+
jinko car-cancel status --booking-ref <booking_ref> --item-id <item_id> --last-name <name>
|
|
177
197
|
```
|
|
178
198
|
|
|
179
199
|
## Flight search filters
|
|
@@ -245,3 +265,9 @@ JSON output stays parseable) and will be removed in the next major release.
|
|
|
245
265
|
| `hotel-search` | `--guest-nationality` | `--nationality` |
|
|
246
266
|
| `hotel-search` | `--star-rating` / `--max-star-rating` | `--stars` / `--max-stars` |
|
|
247
267
|
| `get-booking`, `hotel-cancel` | `--booking-ref` | `--ref` |
|
|
268
|
+
|
|
269
|
+
### Servicing a booked item
|
|
270
|
+
|
|
271
|
+
Run `get-booking` before every refund, exchange, hotel cancellation or car cancellation. Its `items[]` expose a stable numeric `item_id` and each item's `servicing` eligibility (`can_refund`, `can_void`, `can_exchange`, support levels and refusal reasons). Select the intended item and pass `--booking-ref` and `--item-id` on every subsequent step, including status polls. The ID remains unchanged after an exchange.
|
|
272
|
+
|
|
273
|
+
Guests must supply `--last-name`; credentials owning the booking may omit it. Omitting `--item-id` works only for a single item of the requested domain. A 409 `item_selection_required` carries safe `items[]` to choose from. CLI commands do not accept provider booking handles. Use the Jinko reference for servicing and the airline locator when speaking to the airline.
|
package/SKILL.md
CHANGED
|
@@ -31,9 +31,15 @@ export JINKO_API_KEY=jnk_...
|
|
|
31
31
|
Carries the full shop filter set (`--max-stops`, `--departure-time-range`, `--refundable-only`,
|
|
32
32
|
`--checked-bag-included`, `--via-airports`, `--origin-alternate-airports`, …) — run
|
|
33
33
|
`jinko flight-search --help` for the list.
|
|
34
|
+
- `car-search` — live car-rental offers for a pick-up place (`--pick-up-airport`, `--pick-up-place` or
|
|
35
|
+
`--pick-up-geo lat,lng,range` — exactly one), branch-local date-times with NO timezone, `--driver-age`
|
|
36
|
+
and `--residence-country` (both change the price — ask, never guess). Each offer's `offer_id` (`car_*`)
|
|
37
|
+
is the `trip_item_token`. An ambiguous `--pick-up-place` returns `candidates` instead of offers: put them
|
|
38
|
+
to the user and retry with one, never auto-pick.
|
|
34
39
|
|
|
35
40
|
### Trip building
|
|
36
|
-
- `trip` — create/update a trip (add items, set travelers + contact). Returns `trip_id`.
|
|
41
|
+
- `trip` — create/update a trip (add items, set travelers + contact). Returns `trip_id`. A cart holding a
|
|
42
|
+
car rental needs `contact.title` (the honorific: mr / ms / mx) before checkout.
|
|
37
43
|
|
|
38
44
|
### Checkout
|
|
39
45
|
- `checkout` — finalize a trip; returns BOTH `checkout_url` (a human opens it to pay) AND `agent_spt_params` (an agent uses them to pay programmatically), plus items + ancillaries + total. (`book` is a deprecated alias.)
|
|
@@ -45,6 +51,10 @@ export JINKO_API_KEY=jnk_...
|
|
|
45
51
|
|
|
46
52
|
### Post-booking
|
|
47
53
|
- `refund check` / `refund commit`
|
|
54
|
+
- `car-cancel preview` / `car-cancel commit` / `car-cancel status` — quote the fee first (`preview` mints the
|
|
55
|
+
`cancellation_id` the commit is bound to), get the user's explicit confirmation, then `commit`. There is NO
|
|
56
|
+
`car-exchange`: changing a rental's dates, vehicle or extras is not offered — the options are keeping the
|
|
57
|
+
booking or cancelling it and running `car-search` again.
|
|
48
58
|
|
|
49
59
|
### Utility
|
|
50
60
|
- `auth`, `config`, `schema`
|
|
@@ -88,6 +98,12 @@ export JINKO_API_KEY=jnk_...
|
|
|
88
98
|
- Passenger types: ADULT | CHILD | INFANT.
|
|
89
99
|
- Cabin classes: economy | premium_economy | business | first. Cabin is ADVISORY —
|
|
90
100
|
providers may still return other cabins, so read each fare's own `cabin_class`.
|
|
101
|
+
- Car rental date-times are BRANCH-LOCAL and zone-less (`2026-10-25T10:00:00`) — a `Z` or an
|
|
102
|
+
offset is rejected because the rental desk works in its own local time.
|
|
103
|
+
- Car money: `price.pay_now` is the ONLY amount Jinko charges; `due_at_desk` is collected by the
|
|
104
|
+
desk and `deposit` is a card hold — never sum them. An empty `cancellation_fees` list, or
|
|
105
|
+
`fee_known: false` on a cancellation, means the fee is UNKNOWN — never say "free cancellation".
|
|
106
|
+
Amounts are `{ value, currency, decimal_places }` in minor units; print `display` when present.
|
|
91
107
|
- `flight-search` reports what it enforced: `applied_filters` lists the requested
|
|
92
108
|
filters the results honor, `unapplied_filters` the ones they do not, each with a
|
|
93
109
|
reason. An unapplied filter is not an error — the call still returns 200. Either
|
|
@@ -97,3 +113,7 @@ export JINKO_API_KEY=jnk_...
|
|
|
97
113
|
The anchor decides ranking, so put the airport that matters most in `--origin`.
|
|
98
114
|
Against a city anchor the list is ignored and reported back under `origin` /
|
|
99
115
|
`destination` in `unapplied_filters`.
|
|
116
|
+
|
|
117
|
+
## Servicing a selected booked item
|
|
118
|
+
|
|
119
|
+
Run `get-booking --booking-ref <ref>` first (guests add `--last-name`). Select the intended `items[].item_id` and inspect its `servicing` eligibility. Pass `--booking-ref` and the same `--item-id` to refund, exchange, hotel-cancel and car-cancel actions, including status polls. The ID remains stable after an exchange. Owning credentials may omit the surname; guests supply it. When a 409 `item_selection_required` lists several items, choose one from `items[]` before retrying.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import type { CarAmount } from '@gojinko/api-client';
|
|
3
|
+
/**
|
|
4
|
+
* The fields the table view reads: the union of what the three car_cancel_*
|
|
5
|
+
* responses carry. Only the preview answers `cancellable`, `manual_required`
|
|
6
|
+
* and `expires_at`; commit and status never do, so every field is optional.
|
|
7
|
+
*/
|
|
8
|
+
export interface CarCancelView {
|
|
9
|
+
cancellation_id?: string;
|
|
10
|
+
state?: string;
|
|
11
|
+
cancellable?: boolean;
|
|
12
|
+
fee_known?: boolean;
|
|
13
|
+
fee?: CarAmount;
|
|
14
|
+
paid?: CarAmount;
|
|
15
|
+
refund_amount?: CarAmount;
|
|
16
|
+
non_refundable?: boolean;
|
|
17
|
+
manual_required?: boolean;
|
|
18
|
+
refund_pending_review?: boolean;
|
|
19
|
+
refund_review_reason?: string;
|
|
20
|
+
rejected_reason?: string;
|
|
21
|
+
expires_at?: string;
|
|
22
|
+
completed_at?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Render a cancel preview/commit/status for the table view.
|
|
26
|
+
*
|
|
27
|
+
* Three distinctions a customer is harmed by if they are flattened:
|
|
28
|
+
* - `fee_known: false` means the fee could NOT be established — unknown, not
|
|
29
|
+
* free. It is rendered as an explicit sentence rather than an absent line,
|
|
30
|
+
* so a zero fee and an unestablished fee never look the same.
|
|
31
|
+
* - `manual_required: true` is not a dead end: the cancellation can be handed
|
|
32
|
+
* to a Jinko agent with `--manual-ok`, but only once the customer agrees, so
|
|
33
|
+
* the line says what the flag does rather than just naming it.
|
|
34
|
+
* - `refund_pending_review: true` means the booking IS cancelled (or recorded
|
|
35
|
+
* for an agent) but the refund figure is not final yet.
|
|
36
|
+
*
|
|
37
|
+
* `refund_review_reason` is printed whenever it is present: on a
|
|
38
|
+
* `cancellable: false` preview it is the supplier's own reason (seen live on
|
|
39
|
+
* dev: "the supplier reports this booking as Cancelled"), which would otherwise
|
|
40
|
+
* vanish from the table. `rejected_reason` is only labelled "rejected" when the
|
|
41
|
+
* state is — the API also uses it for the review note on a confirmed
|
|
42
|
+
* cancellation whose fee could not be established.
|
|
43
|
+
*/
|
|
44
|
+
export declare function formatCarCancel(data: CarCancelView): string[];
|
|
45
|
+
export declare function registerCarCancelCommand(program: Command): void;
|
|
46
|
+
//# sourceMappingURL=car-cancel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"car-cancel.d.ts","sourceRoot":"","sources":["../../../src/commands/cars/car-cancel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAMrD;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,EAAE,CAsC7D;AAqBD,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAyE/D"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { withClient, output, userIntentOption } from '../shared.js';
|
|
2
|
+
import { buildAuthFields, itemIdOption } from '../refund.js';
|
|
3
|
+
import { publicServicingResponse } from '../../output/servicing.js';
|
|
4
|
+
import { formatCarAmount } from './car-shared.js';
|
|
5
|
+
/**
|
|
6
|
+
* Render a cancel preview/commit/status for the table view.
|
|
7
|
+
*
|
|
8
|
+
* Three distinctions a customer is harmed by if they are flattened:
|
|
9
|
+
* - `fee_known: false` means the fee could NOT be established — unknown, not
|
|
10
|
+
* free. It is rendered as an explicit sentence rather than an absent line,
|
|
11
|
+
* so a zero fee and an unestablished fee never look the same.
|
|
12
|
+
* - `manual_required: true` is not a dead end: the cancellation can be handed
|
|
13
|
+
* to a Jinko agent with `--manual-ok`, but only once the customer agrees, so
|
|
14
|
+
* the line says what the flag does rather than just naming it.
|
|
15
|
+
* - `refund_pending_review: true` means the booking IS cancelled (or recorded
|
|
16
|
+
* for an agent) but the refund figure is not final yet.
|
|
17
|
+
*
|
|
18
|
+
* `refund_review_reason` is printed whenever it is present: on a
|
|
19
|
+
* `cancellable: false` preview it is the supplier's own reason (seen live on
|
|
20
|
+
* dev: "the supplier reports this booking as Cancelled"), which would otherwise
|
|
21
|
+
* vanish from the table. `rejected_reason` is only labelled "rejected" when the
|
|
22
|
+
* state is — the API also uses it for the review note on a confirmed
|
|
23
|
+
* cancellation whose fee could not be established.
|
|
24
|
+
*/
|
|
25
|
+
export function formatCarCancel(data) {
|
|
26
|
+
const lines = [];
|
|
27
|
+
if (data.state)
|
|
28
|
+
lines.push(`state: ${data.state}`);
|
|
29
|
+
if (data.cancellable !== undefined)
|
|
30
|
+
lines.push(`cancellable: ${data.cancellable}`);
|
|
31
|
+
if (data.non_refundable === true)
|
|
32
|
+
lines.push('non-refundable');
|
|
33
|
+
if (data.manual_required === true) {
|
|
34
|
+
lines.push(`cannot be completed online: ${data.refund_review_reason ?? 'a person has to complete this cancellation'}.`, 'No refund figure is given and none may be quoted. To hand it to a Jinko agent, run `car-cancel commit` with --manual-ok once the customer has agreed.');
|
|
35
|
+
}
|
|
36
|
+
if (data.fee_known === false) {
|
|
37
|
+
lines.push('cancellation fee: UNKNOWN — not established yet; we will confirm it. This is NOT a zero fee.');
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
const fee = formatCarAmount(data.fee);
|
|
41
|
+
if (fee)
|
|
42
|
+
lines.push(`cancellation fee: ${fee}`);
|
|
43
|
+
}
|
|
44
|
+
const paid = formatCarAmount(data.paid);
|
|
45
|
+
if (paid)
|
|
46
|
+
lines.push(`paid: ${paid}`);
|
|
47
|
+
const refund = formatCarAmount(data.refund_amount);
|
|
48
|
+
if (refund)
|
|
49
|
+
lines.push(`refund amount: ${refund}`);
|
|
50
|
+
if (data.refund_pending_review === true) {
|
|
51
|
+
lines.push('refund pending manual review — do not quote the refund amount as final');
|
|
52
|
+
}
|
|
53
|
+
if (data.manual_required !== true && data.refund_review_reason) {
|
|
54
|
+
lines.push(`${data.cancellable === false ? 'not cancellable' : 'reason'}: ${data.refund_review_reason}`);
|
|
55
|
+
}
|
|
56
|
+
if (data.rejected_reason) {
|
|
57
|
+
lines.push(`${data.state === 'rejected' ? 'rejected' : 'reason'}: ${data.rejected_reason}`);
|
|
58
|
+
}
|
|
59
|
+
if (data.completed_at)
|
|
60
|
+
lines.push(`completed_at: ${data.completed_at}`);
|
|
61
|
+
if (data.expires_at)
|
|
62
|
+
lines.push(`commit before: ${data.expires_at} (after that, preview again)`);
|
|
63
|
+
// Printed last and in full — it is what binds `car-cancel commit` to this quote.
|
|
64
|
+
if (data.cancellation_id)
|
|
65
|
+
lines.push(`cancellation_id: ${data.cancellation_id}`);
|
|
66
|
+
return lines;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Print a car_cancel_* response. Both formats go through the servicing
|
|
70
|
+
* projection first, like every other servicing command, so an older API
|
|
71
|
+
* deployment that still returns supplier handles never prints them.
|
|
72
|
+
*/
|
|
73
|
+
function render(response, globals) {
|
|
74
|
+
const publicResponse = publicServicingResponse(response);
|
|
75
|
+
if (globals.format === 'table') {
|
|
76
|
+
console.log('');
|
|
77
|
+
for (const line of formatCarCancel(publicResponse))
|
|
78
|
+
console.log(line);
|
|
79
|
+
console.log('');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
output(publicResponse, { format: globals.format });
|
|
83
|
+
}
|
|
84
|
+
const LAST_NAME_HELP = 'Driver surname, required for guest lookup; owning credentials may omit it. JSON: `last_name`.';
|
|
85
|
+
export function registerCarCancelCommand(program) {
|
|
86
|
+
// There is deliberately no `car-exchange` beside this (JIN-2312): Auto Europe
|
|
87
|
+
// support is search, book and cancel, the API withdrew the exchange trio from
|
|
88
|
+
// the contract (0.16.0) and its routes answer 404. A different rental means
|
|
89
|
+
// cancelling this one and booking again with `car-search`.
|
|
90
|
+
const car = program
|
|
91
|
+
.command('car-cancel')
|
|
92
|
+
.description('Call get-booking first, choose the car item_id, then quote the fee for, commit, or poll a car-rental cancellation using that booking reference and stable item ID. Modifying a rental (dates, vehicle, extras) is not offered: the options are keeping the booking as is, or cancelling it (fee shown first) and booking a new rental with car-search.');
|
|
93
|
+
car
|
|
94
|
+
.command('preview')
|
|
95
|
+
.summary('Quote the cancellation fee for a car rental')
|
|
96
|
+
.description('Quote the cancellation fee for a booked car rental and mint the `cancellation_id` that binds a later commit to this quote. `fee_known: false` means the fee could NOT be established — unknown, not free. `manual_required: true` means it cannot be completed online; commit with --manual-ok, once the customer agrees, to hand it to a Jinko agent.')
|
|
97
|
+
.requiredOption('--booking-ref <ref>', 'Jinko booking reference from get-booking. JSON: `booking_ref`. [required]')
|
|
98
|
+
.option('--last-name <name>', LAST_NAME_HELP)
|
|
99
|
+
.addOption(itemIdOption())
|
|
100
|
+
.addOption(userIntentOption())
|
|
101
|
+
.action(withClient(async (client, globals, opts) => {
|
|
102
|
+
render(await client.carCancelPreview(buildAuthFields(opts)), globals);
|
|
103
|
+
}));
|
|
104
|
+
car
|
|
105
|
+
.command('commit')
|
|
106
|
+
.summary('Cancel a booked car rental')
|
|
107
|
+
.description('Cancel a booked car rental, bound to the fee quoted by a preceding `car-cancel preview`. Pass that preview\'s `cancellation_id`. A 409 refuses the commit without cancelling anything: `quote_expired` (preview again), `quote_drift` (a fresh `requote` reference and the figure now — show it, then commit against it), `active_operation_exists` (wait for it; poll `car-cancel status`), `not_cancellable`, `funds_unavailable`, or `manual_required` (resend with --manual-ok once the customer agrees).')
|
|
108
|
+
.requiredOption('--booking-ref <ref>', 'Jinko booking reference from get-booking. JSON: `booking_ref`. [required]')
|
|
109
|
+
.option('--last-name <name>', LAST_NAME_HELP)
|
|
110
|
+
.addOption(itemIdOption())
|
|
111
|
+
.requiredOption('--cancellation-id <id>', 'the "ccl_..." reference from `car-cancel preview` — binds this commit to the fee the customer saw. JSON: `cancellation_id`. [required]')
|
|
112
|
+
.option('--reason <text>', 'free-text cancellation reason. JSON: `reason`.')
|
|
113
|
+
.option('--manual-ok', 'commit a preview that answered `manual_required: true`: the cancellation is recorded for a Jinko agent — nothing is sent to the rental company by this call — and the answer is `state: pending` with `refund_pending_review: true`. Without it such a commit is refused 409 `manual_required`. Send it only once the customer has agreed; on a preview that can be committed online it changes nothing. JSON: `manual_ok`.')
|
|
114
|
+
.addOption(userIntentOption())
|
|
115
|
+
.action(withClient(async (client, globals, opts) => {
|
|
116
|
+
const response = await client.carCancelCommit({
|
|
117
|
+
...buildAuthFields(opts),
|
|
118
|
+
cancellation_id: opts.cancellationId,
|
|
119
|
+
...(opts.reason && { reason: opts.reason }),
|
|
120
|
+
...(opts.manualOk === true && { manual_ok: true }),
|
|
121
|
+
});
|
|
122
|
+
render(response, globals);
|
|
123
|
+
}));
|
|
124
|
+
car
|
|
125
|
+
.command('status')
|
|
126
|
+
.summary('Check the status of a car-rental cancellation')
|
|
127
|
+
.description('Poll the current status of a previously committed car-rental cancellation. Read-only. `pending` with `refund_pending_review: true` means a Jinko agent has it — do not commit again.')
|
|
128
|
+
.requiredOption('--booking-ref <ref>', 'Jinko booking reference from get-booking. JSON: `booking_ref`. [required]')
|
|
129
|
+
.option('--last-name <name>', LAST_NAME_HELP)
|
|
130
|
+
.addOption(itemIdOption())
|
|
131
|
+
.addOption(userIntentOption())
|
|
132
|
+
.action(withClient(async (client, globals, opts) => {
|
|
133
|
+
render(await client.carCancelStatus(buildAuthFields(opts)), globals);
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=car-cancel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"car-cancel.js","sourceRoot":"","sources":["../../../src/commands/cars/car-cancel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import type { CarSearchRequest, CarOffer, CarResolvedPlace } from '@gojinko/api-client';
|
|
3
|
+
/** Resolved, validated inputs for a car_search request. */
|
|
4
|
+
export interface CarSearchInputs {
|
|
5
|
+
pickUpDateTime: string;
|
|
6
|
+
pickUpAirport?: string;
|
|
7
|
+
pickUpPlace?: string;
|
|
8
|
+
pickUpGeo?: string;
|
|
9
|
+
dropOffDateTime?: string;
|
|
10
|
+
dropOffAirport?: string;
|
|
11
|
+
dropOffPlace?: string;
|
|
12
|
+
dropOffGeo?: string;
|
|
13
|
+
driverAge: string;
|
|
14
|
+
residenceCountry: string;
|
|
15
|
+
currency?: string;
|
|
16
|
+
lang?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Build the canonical car_search body (jinko-api POST /v1/car_search).
|
|
20
|
+
*
|
|
21
|
+
* Two rules the contract enforces at runtime but cannot express in JSON Schema,
|
|
22
|
+
* so they are checked here instead of surfacing as an opaque 400:
|
|
23
|
+
* 1. `pick_up` names exactly ONE of airport_code / place / geo.
|
|
24
|
+
* 2. a drop-off time must come from `drop_off.date_time` or the top-level
|
|
25
|
+
* `drop_off_date_time`.
|
|
26
|
+
*
|
|
27
|
+
* A drop-off LOCATION is optional — omitting it means returning the car to the
|
|
28
|
+
* pick-up branch, which is the common case and a different thing from omitting
|
|
29
|
+
* the drop-off TIME.
|
|
30
|
+
*
|
|
31
|
+
* Pure — exported for tests.
|
|
32
|
+
*/
|
|
33
|
+
export declare function buildCarSearchBody(input: CarSearchInputs): CarSearchRequest;
|
|
34
|
+
/**
|
|
35
|
+
* Table view.
|
|
36
|
+
*
|
|
37
|
+
* `offer_id` is printed in full on its own line and never truncated: it is the
|
|
38
|
+
* trip item token, so a shortened one is silently unusable.
|
|
39
|
+
*/
|
|
40
|
+
export declare function formatCarTable(offers: CarOffer[], candidates?: CarResolvedPlace[]): string[];
|
|
41
|
+
export declare function registerCarSearchCommand(program: Command): void;
|
|
42
|
+
//# sourceMappingURL=car-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"car-search.d.ts","sourceRoot":"","sources":["../../../src/commands/cars/car-search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAIxF,2DAA2D;AAC3D,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,gBAAgB,CAgD3E;AAiCD;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,QAAQ,EAAE,EAClB,UAAU,GAAE,gBAAgB,EAAO,GAClC,MAAM,EAAE,CAoDV;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAsE/D"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { withClient, output, userIntentOption, exitWithError, UsageError } from '../shared.js';
|
|
2
|
+
import { formatCarAmount, requireLocalDateTime, resolveLocation } from './car-shared.js';
|
|
3
|
+
/**
|
|
4
|
+
* Build the canonical car_search body (jinko-api POST /v1/car_search).
|
|
5
|
+
*
|
|
6
|
+
* Two rules the contract enforces at runtime but cannot express in JSON Schema,
|
|
7
|
+
* so they are checked here instead of surfacing as an opaque 400:
|
|
8
|
+
* 1. `pick_up` names exactly ONE of airport_code / place / geo.
|
|
9
|
+
* 2. a drop-off time must come from `drop_off.date_time` or the top-level
|
|
10
|
+
* `drop_off_date_time`.
|
|
11
|
+
*
|
|
12
|
+
* A drop-off LOCATION is optional — omitting it means returning the car to the
|
|
13
|
+
* pick-up branch, which is the common case and a different thing from omitting
|
|
14
|
+
* the drop-off TIME.
|
|
15
|
+
*
|
|
16
|
+
* Pure — exported for tests.
|
|
17
|
+
*/
|
|
18
|
+
export function buildCarSearchBody(input) {
|
|
19
|
+
requireLocalDateTime(input.pickUpDateTime, '--pick-up-date-time');
|
|
20
|
+
requireLocalDateTime(input.dropOffDateTime, '--drop-off-date-time');
|
|
21
|
+
const pickUpLocation = resolveLocation('pick-up', { airport: input.pickUpAirport, place: input.pickUpPlace, geo: input.pickUpGeo }, { required: true });
|
|
22
|
+
const dropOffLocation = resolveLocation('drop-off', { airport: input.dropOffAirport, place: input.dropOffPlace, geo: input.dropOffGeo }, { required: false });
|
|
23
|
+
const hasDropOffLocation = Object.keys(dropOffLocation).length > 0;
|
|
24
|
+
if (!input.dropOffDateTime && !hasDropOffLocation) {
|
|
25
|
+
throw new UsageError('--drop-off-date-time is required (or give a --drop-off-* location carrying its own time).');
|
|
26
|
+
}
|
|
27
|
+
const driverAge = Number(input.driverAge);
|
|
28
|
+
if (!/^\d+$/.test(input.driverAge.trim()) || driverAge < 18 || driverAge > 99) {
|
|
29
|
+
throw new UsageError(`--driver-age must be a whole number between 18 and 99 (got "${input.driverAge}").`);
|
|
30
|
+
}
|
|
31
|
+
if (!/^[A-Za-z]{2}$/.test(input.residenceCountry)) {
|
|
32
|
+
throw new UsageError(`--residence-country must be a 2-letter ISO 3166-1 alpha-2 code, e.g. FR (got "${input.residenceCountry}").`);
|
|
33
|
+
}
|
|
34
|
+
const body = {
|
|
35
|
+
pick_up: { date_time: input.pickUpDateTime, ...pickUpLocation },
|
|
36
|
+
driver_age: driverAge,
|
|
37
|
+
residence_country: input.residenceCountry.toUpperCase(),
|
|
38
|
+
};
|
|
39
|
+
if (hasDropOffLocation) {
|
|
40
|
+
body.drop_off = { ...dropOffLocation, ...(input.dropOffDateTime && { date_time: input.dropOffDateTime }) };
|
|
41
|
+
}
|
|
42
|
+
else if (input.dropOffDateTime) {
|
|
43
|
+
body.drop_off_date_time = input.dropOffDateTime;
|
|
44
|
+
}
|
|
45
|
+
if (input.currency)
|
|
46
|
+
body.currency = input.currency;
|
|
47
|
+
if (input.lang)
|
|
48
|
+
body.lang = input.lang;
|
|
49
|
+
return body;
|
|
50
|
+
}
|
|
51
|
+
/** The vehicle line: "Compact · VW Golf or similar [CDMR] · 5 seats · manual". */
|
|
52
|
+
function vehicleLine(offer) {
|
|
53
|
+
const v = offer.vehicle;
|
|
54
|
+
return [
|
|
55
|
+
v?.category,
|
|
56
|
+
v?.name,
|
|
57
|
+
v?.acriss_code && `[${v.acriss_code}]`,
|
|
58
|
+
v?.seats && `${v.seats} seats`,
|
|
59
|
+
v?.transmission,
|
|
60
|
+
v?.air_conditioned === true && 'A/C',
|
|
61
|
+
]
|
|
62
|
+
.filter(Boolean)
|
|
63
|
+
.join(' · ');
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Cancellation summary for one offer.
|
|
67
|
+
*
|
|
68
|
+
* An EMPTY `cancellation_fees[]` means the fee could not be established —
|
|
69
|
+
* UNKNOWN, not free. Printing nothing (or "free cancellation") there would tell
|
|
70
|
+
* a customer they can walk away at no cost when they may not be able to.
|
|
71
|
+
*/
|
|
72
|
+
function cancellationLine(offer) {
|
|
73
|
+
const fees = offer.cancellation_fees ?? [];
|
|
74
|
+
if (!fees.length)
|
|
75
|
+
return 'cancellation fee: unknown — confirm before booking';
|
|
76
|
+
const applicable = fees.find((f) => f.applicable_now) ?? fees[0];
|
|
77
|
+
if (applicable?.non_refundable)
|
|
78
|
+
return 'non-refundable';
|
|
79
|
+
const amount = formatCarAmount(applicable?.fee);
|
|
80
|
+
return amount ? `cancellation fee: ${amount}` : 'cancellation fee: unknown — confirm before booking';
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Table view.
|
|
84
|
+
*
|
|
85
|
+
* `offer_id` is printed in full on its own line and never truncated: it is the
|
|
86
|
+
* trip item token, so a shortened one is silently unusable.
|
|
87
|
+
*/
|
|
88
|
+
export function formatCarTable(offers, candidates = []) {
|
|
89
|
+
// Candidates instead of offers is the ambiguous-place RETRY signal, not an
|
|
90
|
+
// empty result — say so, and name the values to retry with.
|
|
91
|
+
if (!offers.length && candidates.length) {
|
|
92
|
+
return [
|
|
93
|
+
'That location was ambiguous — no offers returned. Retry with one of:',
|
|
94
|
+
...candidates.map((c) => ` --pick-up-place "${c.name ?? '?'}"${[c.kind, c.city, c.country].filter(Boolean).length ? ` (${[c.kind, c.city, c.country].filter(Boolean).join(', ')})` : ''}`),
|
|
95
|
+
];
|
|
96
|
+
}
|
|
97
|
+
if (!offers.length) {
|
|
98
|
+
return [
|
|
99
|
+
'No car offers found.',
|
|
100
|
+
'Check the pick-up/drop-off times are branch-local (no Z suffix) and that the driver age is eligible for this branch.',
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
const lines = [];
|
|
104
|
+
for (const offer of offers) {
|
|
105
|
+
const price = formatCarAmount(offer.price?.pay_now);
|
|
106
|
+
const dueAtDesk = formatCarAmount(offer.price?.due_at_desk);
|
|
107
|
+
lines.push([
|
|
108
|
+
price ? `${price} now` : '',
|
|
109
|
+
dueAtDesk && `+ ${dueAtDesk} at desk`,
|
|
110
|
+
offer.on_request === true && '[ON REQUEST]',
|
|
111
|
+
]
|
|
112
|
+
.filter(Boolean)
|
|
113
|
+
.join(' '));
|
|
114
|
+
lines.push(` ${vehicleLine(offer)}`);
|
|
115
|
+
const pkg = offer.package;
|
|
116
|
+
const pkgLine = [
|
|
117
|
+
pkg?.supplier_name,
|
|
118
|
+
pkg?.name,
|
|
119
|
+
pkg?.mileage_unlimited === true ? 'unlimited mileage' : pkg?.mileage_allowance,
|
|
120
|
+
pkg?.fuel_policy,
|
|
121
|
+
]
|
|
122
|
+
.filter(Boolean)
|
|
123
|
+
.join(' · ');
|
|
124
|
+
if (pkgLine)
|
|
125
|
+
lines.push(` ${pkgLine}`);
|
|
126
|
+
const branch = offer.pick_up;
|
|
127
|
+
if (branch?.name || branch?.address) {
|
|
128
|
+
lines.push(` pick-up: ${[branch.name, branch.address, branch.city].filter(Boolean).join(', ')}`);
|
|
129
|
+
}
|
|
130
|
+
lines.push(` ${cancellationLine(offer)}`);
|
|
131
|
+
if (offer.expires_at)
|
|
132
|
+
lines.push(` expires_at: ${offer.expires_at}`);
|
|
133
|
+
lines.push(` trip_item_token: ${offer.offer_id ?? '(missing)'}`);
|
|
134
|
+
lines.push('');
|
|
135
|
+
}
|
|
136
|
+
return lines;
|
|
137
|
+
}
|
|
138
|
+
export function registerCarSearchCommand(program) {
|
|
139
|
+
program
|
|
140
|
+
.command('car-search')
|
|
141
|
+
.summary('Search live car-rental offers')
|
|
142
|
+
.description('Search live car-rental inventory for a pick-up location, date-time window and driver age. Pick-up and drop-off times are BRANCH-LOCAL with no timezone (2026-09-12T10:00:00) — the rental desk\'s own wall clock. Give exactly one of --pick-up-airport, --pick-up-place or --pick-up-geo; a free-text --pick-up-place that resolves ambiguously comes back as a candidate list instead of offers, which is a retry signal, not an error. Each offer\'s `offer_id` is a trip item token you can hand straight to the trip command to start a booking.')
|
|
143
|
+
.option('--pick-up-date-time <datetime>', 'pick-up date-time, branch-local, no timezone: 2026-09-12T10:00:00. JSON: `pick_up.date_time`. [required]')
|
|
144
|
+
.option('--pick-up-airport <code>', 'pick-up IATA airport code, e.g. LYS. JSON: `pick_up.airport_code`.')
|
|
145
|
+
.option('--pick-up-place <text>', 'free-text pick-up location — a city, district or landmark, e.g. "Lyon city centre". Resolved server-side. JSON: `pick_up.place`.')
|
|
146
|
+
.option('--pick-up-geo <lat,lng,range>', 'pick-up coordinates and search radius in metres (min 50), e.g. 45.72,4.94,5000. JSON: `pick_up.geo`.')
|
|
147
|
+
.option('--drop-off-date-time <datetime>', 'drop-off date-time, branch-local, no timezone. JSON: `drop_off_date_time`. [required unless a --drop-off-* location carries its own time]')
|
|
148
|
+
.option('--drop-off-airport <code>', 'drop-off IATA airport code, for a one-way rental. JSON: `drop_off.airport_code`.')
|
|
149
|
+
.option('--drop-off-place <text>', 'free-text drop-off location, for a one-way rental. JSON: `drop_off.place`.')
|
|
150
|
+
.option('--drop-off-geo <lat,lng,range>', 'drop-off coordinates and radius, for a one-way rental. JSON: `drop_off.geo`.')
|
|
151
|
+
.option('--driver-age <age>', "driver's age at pick-up, 18-99. Pricing and availability are age-dependent. JSON: `driver_age`. [required]")
|
|
152
|
+
.option('--residence-country <code>', "driver's country of residence, ISO 3166-1 alpha-2, e.g. FR. Rates and inclusions vary by residence. JSON: `residence_country`. [required]")
|
|
153
|
+
.option('--currency <code>', 'ISO 4217 display currency, e.g. EUR. Defaults to USD. JSON: `currency`.')
|
|
154
|
+
.option('--lang <tag>', 'BCP-47 language tag for vehicle and branch text, e.g. en-gb. JSON: `lang`.')
|
|
155
|
+
.addOption(userIntentOption())
|
|
156
|
+
.action(withClient(async (client, globals, opts) => {
|
|
157
|
+
let body;
|
|
158
|
+
try {
|
|
159
|
+
if (!opts.pickUpDateTime) {
|
|
160
|
+
throw new UsageError('--pick-up-date-time is required.');
|
|
161
|
+
}
|
|
162
|
+
if (!opts.driverAge)
|
|
163
|
+
throw new UsageError('--driver-age is required.');
|
|
164
|
+
if (!opts.residenceCountry)
|
|
165
|
+
throw new UsageError('--residence-country is required.');
|
|
166
|
+
body = buildCarSearchBody(opts);
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
exitWithError(error, globals);
|
|
170
|
+
}
|
|
171
|
+
const data = await client.carSearch(body);
|
|
172
|
+
if (globals.format === 'table') {
|
|
173
|
+
const where = opts.pickUpAirport ?? opts.pickUpPlace ?? opts.pickUpGeo ?? '(pick-up)';
|
|
174
|
+
const resolved = data.resolved_place?.name;
|
|
175
|
+
console.log(`\nCar offers: ${where}${resolved && resolved !== where ? ` → ${resolved}` : ''} (${opts.pickUpDateTime}${opts.dropOffDateTime ? ` → ${opts.dropOffDateTime}` : ''})\n`);
|
|
176
|
+
for (const line of formatCarTable(data.offers ?? [], data.candidates ?? [])) {
|
|
177
|
+
console.log(line);
|
|
178
|
+
}
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
output(data, { format: globals.format });
|
|
182
|
+
}));
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=car-search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"car-search.js","sourceRoot":"","sources":["../../../src/commands/cars/car-search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type CarAmount, type CarGeo } from '@gojinko/api-client';
|
|
2
|
+
/**
|
|
3
|
+
* Branch-local date-time, no timezone: `2026-09-12T10:00:00`.
|
|
4
|
+
*
|
|
5
|
+
* Car rentals are quoted against the branch's own wall clock, so the contract
|
|
6
|
+
* deliberately carries no offset. Accepting a `Z`-suffixed or offset-bearing
|
|
7
|
+
* value here would send the provider a time it silently reinterprets as local,
|
|
8
|
+
* shifting a pick-up by hours without any error.
|
|
9
|
+
*/
|
|
10
|
+
export declare const LOCAL_DATETIME_RE: RegExp;
|
|
11
|
+
/** Validate a branch-local date-time, throwing a UsageError naming the flag. */
|
|
12
|
+
export declare function requireLocalDateTime(value: string | undefined, flag: string): void;
|
|
13
|
+
/**
|
|
14
|
+
* Render a CarAmount for the table view, through the api-client money reader
|
|
15
|
+
* (JIN-2220): the server's `display` string when the amount carries one, else
|
|
16
|
+
* "EUR 412.50" built from the reading — ISO code first, the same shape every
|
|
17
|
+
* other money line the CLI prints.
|
|
18
|
+
*
|
|
19
|
+
* A CarAmount is integer minor units under `value` with `decimal_places`. The
|
|
20
|
+
* reader divides by the scale that came with the amount and, when the field is
|
|
21
|
+
* genuinely absent, falls back to the currency's published ISO digits — never
|
|
22
|
+
* an assumed 2, which would print a JPY deposit of {value: 20000} as
|
|
23
|
+
* "JPY 200.00". An explicit 0 is a real price (a zero cancellation fee) and
|
|
24
|
+
* prints as one; an absent or unreadable amount prints nothing rather than a
|
|
25
|
+
* plausible wrong figure.
|
|
26
|
+
*/
|
|
27
|
+
export declare function formatCarAmount(amount: CarAmount | undefined): string;
|
|
28
|
+
/**
|
|
29
|
+
* Parse `--*-geo <lat,lng,range>` into a CarGeo.
|
|
30
|
+
*
|
|
31
|
+
* One flag rather than three keeps the three parts inseparable: a geo search is
|
|
32
|
+
* meaningless with a latitude but no range, and Commander cannot express
|
|
33
|
+
* "these three flags are required together".
|
|
34
|
+
*/
|
|
35
|
+
export declare function parseGeo(raw: string, flag: string): CarGeo;
|
|
36
|
+
/**
|
|
37
|
+
* Resolve the exactly-one-of location flags for one side of the rental.
|
|
38
|
+
*
|
|
39
|
+
* `airport_code`, `place` and `geo` are mutually exclusive server-side — a rule
|
|
40
|
+
* the JSON Schema cannot express, so it is enforced here rather than surfacing
|
|
41
|
+
* as an opaque 400.
|
|
42
|
+
*/
|
|
43
|
+
export declare function resolveLocation(side: 'pick-up' | 'drop-off', opts: {
|
|
44
|
+
airport?: string;
|
|
45
|
+
place?: string;
|
|
46
|
+
geo?: string;
|
|
47
|
+
}, { required }: {
|
|
48
|
+
required: boolean;
|
|
49
|
+
}): {
|
|
50
|
+
airport_code?: string;
|
|
51
|
+
place?: string;
|
|
52
|
+
geo?: CarGeo;
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=car-shared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"car-shared.d.ts","sourceRoot":"","sources":["../../../src/commands/cars/car-shared.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,SAAS,EAAE,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAGhF;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,QAA0C,CAAC;AAEzE,gFAAgF;AAChF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAMlF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAKrE;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAa1D;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,SAAS,GAAG,UAAU,EAC5B,IAAI,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EACxD,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,GAClC;IAAE,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAsBzD"}
|