tixbit 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -5
- package/dist/cli.js +344 -90
- package/dist/index.d.ts +68 -1
- package/dist/index.js +251 -16
- package/package.json +3 -2
- package/skills/tixbit/SKILL.md +114 -75
- package/skills/tixbit-selling/SKILL.md +171 -0
- package/skills/tixbit-selling/agents/openai.yaml +4 -0
- package/skills/tixbit-selling/references/troubleshooting.md +23 -0
- package/skills/tixbit-selling/references/workflows.md +72 -0
package/dist/cli.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
6
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
5
7
|
|
|
6
8
|
// src/safety.ts
|
|
7
9
|
function usdCents(raw) {
|
|
@@ -41,6 +43,235 @@ function redact(value, secrets = []) {
|
|
|
41
43
|
return value;
|
|
42
44
|
}
|
|
43
45
|
|
|
46
|
+
// src/seller.ts
|
|
47
|
+
import { createHash, randomBytes, randomUUID } from "crypto";
|
|
48
|
+
import { mkdir, readFile, writeFile, rename, unlink, lstat, chmod } from "fs/promises";
|
|
49
|
+
import { homedir } from "os";
|
|
50
|
+
import { join } from "path";
|
|
51
|
+
var SELLER_ORIGIN = "https://mcp.tixbit.com";
|
|
52
|
+
var SELLER_CALLBACK = `${SELLER_ORIGIN}/cli/callback`;
|
|
53
|
+
var DEFAULT_SELLER_CLIENT_ID = "tixbit-client.eyJ0eXAiOiJjbGllbnQiLCJpYXQiOjE3OTIxOTI4MDAsImNsaWVudE5hbWUiOiJUaXhCaXQgQ0xJIiwicmVkaXJlY3RVcmlzIjpbImh0dHBzOi8vbWNwLnRpeGJpdC5jb20vY2xpL2NhbGxiYWNrIl0sInNjb3BlIjoibWNwOnJlYWQgaW52ZW50b3J5OnJlYWQgc2VsbGVyOndyaXRlIn0.C9ijXrH07kd2p15Dd1z5yzfVsz4XoBUJI90GP9Rbg7k";
|
|
54
|
+
var SELLER_SCOPES = "mcp:read inventory:read seller:write";
|
|
55
|
+
var uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
56
|
+
var disclosures = ["ada", "twenty_one_plus", "obstructed_view", "limited_view", "standing_room", "aisle_seat"];
|
|
57
|
+
var SellerError = class extends Error {
|
|
58
|
+
constructor(code, message, action, retryable = false) {
|
|
59
|
+
super(message);
|
|
60
|
+
this.code = code;
|
|
61
|
+
this.action = action;
|
|
62
|
+
this.retryable = retryable;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
function sellerId(value) {
|
|
66
|
+
if (!uuid.test(value)) throw new TypeError("Review and attempt IDs must be UUID v4 values");
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
function text(value, label, max) {
|
|
70
|
+
if (typeof value !== "string" || !value.trim() || value.length > max || /[\x00-\x1f\x7f]/.test(value)) throw new TypeError(`${label} must be nonempty text (max ${max} characters)`);
|
|
71
|
+
return value.trim();
|
|
72
|
+
}
|
|
73
|
+
function normalizeSellerOperation(operation, value) {
|
|
74
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new TypeError("Expected a JSON object");
|
|
75
|
+
const b = value;
|
|
76
|
+
const allowed = operation === "seller.create" ? ["externalEventId", "section", "quantity", "priceCents", "termsAccepted", "row", "seatNumbers", "generalAdmission", "faceValueCents", "disclosures", "notes", "ownedImportId"] : operation === "seller.reprice" ? ["listingId", "priceCents"] : ["listingId"];
|
|
77
|
+
for (const key of Object.keys(b)) if (!allowed.includes(key)) throw new TypeError(`Unsupported seller field: ${key}. Quantity edits and fulfillment/payment changes are unavailable.`);
|
|
78
|
+
const result = {};
|
|
79
|
+
if (operation !== "seller.create") result.listingId = text(b.listingId, "listingId", 128);
|
|
80
|
+
if (operation !== "seller.delist") {
|
|
81
|
+
if (!Number.isSafeInteger(b.priceCents) || b.priceCents < 100 || b.priceCents > 5e6) throw new TypeError("priceCents must be an integer from 100 to 5000000 (USD per ticket)");
|
|
82
|
+
result.priceCents = b.priceCents;
|
|
83
|
+
}
|
|
84
|
+
if (operation === "seller.create") {
|
|
85
|
+
result.externalEventId = text(b.externalEventId, "externalEventId", 128);
|
|
86
|
+
result.section = text(b.section, "section", 128);
|
|
87
|
+
if (!Number.isInteger(b.quantity) || b.quantity < 1 || b.quantity > 20) throw new TypeError("quantity must be an integer from 1 to 20");
|
|
88
|
+
if (b.termsAccepted !== true) throw new TypeError("Seller approval requires termsAccepted: true");
|
|
89
|
+
result.quantity = b.quantity;
|
|
90
|
+
result.termsAccepted = true;
|
|
91
|
+
for (const key of ["row", "notes", "ownedImportId"]) if (b[key] !== void 0) result[key] = text(b[key], key, key === "notes" ? 280 : key === "row" ? 64 : 100);
|
|
92
|
+
if (b.generalAdmission !== void 0) {
|
|
93
|
+
if (typeof b.generalAdmission !== "boolean") throw new TypeError("generalAdmission must be a boolean");
|
|
94
|
+
result.generalAdmission = b.generalAdmission;
|
|
95
|
+
}
|
|
96
|
+
if (b.seatNumbers !== void 0) {
|
|
97
|
+
if (!Array.isArray(b.seatNumbers) || b.seatNumbers.length > 0 && b.seatNumbers.length !== b.quantity || new Set(b.seatNumbers).size !== b.seatNumbers.length) throw new TypeError("seatNumbers must contain one distinct seat per ticket");
|
|
98
|
+
result.seatNumbers = b.seatNumbers.map((s) => text(s, "seatNumber", 16));
|
|
99
|
+
}
|
|
100
|
+
if (b.faceValueCents !== void 0) {
|
|
101
|
+
if (!Number.isSafeInteger(b.faceValueCents) || b.faceValueCents < 0 || b.faceValueCents > 5e6) throw new TypeError("faceValueCents must be a nonnegative integer");
|
|
102
|
+
result.faceValueCents = b.faceValueCents;
|
|
103
|
+
}
|
|
104
|
+
if (b.ownedImportId !== void 0 && !/^ori_[0-9a-f-]{36}$/i.test(String(b.ownedImportId))) throw new TypeError("ownedImportId must be a TixBit-owned resale import ID");
|
|
105
|
+
if (b.generalAdmission === true && (b.row || Array.isArray(b.seatNumbers) && b.seatNumbers.length)) throw new TypeError("General admission cannot include a row or seat numbers");
|
|
106
|
+
if (b.generalAdmission !== true && (!b.row || !b.ownedImportId && (!Array.isArray(b.seatNumbers) || !b.seatNumbers.length))) throw new TypeError("Reserved seating requires row and exact seatNumbers matching quantity; use generalAdmission: true only for GA tickets");
|
|
107
|
+
if (Array.isArray(b.seatNumbers) && b.seatNumbers.some((s) => /^(tbd|unknown|n\/?a|pending)$/i.test(s.trim()))) throw new TypeError("Exact seat numbers are required; placeholder seats are not accepted");
|
|
108
|
+
if (b.disclosures !== void 0) {
|
|
109
|
+
if (!Array.isArray(b.disclosures) || b.disclosures.some((d) => !disclosures.includes(d))) throw new TypeError(`disclosures must use: ${disclosures.join(", ")}`);
|
|
110
|
+
result.disclosures = [...new Set(b.disclosures)];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
var SellerStore = class {
|
|
116
|
+
constructor(directory = join(homedir(), ".config", "tixbit", "seller")) {
|
|
117
|
+
this.directory = directory;
|
|
118
|
+
}
|
|
119
|
+
async save(name, value) {
|
|
120
|
+
await mkdir(this.directory, { recursive: true, mode: 448 });
|
|
121
|
+
const info = await lstat(this.directory);
|
|
122
|
+
if (info.isSymbolicLink() || !info.isDirectory()) throw new TypeError("Seller state directory must be a real directory");
|
|
123
|
+
await chmod(this.directory, 448);
|
|
124
|
+
const temporary = join(this.directory, `.${randomUUID()}.tmp`);
|
|
125
|
+
await writeFile(temporary, JSON.stringify(value), { mode: 384, flag: "wx" });
|
|
126
|
+
await rename(temporary, join(this.directory, name));
|
|
127
|
+
}
|
|
128
|
+
async load(name) {
|
|
129
|
+
try {
|
|
130
|
+
const path = join(this.directory, name);
|
|
131
|
+
const info = await lstat(path);
|
|
132
|
+
if (!info.isFile() || info.isSymbolicLink() || (info.mode & 63) !== 0) throw new Error();
|
|
133
|
+
return JSON.parse(await readFile(path, "utf8"));
|
|
134
|
+
} catch {
|
|
135
|
+
throw new SellerError("LOCAL_STATE_MISSING", "Private seller session or review is missing or unreadable.", "Run sell login, or prepare a new review after checking inventory.");
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async remove(name) {
|
|
139
|
+
await unlink(join(this.directory, name)).catch(() => {
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
var SellerClient = class {
|
|
144
|
+
constructor(store = new SellerStore(), fetcher = fetch) {
|
|
145
|
+
this.store = store;
|
|
146
|
+
this.fetcher = fetcher;
|
|
147
|
+
}
|
|
148
|
+
async request(path, body, token) {
|
|
149
|
+
let response;
|
|
150
|
+
try {
|
|
151
|
+
response = await this.fetcher(`${SELLER_ORIGIN}${path}`, { method: body === void 0 ? "GET" : "POST", headers: { ...body === void 0 ? {} : { "Content-Type": "application/json" }, ...token ? { Authorization: `Bearer ${token}` } : {} }, ...body === void 0 ? {} : { body: JSON.stringify(body) }, redirect: "error", signal: AbortSignal.timeout(3e4) });
|
|
152
|
+
} catch {
|
|
153
|
+
throw new SellerError("SELLER_OUTCOME_UNKNOWN", "The seller service response was not received.", "For a confirm, retry the SAME review ID. For prepare, check inventory before preparing another attempt.", true);
|
|
154
|
+
}
|
|
155
|
+
const data = await response.json().catch(() => null);
|
|
156
|
+
if (!response.ok) {
|
|
157
|
+
const errors = {
|
|
158
|
+
authentication_required: { message: "Seller sign-in is required.", action: "Run tixbit sell login to authorize this CLI." },
|
|
159
|
+
seller_unavailable: { message: "Seller account support is not configured.", action: "Ask the operator to configure the seller account bridge and approved OAuth client." },
|
|
160
|
+
permission_required: { message: "Current seller account permission is required.", action: "Check seller ownership, sign in to the correct account, and reconnect to grant inventory:read and seller:write." },
|
|
161
|
+
invalid_request: { message: "The seller request is invalid.", action: "Check required fields, exact event and seat IDs, quantity, price in cents and UUID attempt. Correct the request before preparing again." },
|
|
162
|
+
invalid_content_type: { message: "The seller request requires JSON.", action: "Use the documented CLI JSON input or send application/json to the API." },
|
|
163
|
+
account_unavailable: { message: "The seller account service is temporarily unavailable.", action: path === "/api/seller/confirm" ? "Read review status and inventory before retrying. Retain the SAME review ID and confirmation; do not prepare a new mutation attempt." : "Retry the read when service recovers. For preparation, reconcile inventory and the existing attempt before preparing again." },
|
|
164
|
+
review_or_listing_unavailable: { message: "The review or owned listing is unavailable.", action: "Run sell list to refresh owned inventory. Use an owned listing or review ID and check that the review has not expired." },
|
|
165
|
+
review_conflict: { message: "The seller review changed, expired or requires reconciliation.", action: "Read sell review and sell list. Retain the existing confirmation; never repeat an uncertain mutation with a new attempt." }
|
|
166
|
+
};
|
|
167
|
+
const code = typeof data?.error?.code === "string" && Object.hasOwn(errors, data.error.code) ? data.error.code : null;
|
|
168
|
+
if (code) {
|
|
169
|
+
const known = errors[code];
|
|
170
|
+
throw new SellerError(code, known.message, known.action, data.error.retryable === true);
|
|
171
|
+
}
|
|
172
|
+
throw new SellerError(`SELLER_HTTP_${response.status}`, `Seller request failed (HTTP ${response.status}).`, "Check command input and service status. Reconcile review status and inventory before retrying any mutation.", false);
|
|
173
|
+
}
|
|
174
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) throw new SellerError("INVALID_SELLER_RESPONSE", "Seller service returned an invalid response.", "Check review status and inventory before another attempt.");
|
|
175
|
+
return redact(data, [token ?? ""]);
|
|
176
|
+
}
|
|
177
|
+
async token() {
|
|
178
|
+
const session = await this.store.load("session.json");
|
|
179
|
+
if (!session.accessToken || !Number.isFinite(session.expiresAt) || session.expiresAt <= Date.now()) throw new SellerError("SELLER_LOGIN_EXPIRED", "Seller sign-in has expired.", "Run sell login again. Saved reviews can be checked after sign-in.");
|
|
180
|
+
return session.accessToken;
|
|
181
|
+
}
|
|
182
|
+
capabilities() {
|
|
183
|
+
return this.request("/api/seller/capabilities");
|
|
184
|
+
}
|
|
185
|
+
async inventory() {
|
|
186
|
+
return this.request("/api/seller/inventory", void 0, await this.token());
|
|
187
|
+
}
|
|
188
|
+
async status(id) {
|
|
189
|
+
const inventory = await this.inventory();
|
|
190
|
+
if (!id) return inventory;
|
|
191
|
+
const listing = inventory.listings?.find((l) => l.listingId === id);
|
|
192
|
+
if (!listing) throw new SellerError("LISTING_NOT_FOUND", "Listing not found in this seller account.", "Run sell list and choose an owned listing ID.");
|
|
193
|
+
return { listing };
|
|
194
|
+
}
|
|
195
|
+
async prepare(operation, body, attempt = randomUUID()) {
|
|
196
|
+
sellerId(attempt);
|
|
197
|
+
const normalized = normalizeSellerOperation(operation, body);
|
|
198
|
+
const token = await this.token();
|
|
199
|
+
try {
|
|
200
|
+
await this.store.load(`attempt-${attempt}.json`);
|
|
201
|
+
throw new SellerError("ATTEMPT_ALREADY_SENT", "This preparation attempt has already been sent.", "Check sell list and sell review before starting a new attempt.");
|
|
202
|
+
} catch (error) {
|
|
203
|
+
if (!(error instanceof SellerError) || error.code !== "LOCAL_STATE_MISSING") throw error;
|
|
204
|
+
}
|
|
205
|
+
await this.store.save(`attempt-${attempt}.json`, { operation, body: normalized, attempt });
|
|
206
|
+
let result;
|
|
207
|
+
try {
|
|
208
|
+
result = await this.request("/api/seller/prepare", { operation, body: normalized, attempt }, token);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
if (error instanceof SellerError) error.action += ` Preparation attempt: ${attempt}.`;
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
if (!result.review || !uuid.test(result.review.id) || result.confirmation?.reviewId !== result.review.id || !/^[A-Za-z0-9_-]{43}$/.test(result.confirmation.nonce)) throw new SellerError("INVALID_SELLER_REVIEW", "Invalid server review.", "Check inventory before another attempt.");
|
|
214
|
+
await this.store.save(`review-${result.review.id}.json`, result);
|
|
215
|
+
return { review: result.review, attempt, action: `Review the operation and seller terms. To approve: tixbit sell confirm ${result.review.id} --confirm. To abandon: tixbit sell cancel ${result.review.id}. Submission does not guarantee a live marketplace listing.` };
|
|
216
|
+
}
|
|
217
|
+
async review(id) {
|
|
218
|
+
return this.request(`/api/seller/reviews/${sellerId(id)}`, void 0, await this.token());
|
|
219
|
+
}
|
|
220
|
+
async confirm(id, approved) {
|
|
221
|
+
if (approved !== true) throw new TypeError("Explicit approval requires --confirm after reviewing the prepared operation");
|
|
222
|
+
const saved = await this.store.load(`review-${sellerId(id)}.json`);
|
|
223
|
+
if (saved.confirmation?.reviewId !== id) throw new TypeError("Review identity mismatch");
|
|
224
|
+
return this.request("/api/seller/confirm", saved.confirmation, await this.token());
|
|
225
|
+
}
|
|
226
|
+
async cancel(id) {
|
|
227
|
+
const saved = await this.store.load(`review-${sellerId(id)}.json`);
|
|
228
|
+
return this.request("/api/seller/cancel", saved.confirmation, await this.token());
|
|
229
|
+
}
|
|
230
|
+
async logout() {
|
|
231
|
+
await this.store.remove("session.json");
|
|
232
|
+
await this.store.remove("login.json");
|
|
233
|
+
return { signedIn: false, action: "Local credentials removed. To revoke an existing grant, use your TixBit account connections." };
|
|
234
|
+
}
|
|
235
|
+
async authStatus() {
|
|
236
|
+
try {
|
|
237
|
+
const session = await this.store.load("session.json");
|
|
238
|
+
return { signedIn: session.expiresAt > Date.now(), expiresAt: session.expiresAt, action: "Run sell list to validate current server access. Local status does not prove the grant is still active." };
|
|
239
|
+
} catch {
|
|
240
|
+
return { signedIn: false, action: "Run tixbit sell login to sign in with the built-in public CLI client." };
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
async startLogin(clientId = DEFAULT_SELLER_CLIENT_ID) {
|
|
244
|
+
text(clientId, "clientId", 8192);
|
|
245
|
+
const pending = { clientId, state: randomBytes(32).toString("base64url"), verifier: randomBytes(32).toString("base64url"), expiresAt: Date.now() + 6e5 };
|
|
246
|
+
await this.store.save("login.json", pending);
|
|
247
|
+
const url = new URL(`${SELLER_ORIGIN}/api/oauth/authorize`);
|
|
248
|
+
for (const [key, value] of Object.entries({ response_type: "code", client_id: clientId, redirect_uri: SELLER_CALLBACK, scope: SELLER_SCOPES, resource: `${SELLER_ORIGIN}/api/mcp`, state: pending.state, code_challenge: createHash("sha256").update(pending.verifier).digest("base64url"), code_challenge_method: "S256" })) url.searchParams.set(key, value);
|
|
249
|
+
return { signInUrl: url.toString(), action: "Open this URL in your browser, sign in and approve seller access. Then run tixbit sell login --complete and paste the full callback URL on stdin. Never paste tokens or browser cookies.", expiresAt: pending.expiresAt };
|
|
250
|
+
}
|
|
251
|
+
async completeLogin(callback) {
|
|
252
|
+
const pending = await this.store.load("login.json");
|
|
253
|
+
let url;
|
|
254
|
+
try {
|
|
255
|
+
url = new URL(callback.trim());
|
|
256
|
+
} catch {
|
|
257
|
+
throw new TypeError("Paste the full TixBit callback URL from your browser");
|
|
258
|
+
}
|
|
259
|
+
if (url.origin + url.pathname !== SELLER_CALLBACK || url.hash || url.username || url.password || pending.expiresAt <= Date.now() || url.searchParams.get("state") !== pending.state || url.searchParams.getAll("state").length !== 1 || url.searchParams.getAll("code").length !== 1 || !url.searchParams.get("code") || url.searchParams.has("error")) throw new TypeError("Callback URL, state, or login expiry does not match. Run sell login again.");
|
|
260
|
+
await this.store.remove("login.json");
|
|
261
|
+
let response;
|
|
262
|
+
try {
|
|
263
|
+
response = await this.fetcher(`${SELLER_ORIGIN}/api/oauth/token`, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ grant_type: "authorization_code", client_id: pending.clientId, redirect_uri: SELLER_CALLBACK, resource: `${SELLER_ORIGIN}/api/mcp`, code: url.searchParams.get("code"), code_verifier: pending.verifier }), redirect: "error", signal: AbortSignal.timeout(3e4) });
|
|
264
|
+
} catch {
|
|
265
|
+
throw new SellerError("LOGIN_EXCHANGE_FAILED", "Could not complete login.", "Run sell login again; do not reuse an uncertain authorization code.");
|
|
266
|
+
}
|
|
267
|
+
const result = await response.json().catch(() => null);
|
|
268
|
+
await this.store.remove("login.json");
|
|
269
|
+
if (!response.ok || typeof result?.access_token !== "string" || !result.access_token || result.token_type?.toLowerCase() !== "bearer" || !Number.isFinite(result.expires_in) || result.expires_in <= 0) throw new SellerError("LOGIN_EXCHANGE_FAILED", "Seller authorization was not granted.", "Run sell login again. Ask the operator to approve the CLI client and seller scopes if unavailable.");
|
|
270
|
+
await this.store.save("session.json", { accessToken: result.access_token, clientId: pending.clientId, expiresAt: Date.now() + result.expires_in * 1e3 });
|
|
271
|
+
return { signedIn: true, expiresIn: result.expires_in, action: "Run tixbit sell list to inspect your inventory." };
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
|
|
44
275
|
// src/cli.ts
|
|
45
276
|
import { createRequire as createRequire2 } from "module";
|
|
46
277
|
|
|
@@ -425,7 +656,8 @@ var TixBitClient = class {
|
|
|
425
656
|
}),
|
|
426
657
|
signal: controller.signal
|
|
427
658
|
});
|
|
428
|
-
const
|
|
659
|
+
const value = await response.json().catch(() => void 0);
|
|
660
|
+
const body = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
429
661
|
const orderReference = normalizeOrderReference(body.orderReference);
|
|
430
662
|
const receiptUrl = normalizeReceiptUrl(body.receiptUrl);
|
|
431
663
|
if (response.status >= 500 && body.status !== "manual_review_required") {
|
|
@@ -458,21 +690,21 @@ var TixBitClient = class {
|
|
|
458
690
|
action: "Configure the official mppx payment client, then retry with the same idempotency key."
|
|
459
691
|
};
|
|
460
692
|
}
|
|
693
|
+
if (response.ok && (!isPurchaseStatus(body.status) || body.status === "fulfilled" && (body.success !== true || !orderReference))) {
|
|
694
|
+
return {
|
|
695
|
+
success: false,
|
|
696
|
+
status: "pending",
|
|
697
|
+
idempotencyKey,
|
|
698
|
+
orderReference,
|
|
699
|
+
receiptUrl,
|
|
700
|
+
error: {
|
|
701
|
+
code: "PURCHASE_OUTCOME_AMBIGUOUS",
|
|
702
|
+
message: "The server did not return a confirmed purchase outcome."
|
|
703
|
+
},
|
|
704
|
+
action: "Check the order with the same idempotency key. Do not create a new payment or purchase attempt."
|
|
705
|
+
};
|
|
706
|
+
}
|
|
461
707
|
if (isPurchaseStatus(body.status)) {
|
|
462
|
-
if (body.status === "fulfilled" && response.ok && body.success === true && !orderReference) {
|
|
463
|
-
return {
|
|
464
|
-
success: false,
|
|
465
|
-
status: "pending",
|
|
466
|
-
idempotencyKey,
|
|
467
|
-
orderReference: null,
|
|
468
|
-
receiptUrl,
|
|
469
|
-
error: {
|
|
470
|
-
code: "PURCHASE_OUTCOME_AMBIGUOUS",
|
|
471
|
-
message: "The purchase response did not include an order reference."
|
|
472
|
-
},
|
|
473
|
-
action: "Retry with the same idempotency key. Do not create a new payment or purchase attempt."
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
708
|
if (body.status === "fulfilled" && (!response.ok || body.success !== true)) {
|
|
477
709
|
return rejectedPurchaseResult(
|
|
478
710
|
idempotencyKey,
|
|
@@ -868,10 +1100,10 @@ var TixBitTimeoutError = class extends Error {
|
|
|
868
1100
|
|
|
869
1101
|
// src/link.ts
|
|
870
1102
|
import { execFile } from "child_process";
|
|
871
|
-
import { randomUUID } from "crypto";
|
|
872
|
-
import { mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
873
|
-
import { homedir } from "os";
|
|
874
|
-
import { dirname, join } from "path";
|
|
1103
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
1104
|
+
import { mkdir as mkdir2, readFile as readFile2, rename as rename2, writeFile as writeFile2 } from "fs/promises";
|
|
1105
|
+
import { homedir as homedir2 } from "os";
|
|
1106
|
+
import { dirname, join as join2 } from "path";
|
|
875
1107
|
import { createRequire } from "module";
|
|
876
1108
|
import { promisify } from "util";
|
|
877
1109
|
import { Challenge } from "mppx";
|
|
@@ -887,7 +1119,7 @@ function linkCliEntry() {
|
|
|
887
1119
|
throw new LinkCheckoutError("LINK_NODE_UNSUPPORTED", "TixBit needs Node.js 22 or later.");
|
|
888
1120
|
}
|
|
889
1121
|
try {
|
|
890
|
-
return
|
|
1122
|
+
return join2(dirname(require2.resolve("@stripe/link-cli/package.json")), "dist", "cli.js");
|
|
891
1123
|
} catch {
|
|
892
1124
|
throw new LinkCheckoutError("LINK_CLI_MISSING", "The bundled Link CLI is missing. Reinstall TixBit, then retry.");
|
|
893
1125
|
}
|
|
@@ -905,20 +1137,20 @@ function paymentEndpoint() {
|
|
|
905
1137
|
}
|
|
906
1138
|
function statePath(orderReference) {
|
|
907
1139
|
if (!ORDER_REFERENCE.test(orderReference)) throw new LinkCheckoutError("INVALID_ORDER_REFERENCE", "Enter the order reference shown by link start.");
|
|
908
|
-
const base = process.env.XDG_STATE_HOME ||
|
|
909
|
-
return
|
|
1140
|
+
const base = process.env.XDG_STATE_HOME || join2(homedir2(), ".local", "state");
|
|
1141
|
+
return join2(base, "tixbit", "link", `${orderReference}.json`);
|
|
910
1142
|
}
|
|
911
1143
|
async function saveState(state) {
|
|
912
1144
|
const path = statePath(state.orderReference);
|
|
913
|
-
await
|
|
914
|
-
const temporary = `${path}.${
|
|
915
|
-
await
|
|
916
|
-
await
|
|
1145
|
+
await mkdir2(dirname(path), { recursive: true, mode: 448 });
|
|
1146
|
+
const temporary = `${path}.${randomUUID2()}.tmp`;
|
|
1147
|
+
await writeFile2(temporary, JSON.stringify(state), { mode: 384, flag: "wx" });
|
|
1148
|
+
await rename2(temporary, path);
|
|
917
1149
|
}
|
|
918
1150
|
async function loadState(orderReference) {
|
|
919
1151
|
let value;
|
|
920
1152
|
try {
|
|
921
|
-
value = JSON.parse(await
|
|
1153
|
+
value = JSON.parse(await readFile2(statePath(orderReference), "utf8"));
|
|
922
1154
|
} catch {
|
|
923
1155
|
throw new LinkCheckoutError("CHECKOUT_NOT_FOUND", "This Link checkout was not found on this device.");
|
|
924
1156
|
}
|
|
@@ -1002,7 +1234,7 @@ function approvalUrl(value) {
|
|
|
1002
1234
|
}
|
|
1003
1235
|
async function startLinkPurchase(input) {
|
|
1004
1236
|
linkCliEntry();
|
|
1005
|
-
const purchase = normalizePurchaseTicketsParams({ ...input, idempotencyKey:
|
|
1237
|
+
const purchase = normalizePurchaseTicketsParams({ ...input, idempotencyKey: randomUUID2() });
|
|
1006
1238
|
if (!Number.isSafeInteger(input.maxAmountCents) || input.maxAmountCents <= 0) {
|
|
1007
1239
|
throw new LinkCheckoutError("INVALID_MAX_PRICE", "Set a positive total price limit.");
|
|
1008
1240
|
}
|
|
@@ -1061,35 +1293,39 @@ async function startLinkPurchase(input) {
|
|
|
1061
1293
|
action: `Approve in Link, then run: tixbit link complete ${body.orderReference}`
|
|
1062
1294
|
};
|
|
1063
1295
|
}
|
|
1296
|
+
function purchaseOutcome(state, result) {
|
|
1297
|
+
if (result.orderReference && result.orderReference !== state.orderReference) {
|
|
1298
|
+
throw new LinkCheckoutError("ORDER_REFERENCE_CHANGED", "TixBit returned a different order reference. Check the order before another payment.");
|
|
1299
|
+
}
|
|
1300
|
+
const sameOrder = result.orderReference === state.orderReference;
|
|
1301
|
+
const terminalFailure = sameOrder && (result.status === "payment_failed" || result.status === "rejected");
|
|
1302
|
+
const manualReview = sameOrder && result.status === "manual_review_required";
|
|
1303
|
+
const fulfilled = sameOrder && result.success && result.status === "fulfilled";
|
|
1304
|
+
return {
|
|
1305
|
+
success: fulfilled,
|
|
1306
|
+
status: fulfilled || manualReview || terminalFailure ? result.status : "pending",
|
|
1307
|
+
orderReference: state.orderReference,
|
|
1308
|
+
amountCents: state.amountCents,
|
|
1309
|
+
...sameOrder && result.order ? { order: result.order } : {},
|
|
1310
|
+
...sameOrder && result.receiptUrl ? { receiptUrl: result.receiptUrl } : {},
|
|
1311
|
+
...!fulfilled ? {
|
|
1312
|
+
error: sameOrder && (manualReview || terminalFailure) && result.error ? result.error : { code: "PURCHASE_OUTCOME_AMBIGUOUS", message: "The order is still being checked." },
|
|
1313
|
+
action: terminalFailure || manualReview ? result.action ?? "Contact TixBit support with this order reference. Do not submit another payment." : "Run the same complete command again later. Do not start another payment or purchase."
|
|
1314
|
+
} : {}
|
|
1315
|
+
};
|
|
1316
|
+
}
|
|
1064
1317
|
async function completeLinkPurchase(orderReference) {
|
|
1065
1318
|
const state = await loadState(orderReference);
|
|
1066
|
-
|
|
1319
|
+
const ambiguousLegacyResult = state.paymentAttempted && state.result?.status === "rejected" && /^HTTP_2\d\d$/.test(state.result.error?.code ?? "");
|
|
1320
|
+
if (state.result && !ambiguousLegacyResult && ["fulfilled", "payment_failed", "rejected"].includes(state.result.status)) return state.result;
|
|
1067
1321
|
if (state.paymentAttempted) {
|
|
1068
1322
|
const request2 = JSON.parse(state.requestBody);
|
|
1069
1323
|
const result2 = await new TixBitClient({ paymentEndpoint: paymentEndpoint(), timeoutMs: 3e4 }).purchaseTickets(request2);
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
}
|
|
1073
|
-
if (state.result?.status === "manual_review_required" && result2.status !== "fulfilled") {
|
|
1324
|
+
const output3 = purchaseOutcome(state, result2);
|
|
1325
|
+
if (state.result?.status === "manual_review_required" && !output3.success) {
|
|
1074
1326
|
return state.result;
|
|
1075
1327
|
}
|
|
1076
|
-
|
|
1077
|
-
const terminalFailure = sameOrder && (result2.status === "payment_failed" || result2.status === "rejected");
|
|
1078
|
-
const manualReview = sameOrder && result2.status === "manual_review_required";
|
|
1079
|
-
const fulfilled = sameOrder && result2.success && result2.status === "fulfilled";
|
|
1080
|
-
const output3 = {
|
|
1081
|
-
success: fulfilled,
|
|
1082
|
-
status: fulfilled || manualReview || terminalFailure ? result2.status : "pending",
|
|
1083
|
-
orderReference,
|
|
1084
|
-
amountCents: state.amountCents,
|
|
1085
|
-
...sameOrder && result2.order ? { order: result2.order } : {},
|
|
1086
|
-
...sameOrder && result2.receiptUrl ? { receiptUrl: result2.receiptUrl } : {},
|
|
1087
|
-
...!fulfilled ? {
|
|
1088
|
-
error: sameOrder && (manualReview || terminalFailure) && result2.error ? result2.error : { code: "PURCHASE_OUTCOME_AMBIGUOUS", message: "The order is still being checked." },
|
|
1089
|
-
action: terminalFailure || manualReview ? result2.action ?? "Contact TixBit support with this order reference. Do not submit another payment." : "Run the same complete command again later. Do not start another payment or purchase."
|
|
1090
|
-
} : {}
|
|
1091
|
-
};
|
|
1092
|
-
if (output3.success || terminalFailure || output3.status === "manual_review_required") {
|
|
1328
|
+
if (ambiguousLegacyResult || output3.success || ["payment_failed", "rejected", "manual_review_required"].includes(output3.status)) {
|
|
1093
1329
|
state.result = output3;
|
|
1094
1330
|
await saveState(state);
|
|
1095
1331
|
}
|
|
@@ -1160,21 +1396,15 @@ async function completeLinkPurchase(orderReference) {
|
|
|
1160
1396
|
const request = JSON.parse(state.requestBody);
|
|
1161
1397
|
const result = await client2.purchaseTickets(request);
|
|
1162
1398
|
if (challengeChanged) throw new LinkCheckoutError("CHALLENGE_CHANGED", "The order or payment amount changed. No payment was submitted.");
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
success: result.success,
|
|
1168
|
-
status: decline ? "payment_failed" : result.status,
|
|
1399
|
+
const outcome = purchaseOutcome(state, result);
|
|
1400
|
+
const output2 = decline ? {
|
|
1401
|
+
success: false,
|
|
1402
|
+
status: "payment_failed",
|
|
1169
1403
|
orderReference,
|
|
1170
1404
|
amountCents: state.amountCents,
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
error: decline ? { code: "CARD_DECLINED", message: "Stripe declined the card selected in Link." } : result.error,
|
|
1175
|
-
action: decline ? "Select another card in Link and start a new spend approval. No ticket was issued." : result.status === "pending" ? "Payment may have succeeded. Do not retry or start another checkout. Contact TixBit support with this order reference." : result.action
|
|
1176
|
-
} : {}
|
|
1177
|
-
};
|
|
1405
|
+
error: { code: "CARD_DECLINED", message: "Stripe declined the card selected in Link." },
|
|
1406
|
+
action: "Select another card in Link and start a new spend approval. No ticket was issued."
|
|
1407
|
+
} : outcome;
|
|
1178
1408
|
state.result = output2;
|
|
1179
1409
|
await saveState(state);
|
|
1180
1410
|
return output2;
|
|
@@ -1190,6 +1420,7 @@ var client = (() => {
|
|
|
1190
1420
|
})();
|
|
1191
1421
|
var packageJson = createRequire2(import.meta.url)("../package.json");
|
|
1192
1422
|
function output(data, json) {
|
|
1423
|
+
data = JSON.parse(JSON.stringify(data, (key, value) => /nonce|verifier/i.test(key) ? "[REDACTED]" : value));
|
|
1193
1424
|
data = redact(data, [process.env.TIXBIT_LINK_TOKEN ?? "", process.env.TIXBIT_ACCESS_TOKEN ?? "", process.env.MPPX_PRIVATE_KEY ?? ""]);
|
|
1194
1425
|
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
1195
1426
|
data = { success: true, ...data };
|
|
@@ -1266,11 +1497,15 @@ function formatItem(item) {
|
|
|
1266
1497
|
return JSON.stringify(item, null, 2);
|
|
1267
1498
|
}
|
|
1268
1499
|
function handleError(err) {
|
|
1500
|
+
if (err instanceof SellerError) {
|
|
1501
|
+
output({ success: false, error: { code: err.code, message: err.message, retryable: err.retryable }, action: err.action }, true);
|
|
1502
|
+
process.exit(1);
|
|
1503
|
+
}
|
|
1269
1504
|
const msg = err instanceof TypeError || err instanceof RangeError ? err.message : "Request failed. Check input and service availability; do not retry an uncertain write.";
|
|
1270
1505
|
output({ success: false, error: { code: "COMMAND_FAILED", message: msg } }, true);
|
|
1271
1506
|
process.exit(1);
|
|
1272
1507
|
}
|
|
1273
|
-
var program = new Command().name("tixbit").description("Search events,
|
|
1508
|
+
var program = new Command().name("tixbit").description("Search events, buy tickets, and manage your own seller listings").version(packageJson.version);
|
|
1274
1509
|
program.command("search [query]").description("Search for events by keyword, city, state, or category").option("--city <city>", "Filter by city").option("--state <state>", "Filter by state (2-letter code)").option("--category <slug>", "Filter by category slug (e.g. nba-basketball)").option("--league <league>", "Filter by league abbreviation (e.g. NBA)").option("--event-type <type>", "SPORT, CONCERT, THEATER, or ALL").option("--performer-id <id>", "Filter by public performer ID").option("--venue-id <id>", "Filter by public venue ID").option("--parking <mode>", "Parking filter: exclude, only, or include").option("--near-lat <lat>", "Latitude for manual location filtering").option("--near-lng <lng>", "Longitude for manual location filtering").option("--location-mode <mode>", "Location mode: inferred, manual, or none").option("--start-date <date>", "Events on or after this date (ISO)").option("--end-date <date>", "Events on or before this date (ISO)").option("--page <n>", "Page number", "1").option("--size <n>", "Results per page", "10").option("--json", "Output raw JSON (for agents)", true).action(async (query, opts) => {
|
|
1275
1510
|
try {
|
|
1276
1511
|
const params = {
|
|
@@ -1387,38 +1622,57 @@ program.command("buy <listingId>").description("Quote or confirm Stripe Link che
|
|
|
1387
1622
|
handleError(error);
|
|
1388
1623
|
}
|
|
1389
1624
|
});
|
|
1390
|
-
var
|
|
1391
|
-
sell.command("
|
|
1625
|
+
var seller = new SellerClient();
|
|
1626
|
+
var sell = program.command("sell").description("Sign in, review and confirm seller inventory changes");
|
|
1627
|
+
var sellerAction = (fn) => async (...args) => {
|
|
1392
1628
|
try {
|
|
1393
|
-
output(await
|
|
1629
|
+
output(await fn(...args), true);
|
|
1394
1630
|
} catch (error) {
|
|
1395
1631
|
handleError(error);
|
|
1396
1632
|
}
|
|
1397
|
-
}
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
if (
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1633
|
+
};
|
|
1634
|
+
async function readSellerInput(file) {
|
|
1635
|
+
if (file) {
|
|
1636
|
+
const input2 = await readFile3(file, "utf8");
|
|
1637
|
+
if (Buffer.byteLength(input2) > 65536) throw new TypeError("Input exceeds 64 KiB");
|
|
1638
|
+
return input2;
|
|
1639
|
+
}
|
|
1640
|
+
if (process.stdin.isTTY) process.stderr.write("Paste input, then press Ctrl-D to finish.\n");
|
|
1641
|
+
let input = "";
|
|
1642
|
+
for await (const chunk of process.stdin) {
|
|
1643
|
+
input += chunk.toString();
|
|
1644
|
+
if (Buffer.byteLength(input) > 65536) throw new TypeError("Input exceeds 64 KiB");
|
|
1645
|
+
}
|
|
1646
|
+
return input;
|
|
1647
|
+
}
|
|
1648
|
+
sell.command("capabilities").description("Discover public seller support, limits and configuration readiness").action(sellerAction(() => seller.capabilities()));
|
|
1649
|
+
sell.command("login").description("Authorize the CLI through browser OAuth with PKCE; no browser cookies or manual tokens").option("--client-id <id>", "Override the built-in public OAuth client ID (or TIXBIT_SELLER_CLIENT_ID)").option("--complete", "Read the full callback URL from stdin and securely finish authorization").action(sellerAction(async (opts) => {
|
|
1650
|
+
if (opts.complete) return seller.completeLogin(await readSellerInput());
|
|
1651
|
+
const clientId = opts.clientId ?? process.env.TIXBIT_SELLER_CLIENT_ID ?? DEFAULT_SELLER_CLIENT_ID;
|
|
1652
|
+
return seller.startLogin(clientId);
|
|
1653
|
+
}));
|
|
1654
|
+
sell.command("auth-status").description("Check local login expiry without exposing credentials").action(sellerAction(() => seller.authStatus()));
|
|
1655
|
+
sell.command("logout").description("Remove local CLI credentials; account grants can be revoked in TixBit").action(sellerAction(() => seller.logout()));
|
|
1656
|
+
sell.command("list").description("Read inventory owned by the signed-in seller").option("--json", "Output JSON", true).action(sellerAction(() => seller.inventory()));
|
|
1657
|
+
sell.command("status [listingId]").description("Read current owned listing status (or all inventory)").action(sellerAction((id) => seller.status(id)));
|
|
1658
|
+
sell.command("review <reviewId>").description("Reconcile a saved review and any completed result").action(sellerAction((id) => seller.review(id)));
|
|
1659
|
+
sell.command("confirm <reviewId>").description("Submit exactly the saved preview; reuse the same review ID after an uncertain response").requiredOption("--confirm", "Approve this previously reviewed operation and seller terms").action(sellerAction((id, opts) => seller.confirm(id, opts.confirm)));
|
|
1660
|
+
sell.command("cancel <reviewId>").description("Abandon a prepared review without changing a listing").action(sellerAction((id) => seller.cancel(id)));
|
|
1661
|
+
for (const name of ["create", "reprice", "delist"]) {
|
|
1662
|
+
sell.command(name).description(`${name === "create" ? "Prepare a listing" : name === "reprice" ? "Prepare a per-ticket price change" : "Prepare removal of an owned listing"}; JSON file or stdin, review before confirm`).option("--file <path>", "Read approved listing/operation JSON from a file (default stdin)").option("--attempt <uuid>", "Stable UUID v4 preparation attempt ID").option("--dry-run", "Validate locally and print preview; no authenticated requests").option("--json", "Output JSON", true).action(sellerAction(async (opts) => {
|
|
1411
1663
|
let body;
|
|
1412
1664
|
try {
|
|
1413
|
-
body = JSON.parse(
|
|
1414
|
-
} catch {
|
|
1415
|
-
throw new TypeError("Invalid
|
|
1665
|
+
body = JSON.parse(await readSellerInput(opts.file));
|
|
1666
|
+
} catch (error) {
|
|
1667
|
+
if (error instanceof SyntaxError) throw new TypeError("Invalid seller JSON. Use --help for input options.");
|
|
1668
|
+
throw error;
|
|
1416
1669
|
}
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
});
|
|
1670
|
+
const operation = `seller.${name}`;
|
|
1671
|
+
const normalized = normalizeSellerOperation(operation, body);
|
|
1672
|
+
if (opts.dryRun) return { dryRun: true, operation, body: normalized, action: "Validate event, date, section, seats, per-ticket USD price and terms with the seller. Omit --dry-run to obtain a server review; no listing was changed." };
|
|
1673
|
+
return seller.prepare(operation, normalized, opts.attempt ?? randomUUID3());
|
|
1674
|
+
}));
|
|
1675
|
+
}
|
|
1422
1676
|
program.command("checkout <listingId>").description("Get a checkout link to buy tickets for a listing").requiredOption("--quantity <n>", "Number of tickets to buy").option("--json", "Output raw JSON", true).action(async (listingId, opts) => {
|
|
1423
1677
|
try {
|
|
1424
1678
|
const quantity = Number(opts.quantity);
|
|
@@ -1713,7 +1967,7 @@ function describePosition(section, allSections) {
|
|
|
1713
1967
|
function configureCommands(command) {
|
|
1714
1968
|
command.exitOverride();
|
|
1715
1969
|
command.configureOutput({
|
|
1716
|
-
writeOut: (
|
|
1970
|
+
writeOut: (text2) => output({ help: text2 }, true),
|
|
1717
1971
|
writeErr: () => {
|
|
1718
1972
|
}
|
|
1719
1973
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -504,4 +504,71 @@ declare function assertMppChallenge(challenge: {
|
|
|
504
504
|
request: Record<string, unknown>;
|
|
505
505
|
}, maxAmountCents: number): void;
|
|
506
506
|
|
|
507
|
-
|
|
507
|
+
declare const DEFAULT_SELLER_CLIENT_ID = "tixbit-client.eyJ0eXAiOiJjbGllbnQiLCJpYXQiOjE3OTIxOTI4MDAsImNsaWVudE5hbWUiOiJUaXhCaXQgQ0xJIiwicmVkaXJlY3RVcmlzIjpbImh0dHBzOi8vbWNwLnRpeGJpdC5jb20vY2xpL2NhbGxiYWNrIl0sInNjb3BlIjoibWNwOnJlYWQgaW52ZW50b3J5OnJlYWQgc2VsbGVyOndyaXRlIn0.C9ijXrH07kd2p15Dd1z5yzfVsz4XoBUJI90GP9Rbg7k";
|
|
508
|
+
type SellerOperation = "seller.create" | "seller.reprice" | "seller.delist";
|
|
509
|
+
declare class SellerError extends Error {
|
|
510
|
+
code: string;
|
|
511
|
+
action: string;
|
|
512
|
+
retryable: boolean;
|
|
513
|
+
constructor(code: string, message: string, action: string, retryable?: boolean);
|
|
514
|
+
}
|
|
515
|
+
/** Strictly validate the actual seller API contract; no payment or fulfillment fields. */
|
|
516
|
+
declare function normalizeSellerOperation(operation: SellerOperation, value: unknown): Record<string, unknown>;
|
|
517
|
+
/** Credentials and confirmation capabilities stay in private files, never stdout or argv. */
|
|
518
|
+
declare class SellerStore {
|
|
519
|
+
directory: string;
|
|
520
|
+
constructor(directory?: string);
|
|
521
|
+
save(name: string, value: unknown): Promise<void>;
|
|
522
|
+
load<T>(name: string): Promise<T>;
|
|
523
|
+
remove(name: string): Promise<void>;
|
|
524
|
+
}
|
|
525
|
+
declare class SellerClient {
|
|
526
|
+
store: SellerStore;
|
|
527
|
+
private fetcher;
|
|
528
|
+
constructor(store?: SellerStore, fetcher?: typeof fetch);
|
|
529
|
+
private request;
|
|
530
|
+
token(): Promise<string>;
|
|
531
|
+
capabilities(): Promise<any>;
|
|
532
|
+
inventory(): Promise<any>;
|
|
533
|
+
status(id?: string): Promise<any>;
|
|
534
|
+
prepare(operation: SellerOperation, body: unknown, attempt?: string): Promise<{
|
|
535
|
+
review: {
|
|
536
|
+
id: string;
|
|
537
|
+
operation: {
|
|
538
|
+
name: SellerOperation;
|
|
539
|
+
body: Record<string, unknown>;
|
|
540
|
+
};
|
|
541
|
+
expiresAt: number;
|
|
542
|
+
};
|
|
543
|
+
attempt: string;
|
|
544
|
+
action: string;
|
|
545
|
+
}>;
|
|
546
|
+
review(id: string): Promise<any>;
|
|
547
|
+
confirm(id: string, approved: boolean): Promise<any>;
|
|
548
|
+
cancel(id: string): Promise<any>;
|
|
549
|
+
logout(): Promise<{
|
|
550
|
+
signedIn: boolean;
|
|
551
|
+
action: string;
|
|
552
|
+
}>;
|
|
553
|
+
authStatus(): Promise<{
|
|
554
|
+
signedIn: boolean;
|
|
555
|
+
expiresAt: number;
|
|
556
|
+
action: string;
|
|
557
|
+
} | {
|
|
558
|
+
signedIn: boolean;
|
|
559
|
+
action: string;
|
|
560
|
+
expiresAt?: undefined;
|
|
561
|
+
}>;
|
|
562
|
+
startLogin(clientId?: string): Promise<{
|
|
563
|
+
signInUrl: string;
|
|
564
|
+
action: string;
|
|
565
|
+
expiresAt: number;
|
|
566
|
+
}>;
|
|
567
|
+
completeLogin(callback: string): Promise<{
|
|
568
|
+
signedIn: boolean;
|
|
569
|
+
expiresIn: any;
|
|
570
|
+
action: string;
|
|
571
|
+
}>;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
export { type AgentResult, type BrowseEventsParams, type BrowseEventsResult, type BuyTicketsParams, type CheckoutLink, type CheckoutParams, DEFAULT_SELLER_CLIENT_ID, type GetListingResult, type GetListingsParams, type GetListingsResult, type GetSeatmapParams, type LocationMode, type ParkingFilter, type PurchaseOrder, type PurchaseStatus, type PurchaseTicketsParams, type PurchaseTicketsResult, type SearchEventsParams, type SearchEventsResult, type SeatmapLabel, type SeatmapResult, type SeatmapSection, type SeatmapVenue, type SeatmapZone, SellerClient, SellerError, type SellerOperation, SellerStore, TixBitApiError, TixBitClient, type TixBitConfig, type TixBitEvent, type TixBitEventDetail, type TixBitListing, type TixBitListingDisclosure, TixBitTimeoutError, assertMppChallenge, normalizeSellerOperation };
|