@agenzo/merchant-cli 0.5.1 → 0.5.2
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 +10 -12
- package/dist/index.js +294 -121
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -896,7 +896,9 @@ var quoteSchema = {
|
|
|
896
896
|
"passenger-count": { type: "int", required: false, description: "Number of passengers" },
|
|
897
897
|
"luggage-count": { type: "int", required: false, description: "Number of luggage items" },
|
|
898
898
|
"passenger-email": { type: "string", required: false, description: "Passenger email address" },
|
|
899
|
-
"children-count": { type: "int", required: false, description: "Number of children" }
|
|
899
|
+
"children-count": { type: "int", required: false, description: "Number of children" },
|
|
900
|
+
"infant-count": { type: "int", required: false, description: "Number of infants (passenger breakdown; affects seat pricing)" },
|
|
901
|
+
"toddler-count": { type: "int", required: false, description: "Number of toddlers (passenger breakdown; affects seat pricing)" }
|
|
900
902
|
},
|
|
901
903
|
response: {
|
|
902
904
|
vehicle_classes: {
|
|
@@ -1241,7 +1243,7 @@ var hotelCreateOrderSchema = {
|
|
|
1241
1243
|
cli: CLI_NAME,
|
|
1242
1244
|
noun: HOTEL_NOUN,
|
|
1243
1245
|
verb: "create-order",
|
|
1244
|
-
description: "Create
|
|
1246
|
+
description: "Create a hotel order WITHOUT charging any account (re-check availability + createOrder upstream), using a product_token and price_items from a recent quote. The order enters AWAITING_PAYMENT \u2014 no money is moved until pay-order. check-in/check-out, guest counts, total-amount and price-items must match the quote",
|
|
1245
1247
|
flags: {
|
|
1246
1248
|
"product-token": { type: "string", required: true, description: "Chosen rate token from quote (quote.response.rates[].product_token). Pass verbatim" },
|
|
1247
1249
|
"total-amount": { type: "float", required: true, description: "Total price for the stay (quote.response.rates[].total_price.amount)", constraints: "DECIMAL units (NOT cents), 0.01\u2013999999999.99. Must equal the chosen rate total_price.amount" },
|
|
@@ -1260,69 +1262,74 @@ var hotelCreateOrderSchema = {
|
|
|
1260
1262
|
"contact-email": { type: "string", required: false, description: "Booking contact email" },
|
|
1261
1263
|
"arrive-time": { type: "string", required: false, description: "Expected arrival time", constraints: "HH:mm, hotel local time" },
|
|
1262
1264
|
"special-requests": { type: "string", required: false, description: "Free-text special requests (non-binding)" },
|
|
1263
|
-
"payment-method-id": { type: "string", required: false, description: "pay_per_call only: charge this SPECIFIC already-bound card instead of the platform auto-selected default/most-recent one. Ignored for monthly_settlement.", constraints: "Must be an ACTIVE card belonging to this developer, else PAYMENT_METHOD_REQUIRED" },
|
|
1264
1265
|
"idempotency-key": {
|
|
1265
1266
|
type: "string",
|
|
1266
1267
|
required: true,
|
|
1267
|
-
description: "Unique key forwarded verbatim as the Idempotency-Key header; never auto-generated. Derive from business intent (e.g. hash(user_id + product_token + check_in)) and reuse the SAME key when retrying the
|
|
1268
|
+
description: "Unique key forwarded verbatim as the Idempotency-Key header; never auto-generated. Derive from business intent (e.g. hash(user_id + product_token + check_in)) and reuse the SAME key when retrying the same order creation",
|
|
1268
1269
|
constraints: "1-128 chars [A-Za-z0-9_-]"
|
|
1269
1270
|
}
|
|
1270
1271
|
},
|
|
1271
1272
|
response: {
|
|
1272
|
-
order_id: { type: "string", description: "Our order reference (coOrderCode). Use as --order-id in get / cancel." },
|
|
1273
|
+
order_id: { type: "string", description: "Our order reference (coOrderCode). Use as --order-id in pay-order / get / cancel." },
|
|
1273
1274
|
fc_order_code: { type: "string", description: "Supplier order reference. Needed as --fc-order-code in cancel and checkout." },
|
|
1274
|
-
total_amount: { type: "float", description: "Amount
|
|
1275
|
-
currency: { type: "string", description: "ISO 4217 currency code
|
|
1275
|
+
total_amount: { type: "float", description: "Amount to be paid in pay-order (DECIMAL units). The order is locked in AWAITING_PAYMENT \u2014 no charge has been made." },
|
|
1276
|
+
currency: { type: "string", description: "ISO 4217 currency code for the payment." }
|
|
1276
1277
|
},
|
|
1277
1278
|
example: {
|
|
1278
1279
|
command: `agenzo-merchant-cli hotel-redaug create-order --product-token <tok> --total-amount 10.00 --currency CNY --price-items '[{"sale_date":"2026-08-20","sale_price":10.00,"breakfast_num":0}]' --check-in 2026-08-20 --check-out 2026-08-21 --adults 2 --guest-name "Zhang San" --contact-name "Zhang San" --contact-phone 13800138000 --idempotency-key create-h1n2`,
|
|
1279
|
-
output_summary: "Returns {order_id, fc_order_code, total_amount, currency}. The order is
|
|
1280
|
+
output_summary: "Returns {order_id, fc_order_code, total_amount, currency}. The order is AWAITING_PAYMENT (no charge). Use order_id as --order-id in pay-order to settle."
|
|
1280
1281
|
},
|
|
1281
1282
|
error_recovery: {
|
|
1282
1283
|
NO_AVAILABILITY: "Availability vanished between quote and create-order. Re-quote the same hotel; if still empty, quote another hotel or change dates. Use a NEW idempotency-key for the new product_token.",
|
|
1283
1284
|
PRICE_CHANGED: "The price/price_items no longer match upstream. Re-quote to get fresh total_price and price_items, confirm the new price with the user, then create-order with the new values and a NEW idempotency-key.",
|
|
1284
|
-
NAME_FORMAT_INVALID: "The supplier requires the guest name in a specific format (commonly Latin first/last name for international properties). Re-collect guest-name in Latin letters and retry with
|
|
1285
|
-
PAYMENT_METHOD_REQUIRED: "pay_per_call developer has no ACTIVE bound card at all, OR the explicitly-passed --payment-method-id is missing/not ACTIVE/does not belong to this developer. Direct the user to bind a card, drop --payment-method-id to auto-select, or pass a valid one.",
|
|
1286
|
-
ACCOUNT_INSUFFICIENT_BALANCE: "monthly_settlement developer settlement credit is insufficient (check via admin-cli accounts get). Direct the user to top up offline; do NOT retry until funded.",
|
|
1287
|
-
ACCOUNT_NOT_FOUND: "monthly_settlement developer has no settlement account. Direct the user to complete contract signing.",
|
|
1288
|
-
ACCOUNT_SUSPENDED: "The settlement account is suspended. Direct the user to contact support; do NOT retry.",
|
|
1285
|
+
NAME_FORMAT_INVALID: "The supplier requires the guest name in a specific format (commonly Latin first/last name for international properties). Re-collect guest-name in Latin letters and retry with the SAME idempotency-key.",
|
|
1289
1286
|
PARAM_IDEMPOTENCY_KEY_REQUIRED: "Add a unique --idempotency-key derived from business intent and retry.",
|
|
1290
1287
|
PARAM_IDEMPOTENCY_KEY_CONFLICT: "The same idempotency-key was used with different parameters. Retry with the original parameters, or generate a NEW key for the new parameters.",
|
|
1291
|
-
UPSTREAM_ERROR: "Transient upstream error during checkBooking or createOrder.
|
|
1288
|
+
UPSTREAM_ERROR: "Transient upstream error during checkBooking or createOrder. Retry once after ~2s backoff with the SAME idempotency-key."
|
|
1292
1289
|
}
|
|
1293
1290
|
};
|
|
1294
1291
|
var hotelPayOrderSchema = {
|
|
1295
1292
|
cli: CLI_NAME,
|
|
1296
1293
|
noun: HOTEL_NOUN,
|
|
1297
1294
|
verb: "pay-order",
|
|
1298
|
-
description:
|
|
1295
|
+
description: 'Settle an existing AWAITING_PAYMENT order created by create-order. Takes only --order-id; the billing path is decided server-side by the order billing_mode. monthly_settlement deducts from the developer credit account then confirms with the supplier; pay_per_call verifies the user EVO payment \u2014 the EVO merchantTransID IS the order_id (the user pays via EVO under the order_id), so the platform queries EVO for that order_id and requires an exact amount/currency match before confirming (the response settlement_path is then "pay_per_call"). On success the order becomes PAID. Supports --watch to poll on PAYMENT_NOT_COMPLETED until PAID',
|
|
1299
1296
|
flags: {
|
|
1300
|
-
"order-id": { type: "string", required: true, description: "Order to
|
|
1297
|
+
"order-id": { type: "string", required: true, description: "Order to settle (create-order.response.order_id). For pay_per_call this is also the EVO merchantTransID the user paid under." },
|
|
1301
1298
|
"idempotency-key": {
|
|
1302
1299
|
type: "string",
|
|
1303
1300
|
required: true,
|
|
1304
|
-
description: "Unique key forwarded verbatim as the Idempotency-Key header; never auto-generated. Derive from business intent and reuse the SAME key when retrying the same
|
|
1301
|
+
description: "Unique key forwarded verbatim as the Idempotency-Key header; never auto-generated. Derive from business intent (e.g. hash(order_id + attempt)) and reuse the SAME key when retrying the same payment",
|
|
1305
1302
|
constraints: "1-128 chars [A-Za-z0-9_-]"
|
|
1306
1303
|
},
|
|
1307
|
-
watch: { type: "bool", required: false, default: false, description: "
|
|
1304
|
+
watch: { type: "bool", required: false, default: false, description: "Poll automatically on PAYMENT_NOT_COMPLETED until PAID or timeout. Each iteration emits one NDJSON line" },
|
|
1308
1305
|
"watch-interval": { type: "int", required: false, default: 5, description: "Seconds between poll attempts in --watch mode" },
|
|
1309
1306
|
"watch-timeout": { type: "int", required: false, default: 300, description: "Total seconds before giving up in --watch mode" }
|
|
1310
1307
|
},
|
|
1311
1308
|
response: {
|
|
1312
1309
|
order_id: { type: "string", description: "Order reference." },
|
|
1313
|
-
settlement_path: { type: "string", description: "
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1310
|
+
settlement_path: { type: "string", description: "Which billing path was used: 'monthly_settlement' or 'pay_per_call' (same tokens as billing_mode)." },
|
|
1311
|
+
amount: { type: "float", description: "Settled amount in DECIMAL units." },
|
|
1312
|
+
currency: { type: "string", description: "ISO 4217 currency code of the settlement." },
|
|
1313
|
+
pay_status: { type: "int", description: "Upstream pay status (1 = paid). The order internal status is now PAID." },
|
|
1314
|
+
settled_at: { type: "string", description: "ISO 8601 settlement timestamp. On idempotent replay, the original first settled_at is returned unchanged." }
|
|
1317
1315
|
},
|
|
1318
1316
|
example: {
|
|
1319
1317
|
command: "agenzo-merchant-cli hotel-redaug pay-order --order-id hho_01KWC63Z5CD6CKBM33Q7SC2ZDT --idempotency-key pay-h1n2",
|
|
1320
|
-
output_summary: '
|
|
1318
|
+
output_summary: 'Settlement path is chosen server-side by billing_mode. monthly_settlement: deducts from the credit account, confirms with the supplier. Returns {order_id, settlement_path:"monthly_settlement", amount, currency, pay_status:1, settled_at}. Order becomes PAID. For pay_per_call the user must have already paid via EVO using the order_id as the merchantTransID; the same command settles it (settlement_path:"pay_per_call").'
|
|
1321
1319
|
},
|
|
1322
1320
|
error_recovery: {
|
|
1323
|
-
|
|
1321
|
+
PAYMENT_NOT_COMPLETED: "pay_per_call only: EVO has not yet confirmed the payment for this order_id. Retry with the SAME idempotency-key after a delay, or use --watch to poll automatically until PAID.",
|
|
1322
|
+
PAYMENT_NOT_FOUND: "No EVO transaction found for this order_id. Confirm the user paid via EVO using the order_id as the merchantTransID; do NOT retry blindly.",
|
|
1323
|
+
PAYMENT_AMOUNT_MISMATCH: "The EVO-confirmed amount/currency does not match the order. The user must pay the exact order amount in the exact currency under the order_id. Do NOT retry until corrected.",
|
|
1324
|
+
PAYMENT_QUERY_FAILED: "EVO gateway query failed (transport/timeout). Retry once after ~5s with the SAME idempotency-key.",
|
|
1325
|
+
BILLING_MODE_MISMATCH: "The order billing_mode is not a recognized settlement mode (only monthly_settlement and pay_per_call are valid). Check the developer billing_mode; do NOT retry blindly.",
|
|
1326
|
+
INVALID_ORDER_STATE: "The order is not in AWAITING_PAYMENT (it may already be PAID or CANCELLED). Check status via get; do NOT retry pay-order.",
|
|
1324
1327
|
ORDER_NOT_FOUND: "No order for this --order-id. Verify it is the order_id returned by create-order. Do NOT retry.",
|
|
1325
|
-
|
|
1328
|
+
ACCOUNT_INSUFFICIENT_BALANCE: "Settlement credit is insufficient (check via admin-cli accounts get). Direct the user to top up offline.",
|
|
1329
|
+
ACCOUNT_NOT_FOUND: "Developer has no settlement account. Direct the user to complete contract signing.",
|
|
1330
|
+
ACCOUNT_SUSPENDED: "The settlement account is suspended. Direct the user to contact support; do NOT retry.",
|
|
1331
|
+
PAYORDER_FAILED: "Upstream payOrder failed after a successful deduction; the deduction was reversed and the order stays AWAITING_PAYMENT. Retry with the SAME idempotency-key after a delay.",
|
|
1332
|
+
PAYORDER_FAILED_AFTER_PAYMENT: "EVO payment confirmed but upstream payOrder failed. The order is recoverable (the EVO payment under the order_id is preserved). Contact support for reconciliation; do NOT retry automatically.",
|
|
1326
1333
|
PARAM_IDEMPOTENCY_KEY_REQUIRED: "Add a unique --idempotency-key and retry.",
|
|
1327
1334
|
PARAM_IDEMPOTENCY_KEY_CONFLICT: "Same idempotency-key used with different parameters. Use the original parameters, or generate a NEW key."
|
|
1328
1335
|
}
|
|
@@ -1736,6 +1743,69 @@ var hotelDetailSchema = {
|
|
|
1736
1743
|
PARAM_INVALID: "Ensure --hotel-id is a non-empty string, then retry."
|
|
1737
1744
|
}
|
|
1738
1745
|
};
|
|
1746
|
+
var ORDERS_NOUN = "orders";
|
|
1747
|
+
var unifiedOrdersListSchema = {
|
|
1748
|
+
cli: CLI_NAME,
|
|
1749
|
+
noun: ORDERS_NOUN,
|
|
1750
|
+
verb: "list",
|
|
1751
|
+
description: 'List orders across ALL providers (ride + hotel) in one call. Use this for generic "my orders" / "order history" requests. Prefer ride-elife/hotel-redaug list-orders when the user names a specific business.',
|
|
1752
|
+
flags: {
|
|
1753
|
+
"order-type": { type: "string", required: false, description: "Filter by provider: ride | hotel" },
|
|
1754
|
+
status: { type: "string", required: false, description: "Filter by NORMALIZED status (NOT the domain-specific status): PENDING | CONFIRMED | COMPLETED | CANCELLED | FAILED" },
|
|
1755
|
+
page: { type: "int", required: false, default: 1, description: "Page number", constraints: ">= 1" },
|
|
1756
|
+
"page-size": { type: "int", required: false, default: 20, description: "Items per page", constraints: ">= 1" }
|
|
1757
|
+
},
|
|
1758
|
+
response: {
|
|
1759
|
+
orders: {
|
|
1760
|
+
type: "array",
|
|
1761
|
+
description: "Slim cross-provider order-index items. For business-specific fields (vehicle_class, hotel_name, etc.), call `orders get --order-id <id>` or the domain-specific `get`.",
|
|
1762
|
+
items: {
|
|
1763
|
+
order_id: { type: "string", description: "Order id (rio_... for ride, hho_... for hotel). Pass to `orders get`." },
|
|
1764
|
+
order_type: { type: "string", description: "'ride' or 'hotel'" },
|
|
1765
|
+
status: { type: "string", description: "Normalized status: PENDING | CONFIRMED | COMPLETED | CANCELLED | FAILED" },
|
|
1766
|
+
amount: { type: "float|null", description: "Order amount in DECIMAL currency units (NOT cents)" },
|
|
1767
|
+
currency: { type: "string|null", description: "ISO 4217 currency code" },
|
|
1768
|
+
created_at: { type: "string|null", description: "ISO 8601 datetime" },
|
|
1769
|
+
updated_at: { type: "string|null", description: "ISO 8601 datetime" }
|
|
1770
|
+
}
|
|
1771
|
+
},
|
|
1772
|
+
total: { type: "int", description: "Total matching orders across all providers" },
|
|
1773
|
+
page: { type: "int", description: "Current page" },
|
|
1774
|
+
page_size: { type: "int", description: "Items per page" }
|
|
1775
|
+
},
|
|
1776
|
+
example: {
|
|
1777
|
+
command: "agenzo-merchant-cli orders list --page 1 --page-size 10",
|
|
1778
|
+
output_summary: "Returns a paginated, cross-provider list of orders (both ride and hotel). If the user then asks about one order, use its order_id with `orders get`."
|
|
1779
|
+
},
|
|
1780
|
+
error_recovery: {
|
|
1781
|
+
INVALID_REQUEST: "The --status value is not one of PENDING/CONFIRMED/COMPLETED/CANCELLED/FAILED. Fix and retry.",
|
|
1782
|
+
INTERNAL_ERROR: "Transient backend error. Retry once after a short delay.",
|
|
1783
|
+
PARAM_INVALID: "Ensure --page / --page-size are positive integers, then retry."
|
|
1784
|
+
}
|
|
1785
|
+
};
|
|
1786
|
+
var unifiedOrdersGetSchema = {
|
|
1787
|
+
cli: CLI_NAME,
|
|
1788
|
+
noun: ORDERS_NOUN,
|
|
1789
|
+
verb: "get",
|
|
1790
|
+
description: "Get a single order detail by id, regardless of which provider (ride/hotel) it belongs to. The platform resolves order_id -> provider internally and returns that provider's own detail shape.",
|
|
1791
|
+
flags: {
|
|
1792
|
+
"order-id": { type: "string", required: true, description: "Order id from `orders list` (rio_... for ride, hho_... for hotel)" }
|
|
1793
|
+
},
|
|
1794
|
+
response: {
|
|
1795
|
+
"(varies by order_type)": {
|
|
1796
|
+
type: "object",
|
|
1797
|
+
description: "The response shape is delegated to the owning provider and therefore varies: a ride order returns the same shape as `ride-elife get`; a hotel order returns the same shape as `hotel-redaug get`. Treat the result as an opaque object and surface whatever fields it contains \u2014 do NOT assume a fixed schema."
|
|
1798
|
+
}
|
|
1799
|
+
},
|
|
1800
|
+
example: {
|
|
1801
|
+
command: "agenzo-merchant-cli orders get --order-id hho_01K...",
|
|
1802
|
+
output_summary: "Returns the order's detail, delegated to its owning provider (ride or hotel)."
|
|
1803
|
+
},
|
|
1804
|
+
error_recovery: {
|
|
1805
|
+
ORDER_NOT_FOUND: "The order_id does not exist or does not belong to this developer/org. Verify the id from `orders list`. Do NOT retry blindly.",
|
|
1806
|
+
INTERNAL_ERROR: "The order's provider detail lookup is temporarily unavailable. Retry once after a short delay."
|
|
1807
|
+
}
|
|
1808
|
+
};
|
|
1739
1809
|
|
|
1740
1810
|
// src/ride-elife/quote.ts
|
|
1741
1811
|
function need(value, flag) {
|
|
@@ -2285,7 +2355,9 @@ var SERVICE_REGISTRY = [
|
|
|
2285
2355
|
},
|
|
2286
2356
|
workflow: ["quote", "book", "get (poll for status)", "cancel (optional)"],
|
|
2287
2357
|
since: "2026-06-01",
|
|
2288
|
-
discovery: {
|
|
2358
|
+
discovery: {
|
|
2359
|
+
help_command: "agenzo-merchant-cli ride-elife --help"
|
|
2360
|
+
}
|
|
2289
2361
|
},
|
|
2290
2362
|
{
|
|
2291
2363
|
service_id: "svc_01J0HT5REDAUG0001",
|
|
@@ -2337,7 +2409,9 @@ var SERVICE_REGISTRY = [
|
|
|
2337
2409
|
"cancel / checkout (optional)"
|
|
2338
2410
|
],
|
|
2339
2411
|
since: "2026-06-25",
|
|
2340
|
-
discovery: {
|
|
2412
|
+
discovery: {
|
|
2413
|
+
help_command: "agenzo-merchant-cli hotel-redaug --help"
|
|
2414
|
+
}
|
|
2341
2415
|
}
|
|
2342
2416
|
];
|
|
2343
2417
|
function findService(serviceId) {
|
|
@@ -2433,78 +2507,30 @@ function registerServicesListCommand(parent, deps) {
|
|
|
2433
2507
|
}
|
|
2434
2508
|
|
|
2435
2509
|
// src/services/get.ts
|
|
2436
|
-
function buildVerbsSummary(data) {
|
|
2437
|
-
const verbs = Array.isArray(data.verbs) ? data.verbs : [];
|
|
2438
|
-
const verbDescriptions = data.verb_descriptions ?? {};
|
|
2439
|
-
const schema = data.schema_content;
|
|
2440
|
-
const schemaVerbs = schema?.verbs;
|
|
2441
|
-
return verbs.map((verb) => {
|
|
2442
|
-
const item = {
|
|
2443
|
-
verb,
|
|
2444
|
-
description: verbDescriptions[verb] ?? String(schemaVerbs?.[verb]?.description ?? "")
|
|
2445
|
-
};
|
|
2446
|
-
const annotations = schemaVerbs?.[verb]?.annotations;
|
|
2447
|
-
if (annotations) item.annotations = annotations;
|
|
2448
|
-
return item;
|
|
2449
|
-
});
|
|
2450
|
-
}
|
|
2451
|
-
function toServiceLayerView(data) {
|
|
2452
|
-
const schema = data.schema_content;
|
|
2453
|
-
const view = {
|
|
2454
|
-
service_id: data.service_id,
|
|
2455
|
-
cli_noun: data.cli_noun,
|
|
2456
|
-
category: data.category,
|
|
2457
|
-
name: data.name,
|
|
2458
|
-
summary: data.description ?? schema?.summary,
|
|
2459
|
-
status: data.status ?? "available",
|
|
2460
|
-
provider: data.provider,
|
|
2461
|
-
version: data.version
|
|
2462
|
-
};
|
|
2463
|
-
if (schema?.selection_hints) view.selection_hints = schema.selection_hints;
|
|
2464
|
-
const discovery = data.discovery;
|
|
2465
|
-
view.schema_ref = schema?.schema_ref ?? {
|
|
2466
|
-
help_command: discovery?.help_command,
|
|
2467
|
-
...discovery?.schema_url ? { schema_url: discovery.schema_url } : {}
|
|
2468
|
-
};
|
|
2469
|
-
if (schema?.conventions) view.conventions = schema.conventions;
|
|
2470
|
-
if (schema?.workflow) {
|
|
2471
|
-
view.workflow = schema.workflow;
|
|
2472
|
-
} else if (Array.isArray(data.workflow)) {
|
|
2473
|
-
view.workflow = { description: data.workflow.join(" \u2192 ") };
|
|
2474
|
-
}
|
|
2475
|
-
view.verbs_summary = buildVerbsSummary(data);
|
|
2476
|
-
if (schema?.cross_service_recovery) view.cross_service_recovery = schema.cross_service_recovery;
|
|
2477
|
-
return view;
|
|
2478
|
-
}
|
|
2479
2510
|
function formatServiceGet(s) {
|
|
2480
2511
|
const lines = [
|
|
2481
2512
|
["Service ID", String(s.service_id ?? "")],
|
|
2482
|
-
["CLI Noun", String(s.cli_noun ?? "")],
|
|
2483
|
-
["Category", String(s.category ?? "")],
|
|
2484
2513
|
["Name", String(s.name ?? "")],
|
|
2485
|
-
["
|
|
2486
|
-
["
|
|
2514
|
+
["Description", String(s.description ?? s.summary ?? "")],
|
|
2515
|
+
["Category", String(s.category ?? "")],
|
|
2516
|
+
["Version", String(s.version ?? "")],
|
|
2487
2517
|
["Provider", String(s.provider ?? "")],
|
|
2488
|
-
["
|
|
2518
|
+
["CLI Noun", String(s.cli_noun ?? "")],
|
|
2519
|
+
["Verbs", Array.isArray(s.verbs) ? s.verbs.join(", ") : ""]
|
|
2489
2520
|
];
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
for (const key of [
|
|
2493
|
-
"selection_hints",
|
|
2494
|
-
"schema_ref",
|
|
2495
|
-
"conventions",
|
|
2496
|
-
"workflow",
|
|
2497
|
-
"verbs_summary",
|
|
2498
|
-
"cross_service_recovery"
|
|
2499
|
-
]) {
|
|
2500
|
-
if (s[key] !== void 0) {
|
|
2501
|
-
sections.push(`${key}:
|
|
2502
|
-
${JSON.stringify(s[key], null, 2)}`);
|
|
2503
|
-
}
|
|
2521
|
+
if (Array.isArray(s.workflow) && s.workflow.length > 0) {
|
|
2522
|
+
lines.push(["Workflow", s.workflow.join(" \u2192 ")]);
|
|
2504
2523
|
}
|
|
2505
|
-
|
|
2524
|
+
const block = Formatter.keyValue(lines);
|
|
2525
|
+
const verbDescriptions = s.verb_descriptions;
|
|
2526
|
+
if (verbDescriptions && Object.keys(verbDescriptions).length > 0) {
|
|
2527
|
+
const detail = Object.entries(verbDescriptions).map(([verb, desc]) => ` ${verb}: ${desc}`).join("\n");
|
|
2528
|
+
return `${block}
|
|
2506
2529
|
|
|
2507
|
-
|
|
2530
|
+
Verb descriptions:
|
|
2531
|
+
${detail}`;
|
|
2532
|
+
}
|
|
2533
|
+
return block;
|
|
2508
2534
|
}
|
|
2509
2535
|
function gateCapability(data, localCaps) {
|
|
2510
2536
|
const noun = String(data.cli_noun ?? "");
|
|
@@ -2525,7 +2551,7 @@ function gateCapability(data, localCaps) {
|
|
|
2525
2551
|
return out;
|
|
2526
2552
|
}
|
|
2527
2553
|
function registerServiceGetCommand(parent, deps) {
|
|
2528
|
-
const cmd = parent.command("get <service-id>").description("Retrieve a single merchant service
|
|
2554
|
+
const cmd = parent.command("get <service-id>").description("Retrieve a single merchant service by id (includes full schema)").option("--api-key <key>", "API Key for authentication (X-Api-Key)");
|
|
2529
2555
|
cmd.action(async (serviceId) => {
|
|
2530
2556
|
const opts = cmd.optsWithGlobals();
|
|
2531
2557
|
const format = resolveFormat(opts.format);
|
|
@@ -2554,7 +2580,7 @@ function registerServiceGetCommand(parent, deps) {
|
|
|
2554
2580
|
`Service '${serviceId}' is not available in this CLI. Run "services list" to see available services.`
|
|
2555
2581
|
);
|
|
2556
2582
|
}
|
|
2557
|
-
const resolved =
|
|
2583
|
+
const resolved = data;
|
|
2558
2584
|
const configManager = new ConfigManager();
|
|
2559
2585
|
const commandResult = {
|
|
2560
2586
|
data: resolved,
|
|
@@ -2564,6 +2590,105 @@ function registerServiceGetCommand(parent, deps) {
|
|
|
2564
2590
|
});
|
|
2565
2591
|
}
|
|
2566
2592
|
|
|
2593
|
+
// src/orders/list.ts
|
|
2594
|
+
var DEFAULT_PAGE2 = "1";
|
|
2595
|
+
var DEFAULT_PAGE_SIZE2 = "20";
|
|
2596
|
+
function positiveInt2(value, flag) {
|
|
2597
|
+
const n = Number(value);
|
|
2598
|
+
if (!Number.isFinite(n) || !Number.isInteger(n) || n < 1) {
|
|
2599
|
+
throw new CliError("PARAM_INVALID", `--${flag} must be a positive integer.`);
|
|
2600
|
+
}
|
|
2601
|
+
return String(n);
|
|
2602
|
+
}
|
|
2603
|
+
function formatOrders2(data) {
|
|
2604
|
+
const orders = data.orders ?? [];
|
|
2605
|
+
if (orders.length === 0) {
|
|
2606
|
+
return Formatter.status("info", "No orders found");
|
|
2607
|
+
}
|
|
2608
|
+
const headers = ["Order ID", "Type", "Status", "Amount", "Currency"];
|
|
2609
|
+
const rows = orders.map((o) => [
|
|
2610
|
+
String(o.order_id ?? "-"),
|
|
2611
|
+
String(o.order_type ?? "-"),
|
|
2612
|
+
String(o.status ?? "-"),
|
|
2613
|
+
String(o.amount ?? "-"),
|
|
2614
|
+
String(o.currency ?? "-")
|
|
2615
|
+
]);
|
|
2616
|
+
const summary = [
|
|
2617
|
+
["Total", String(data.total ?? "-")],
|
|
2618
|
+
["Page", String(data.page ?? "-")],
|
|
2619
|
+
["Page size", String(data.page_size ?? "-")]
|
|
2620
|
+
];
|
|
2621
|
+
return `${Formatter.table(headers, rows)}
|
|
2622
|
+
${Formatter.keyValue(summary)}`;
|
|
2623
|
+
}
|
|
2624
|
+
function registerOrdersListCommand(parent, deps) {
|
|
2625
|
+
const cmd = parent.command("list").description("List orders across ALL providers (ride + hotel), with optional type/status filters").option("--api-key <key>", "API Key for authentication (X-Api-Key)").option("--order-type <type>", "Filter by order type: ride | hotel").option("--status <status>", "Filter by normalized status: PENDING | CONFIRMED | COMPLETED | CANCELLED | FAILED").option("--page <page>", "Page number", DEFAULT_PAGE2).option("--page-size <size>", "Page size", DEFAULT_PAGE_SIZE2);
|
|
2626
|
+
attachSchemaHelp(cmd, unifiedOrdersListSchema);
|
|
2627
|
+
cmd.action(async () => {
|
|
2628
|
+
const opts = cmd.optsWithGlobals();
|
|
2629
|
+
const format = resolveFormat(opts.format);
|
|
2630
|
+
const apiKey = await PromptEngine.resolveInput(opts.apiKey, {
|
|
2631
|
+
message: "API Key:",
|
|
2632
|
+
type: "password"
|
|
2633
|
+
});
|
|
2634
|
+
const params = {
|
|
2635
|
+
page: positiveInt2(opts.page ?? DEFAULT_PAGE2, "page"),
|
|
2636
|
+
page_size: positiveInt2(opts.pageSize ?? DEFAULT_PAGE_SIZE2, "page-size")
|
|
2637
|
+
};
|
|
2638
|
+
if (opts.orderType !== void 0) params.order_type = opts.orderType;
|
|
2639
|
+
if (opts.status !== void 0) params.status = opts.status;
|
|
2640
|
+
const spinner = format === "json" ? null : createSpinner("Fetching orders...");
|
|
2641
|
+
const result = await deps.apiClient.get(
|
|
2642
|
+
"/orders",
|
|
2643
|
+
{ type: "api-key", key: apiKey },
|
|
2644
|
+
params
|
|
2645
|
+
);
|
|
2646
|
+
spinner?.stop();
|
|
2647
|
+
if (!result.success) {
|
|
2648
|
+
throw CliError.fromApi(result, { auth: "api-key" });
|
|
2649
|
+
}
|
|
2650
|
+
const data = result.data;
|
|
2651
|
+
const configManager = new ConfigManager();
|
|
2652
|
+
const commandResult = {
|
|
2653
|
+
data,
|
|
2654
|
+
text: () => formatOrders2(data)
|
|
2655
|
+
};
|
|
2656
|
+
await renderWithContext(commandResult, { format }, configManager);
|
|
2657
|
+
});
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2660
|
+
// src/orders/get.ts
|
|
2661
|
+
function registerOrdersGetCommand(parent, deps) {
|
|
2662
|
+
const cmd = parent.command("get").description("Get a single order detail by id, regardless of provider (ride/hotel)").option("--api-key <key>", "API Key for authentication (X-Api-Key)").requiredOption("--order-id <id>", "Order id (e.g. rio_... or hho_...)");
|
|
2663
|
+
attachSchemaHelp(cmd, unifiedOrdersGetSchema);
|
|
2664
|
+
cmd.action(async () => {
|
|
2665
|
+
const opts = cmd.optsWithGlobals();
|
|
2666
|
+
const format = resolveFormat(opts.format);
|
|
2667
|
+
const apiKey = await PromptEngine.resolveInput(opts.apiKey, {
|
|
2668
|
+
message: "API Key:",
|
|
2669
|
+
type: "password"
|
|
2670
|
+
});
|
|
2671
|
+
const spinner = format === "json" ? null : createSpinner("Fetching order...");
|
|
2672
|
+
const result = await deps.apiClient.get(
|
|
2673
|
+
`/orders/${encodeURIComponent(opts.orderId)}`,
|
|
2674
|
+
{ type: "api-key", key: apiKey }
|
|
2675
|
+
);
|
|
2676
|
+
spinner?.stop();
|
|
2677
|
+
if (!result.success) {
|
|
2678
|
+
throw CliError.fromApi(result, { auth: "api-key" });
|
|
2679
|
+
}
|
|
2680
|
+
const data = result.data;
|
|
2681
|
+
const configManager = new ConfigManager();
|
|
2682
|
+
const commandResult = {
|
|
2683
|
+
data,
|
|
2684
|
+
text: () => Formatter.keyValue(
|
|
2685
|
+
Object.entries(data).map(([k, v]) => [k, typeof v === "object" ? JSON.stringify(v) : String(v)])
|
|
2686
|
+
)
|
|
2687
|
+
};
|
|
2688
|
+
await renderWithContext(commandResult, { format }, configManager);
|
|
2689
|
+
});
|
|
2690
|
+
}
|
|
2691
|
+
|
|
2567
2692
|
// src/hotel-redaug/search.ts
|
|
2568
2693
|
function need5(value, flag) {
|
|
2569
2694
|
if (value === void 0) {
|
|
@@ -2585,7 +2710,7 @@ function coord(value, flag, min, max) {
|
|
|
2585
2710
|
}
|
|
2586
2711
|
return n;
|
|
2587
2712
|
}
|
|
2588
|
-
function
|
|
2713
|
+
function positiveInt3(value, flag) {
|
|
2589
2714
|
const n = num3(value, flag);
|
|
2590
2715
|
if (!Number.isInteger(n) || n <= 0) {
|
|
2591
2716
|
throw new CliError("PARAM_INVALID", `--${flag} must be a positive integer.`);
|
|
@@ -2662,8 +2787,8 @@ function registerHotelSearchCommand(parent, deps) {
|
|
|
2662
2787
|
if (!(checkOut > checkIn)) {
|
|
2663
2788
|
throw new CliError("PARAM_INVALID", "--check-out must be strictly after --check-in.");
|
|
2664
2789
|
}
|
|
2665
|
-
const page =
|
|
2666
|
-
const pageSize =
|
|
2790
|
+
const page = positiveInt3(opts.page, "page");
|
|
2791
|
+
const pageSize = positiveInt3(opts.pageSize, "page-size");
|
|
2667
2792
|
const body = {};
|
|
2668
2793
|
if (hasDestination) {
|
|
2669
2794
|
body.destination_id = need5(opts.destinationId, "destination-id");
|
|
@@ -2861,15 +2986,19 @@ function formatCreateOrder(data) {
|
|
|
2861
2986
|
const rows = rooms.map((r) => [String(r.room_index ?? "-"), String(r.guest_name ?? "-")]);
|
|
2862
2987
|
out.push("", Formatter.table(headers, rows));
|
|
2863
2988
|
}
|
|
2989
|
+
out.push(
|
|
2990
|
+
"",
|
|
2991
|
+
Formatter.status(
|
|
2992
|
+
"info",
|
|
2993
|
+
`Order created with status AWAITING_PAYMENT \u2014 proceed with 'hotel-redaug pay-order --order-id ${data.order_id ?? "<order_id>"}' to settle.`
|
|
2994
|
+
)
|
|
2995
|
+
);
|
|
2864
2996
|
return out.join("\n");
|
|
2865
2997
|
}
|
|
2866
2998
|
function registerHotelCreateOrderCommand(parent, deps) {
|
|
2867
|
-
const cmd = parent.command("create-order").description("Create
|
|
2868
|
-
"--payment-method-id <id>",
|
|
2869
|
-
"pay_per_call only: charge this SPECIFIC already-bound card instead of the platform auto-selected one"
|
|
2870
|
-
).option(
|
|
2999
|
+
const cmd = parent.command("create-order").description("Create a hotel order without charging (lock inventory, await payment)").option("--api-key <key>", "API Key for authentication (X-Api-Key)").option("--product-token <token>", "Chosen rate token from quote (rates[].product_token)").option("--total-amount <amount>", "Total stay price in decimal currency units (not cents)").option("--currency <currency>", "ISO 4217 currency code from the chosen rate").option("--price-items <json>", "Per-night price breakdown JSON array (copied verbatim from quote)").option("--check-in <date>", "Check-in date (YYYY-MM-DD, match quote)").option("--check-out <date>", "Check-out date (YYYY-MM-DD, strictly after check-in)").option("--room-num <n>", "Number of rooms (match quote)", "1").option("--adults <n>", "Adults per room (match quote)", "2").option("--children <n>", "Children per room (match quote)", "0").option("--nationality <code>", "Guest nationality (ISO country code)", "CN").option("--guest-name <name>", "Primary guest name").option("--contact-name <name>", "Booking contact name (may equal guest-name)").option("--contact-phone <phone>", "Booking contact phone number (digits)").option("--contact-country-code <code>", "Phone country calling code without '+'", "86").option("--contact-email <email>", "Booking contact email").option("--arrive-time <time>", "Expected arrival time (HH:mm, hotel local time)").option("--special-requests <text>", "Free-text special requests (non-binding)").option("--hotel-name <name>", "Hotel name (display-only, stored for order summary)").option(
|
|
2871
3000
|
"--idempotency-key <key>",
|
|
2872
|
-
"Idempotency key forwarded verbatim as the Idempotency-Key header
|
|
3001
|
+
"Idempotency key forwarded verbatim as the Idempotency-Key header"
|
|
2873
3002
|
);
|
|
2874
3003
|
attachSchemaHelp(cmd, hotelCreateOrderSchema);
|
|
2875
3004
|
cmd.action(async () => {
|
|
@@ -2911,10 +3040,10 @@ function registerHotelCreateOrderCommand(parent, deps) {
|
|
|
2911
3040
|
if (opts.contactEmail !== void 0) body.contact_email = opts.contactEmail;
|
|
2912
3041
|
if (opts.arriveTime !== void 0) body.arrive_time = opts.arriveTime;
|
|
2913
3042
|
if (opts.specialRequests !== void 0) body.special_requests = opts.specialRequests;
|
|
2914
|
-
if (opts.
|
|
3043
|
+
if (opts.hotelName !== void 0) body.hotel_name = opts.hotelName;
|
|
2915
3044
|
if (!isYes) {
|
|
2916
3045
|
const confirmed = await confirm3({
|
|
2917
|
-
message: `Create
|
|
3046
|
+
message: `Create this hotel order for ${totalAmount} ${currency} (check-in ${checkIn}, check-out ${checkOut})? This locks the rate but does NOT charge anything yet.`,
|
|
2918
3047
|
default: false
|
|
2919
3048
|
});
|
|
2920
3049
|
if (!confirmed) {
|
|
@@ -3073,20 +3202,23 @@ var DEFAULT_PAY_WATCH_TIMEOUT_SECONDS = 300;
|
|
|
3073
3202
|
function formatPayOrder(data) {
|
|
3074
3203
|
const lines = [
|
|
3075
3204
|
["Order ID", String(data.order_id ?? "-")],
|
|
3076
|
-
["Order status", String(data.
|
|
3205
|
+
["Order status", String(data.order_status ?? "-")]
|
|
3077
3206
|
];
|
|
3078
3207
|
if (data.settlement_path) lines.push(["Settlement path", String(data.settlement_path)]);
|
|
3079
|
-
if (data.
|
|
3080
|
-
|
|
3208
|
+
if (data.pay_status) lines.push(["Pay status", String(data.pay_status)]);
|
|
3209
|
+
if (data.total_amount != null && data.currency) {
|
|
3210
|
+
lines.push(["Amount", `${data.total_amount} ${data.currency}`]);
|
|
3081
3211
|
}
|
|
3212
|
+
if (data.billing_entry_id) lines.push(["Billing entry", String(data.billing_entry_id)]);
|
|
3213
|
+
if (data.merchant_trans_id) lines.push(["Merchant trans ID", String(data.merchant_trans_id)]);
|
|
3082
3214
|
return Formatter.keyValue(lines);
|
|
3083
3215
|
}
|
|
3084
3216
|
function isPaymentTerminal(record) {
|
|
3085
|
-
const status = record.
|
|
3217
|
+
const status = record.order_status;
|
|
3086
3218
|
return status === "PAID";
|
|
3087
3219
|
}
|
|
3088
3220
|
function registerHotelPayOrderCommand(parent, deps) {
|
|
3089
|
-
const cmd = parent.command("pay-order").description("
|
|
3221
|
+
const cmd = parent.command("pay-order").description("Settle an existing hotel order (path decided by billing_mode: monthly_settlement or pay_per_call)").option("--api-key <key>", "API Key for authentication (X-Api-Key)").option("--order-id <id>", "Order ID to pay (from create-order response)").option(
|
|
3090
3222
|
"--idempotency-key <key>",
|
|
3091
3223
|
"Idempotency key forwarded verbatim as the Idempotency-Key header"
|
|
3092
3224
|
).option("--watch", "Poll until PAID or timeout (NDJSON output)", false).option(
|
|
@@ -3121,11 +3253,11 @@ function registerHotelPayOrderCommand(parent, deps) {
|
|
|
3121
3253
|
const body = {};
|
|
3122
3254
|
if (!isYes) {
|
|
3123
3255
|
const confirmed = await confirm4({
|
|
3124
|
-
message: `
|
|
3125
|
-
default:
|
|
3256
|
+
message: `Settle hotel order ${orderId}? This moves money via the order's configured billing mode (monthly_settlement or pay_per_call).`,
|
|
3257
|
+
default: false
|
|
3126
3258
|
});
|
|
3127
3259
|
if (!confirmed) {
|
|
3128
|
-
throw new CliError("CLIENT_ABORTED", "
|
|
3260
|
+
throw new CliError("CLIENT_ABORTED", "Payment aborted by user.");
|
|
3129
3261
|
}
|
|
3130
3262
|
}
|
|
3131
3263
|
const path = `/hotel/${encodeURIComponent(orderId)}/pay`;
|
|
@@ -3168,7 +3300,19 @@ function registerHotelPayOrderCommand(parent, deps) {
|
|
|
3168
3300
|
return;
|
|
3169
3301
|
}
|
|
3170
3302
|
} else {
|
|
3171
|
-
|
|
3303
|
+
const errorCode = result.code ?? result.errorCode;
|
|
3304
|
+
const errorCodeStr = String(errorCode);
|
|
3305
|
+
if (errorCodeStr === "PAYMENT_NOT_COMPLETED" || errorCodeStr === "1933") {
|
|
3306
|
+
const errorLine = {
|
|
3307
|
+
watch_status: "pending",
|
|
3308
|
+
error_code: errorCodeStr === "1933" ? "PAYMENT_NOT_COMPLETED" : errorCodeStr,
|
|
3309
|
+
message: result.errorMessage ?? "Payment not yet completed"
|
|
3310
|
+
};
|
|
3311
|
+
lastRecord = errorLine;
|
|
3312
|
+
ndjsonWriteLine(errorLine);
|
|
3313
|
+
} else {
|
|
3314
|
+
throw CliError.fromApi(result, { auth: "api-key" });
|
|
3315
|
+
}
|
|
3172
3316
|
}
|
|
3173
3317
|
if (Date.now() + watchInterval * 1e3 >= deadline) {
|
|
3174
3318
|
const timeoutLine = {
|
|
@@ -3567,16 +3711,16 @@ function registerHotelGetCheckoutCommand(parent, deps) {
|
|
|
3567
3711
|
}
|
|
3568
3712
|
|
|
3569
3713
|
// src/hotel-redaug/list-orders.ts
|
|
3570
|
-
var
|
|
3571
|
-
var
|
|
3572
|
-
function
|
|
3714
|
+
var DEFAULT_PAGE3 = "1";
|
|
3715
|
+
var DEFAULT_PAGE_SIZE3 = "20";
|
|
3716
|
+
function positiveInt4(value, flag) {
|
|
3573
3717
|
const n = Number(value);
|
|
3574
3718
|
if (!Number.isFinite(n) || !Number.isInteger(n) || n < 1) {
|
|
3575
3719
|
throw new CliError("PARAM_INVALID", `--${flag} must be a positive integer.`);
|
|
3576
3720
|
}
|
|
3577
3721
|
return String(n);
|
|
3578
3722
|
}
|
|
3579
|
-
function
|
|
3723
|
+
function formatOrders3(data) {
|
|
3580
3724
|
const orders = data.orders ?? [];
|
|
3581
3725
|
if (orders.length === 0) {
|
|
3582
3726
|
return Formatter.status("info", "No hotel orders found");
|
|
@@ -3600,7 +3744,7 @@ function formatOrders2(data) {
|
|
|
3600
3744
|
${Formatter.keyValue(summary)}`;
|
|
3601
3745
|
}
|
|
3602
3746
|
function registerHotelListOrdersCommand(parent, deps) {
|
|
3603
|
-
const cmd = parent.command("list-orders").description("List the developer's hotel orders with optional status filtering and pagination").option("--api-key <key>", "API Key for authentication (X-Api-Key)").option("--status <status>", "Filter by order status (omitted entirely when absent \u2192 all statuses)").option("--page <page>", "Page number",
|
|
3747
|
+
const cmd = parent.command("list-orders").description("List the developer's hotel orders with optional status filtering and pagination").option("--api-key <key>", "API Key for authentication (X-Api-Key)").option("--status <status>", "Filter by order status (omitted entirely when absent \u2192 all statuses)").option("--page <page>", "Page number", DEFAULT_PAGE3).option("--page-size <size>", "Page size", DEFAULT_PAGE_SIZE3);
|
|
3604
3748
|
attachSchemaHelp(cmd, hotelListOrdersSchema);
|
|
3605
3749
|
cmd.action(async () => {
|
|
3606
3750
|
const opts = cmd.optsWithGlobals();
|
|
@@ -3610,8 +3754,8 @@ function registerHotelListOrdersCommand(parent, deps) {
|
|
|
3610
3754
|
type: "password"
|
|
3611
3755
|
});
|
|
3612
3756
|
const params = {
|
|
3613
|
-
page:
|
|
3614
|
-
page_size:
|
|
3757
|
+
page: positiveInt4(opts.page ?? DEFAULT_PAGE3, "page"),
|
|
3758
|
+
page_size: positiveInt4(opts.pageSize ?? DEFAULT_PAGE_SIZE3, "page-size")
|
|
3615
3759
|
};
|
|
3616
3760
|
if (opts.status !== void 0) params.status = opts.status;
|
|
3617
3761
|
const spinner = format === "json" ? null : createSpinner("Fetching orders...");
|
|
@@ -3628,7 +3772,7 @@ function registerHotelListOrdersCommand(parent, deps) {
|
|
|
3628
3772
|
const configManager = new ConfigManager();
|
|
3629
3773
|
const commandResult = {
|
|
3630
3774
|
data,
|
|
3631
|
-
text: () =>
|
|
3775
|
+
text: () => formatOrders3(data)
|
|
3632
3776
|
};
|
|
3633
3777
|
await renderWithContext(commandResult, { format }, configManager);
|
|
3634
3778
|
});
|
|
@@ -4182,7 +4326,31 @@ async function main() {
|
|
|
4182
4326
|
const flag = thisCommand.opts().format;
|
|
4183
4327
|
process.env.AGENZO_FORMAT = resolveFormat(flag);
|
|
4184
4328
|
});
|
|
4185
|
-
const rideCmd = program.command("ride-elife").description(
|
|
4329
|
+
const rideCmd = program.command("ride-elife").description(
|
|
4330
|
+
`Ride ordering (eLife) \u2014 quote a fare, book it, poll status, and cancel.
|
|
4331
|
+
|
|
4332
|
+
Workflow (typical order):
|
|
4333
|
+
1. quote Get fare quotes between two points \u2192 vehicle_classes[] each with a quote_id
|
|
4334
|
+
2. book Book one vehicle_class using its quote_id \u2192 ride_id (write; needs --idempotency-key)
|
|
4335
|
+
3. get Poll ride status by --order-id=<ride_id> until terminal (or use --watch for NDJSON)
|
|
4336
|
+
4. cancel (optional) Cancel a ride by --order-id (write; may incur a fee)
|
|
4337
|
+
list-orders (standalone) List the developer's previous ride orders
|
|
4338
|
+
|
|
4339
|
+
Key notes:
|
|
4340
|
+
\u2022 Coordinates are NOT geocoded by the backend \u2014 supply --pickup-lat/lng and --dropoff-lat/lng.
|
|
4341
|
+
book must repeat the SAME pickup/dropoff coordinates+names and pickup-time used in quote;
|
|
4342
|
+
the quote_id does NOT carry them forward.
|
|
4343
|
+
\u2022 quote_id from quote is opaque and provider-specific \u2014 pass it unchanged to book, never reuse
|
|
4344
|
+
it across services.
|
|
4345
|
+
\u2022 vehicle_class values are case-sensitive literals (Sedan / SUV / MPV-5 / MPV-7 / Van / Luxury /
|
|
4346
|
+
Train) \u2014 pass back to book verbatim.
|
|
4347
|
+
\u2022 --pickup-time is UTC epoch seconds or the literal "now"; convert the user's local time to UTC.
|
|
4348
|
+
\u2022 All amounts are DECIMAL currency units (e.g. 42.50 = $42.50), never minor units (cents).
|
|
4349
|
+
\u2022 Billing mode is decided server-side: pay_per_call needs a PAID --payment-order-id (from
|
|
4350
|
+
payment-cli); monthly_settlement forbids it (settled against the developer's account).
|
|
4351
|
+
\u2022 Write verbs (book / cancel) require --idempotency-key; reuse the SAME key when retrying the
|
|
4352
|
+
same intent.`
|
|
4353
|
+
);
|
|
4186
4354
|
registerQuoteCommand(rideCmd, deps);
|
|
4187
4355
|
registerBookCommand(rideCmd, deps);
|
|
4188
4356
|
registerRideGetCommand(rideCmd, deps);
|
|
@@ -4191,6 +4359,11 @@ async function main() {
|
|
|
4191
4359
|
const servicesCmd = program.command("services").description("Merchant service discovery");
|
|
4192
4360
|
registerServicesListCommand(servicesCmd, { discoveryClient, program });
|
|
4193
4361
|
registerServiceGetCommand(servicesCmd, { discoveryClient, program });
|
|
4362
|
+
const ordersCmd = program.command("orders").description(
|
|
4363
|
+
'Unified cross-provider order index (spans ride + hotel). Use for generic "my orders" requests; use ride-elife/hotel-redaug commands once the business is known.'
|
|
4364
|
+
);
|
|
4365
|
+
registerOrdersListCommand(ordersCmd, deps);
|
|
4366
|
+
registerOrdersGetCommand(ordersCmd, deps);
|
|
4194
4367
|
const hotelCmd = program.command("hotel-redaug").description(
|
|
4195
4368
|
`Hotel booking (Redaug) \u2014 international hotel search, quote, create-order, pay-order, cancel, and check-out.
|
|
4196
4369
|
|