byr-pt-cli 0.1.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 +64 -0
- package/dist/cli-CdpxYIyd.mjs +794 -0
- package/dist/cli.d.mts +18 -0
- package/dist/cli.mjs +4 -0
- package/dist/client-DT9oDCaE.mjs +1654 -0
- package/dist/commands/download.d.mts +21 -0
- package/dist/commands/download.mjs +49 -0
- package/dist/commands/get.d.mts +11 -0
- package/dist/commands/get.mjs +28 -0
- package/dist/commands/search.d.mts +20 -0
- package/dist/commands/search.mjs +36 -0
- package/dist/domain/client.d.mts +33 -0
- package/dist/domain/client.mjs +3 -0
- package/dist/domain/types.d.mts +112 -0
- package/dist/domain/types.mjs +1 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +10 -0
- package/package.json +35 -0
- package/skill-openclaw/SKILL.md +97 -0
- package/skill-openclaw/examples.md +54 -0
- package/skill-openclaw/publish.json +7 -0
|
@@ -0,0 +1,794 @@
|
|
|
1
|
+
import { renderDownloadOutput, runDownloadCommand } from "./commands/download.mjs";
|
|
2
|
+
import { renderGetOutput, runGetCommand } from "./commands/get.mjs";
|
|
3
|
+
import { renderSearchOutput, runSearchCommand } from "./commands/search.mjs";
|
|
4
|
+
import { a as BYR_INCLDEAD_FACET, c as parseCategoryAliases, i as BYR_BOOKMARKED_FACET, l as parseSimpleFacetAliases, o as BYR_SPSTATE_FACET, s as getByrMetadata, t as createByrClient } from "./client-DT9oDCaE.mjs";
|
|
5
|
+
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
6
|
+
import { CliAppError, EXIT_CODES, createCommandContext, createErrorEnvelope, createSuccessEnvelope, mapErrorCodeToExitCode, toCliAppError } from "@onemoreproduct/cli-core";
|
|
7
|
+
import { copyFileSync, existsSync, readFileSync, statSync } from "node:fs";
|
|
8
|
+
import { homedir, tmpdir } from "node:os";
|
|
9
|
+
import { dirname, join } from "node:path";
|
|
10
|
+
import { createDecipheriv, pbkdf2Sync, randomUUID } from "node:crypto";
|
|
11
|
+
import { spawnSync } from "node:child_process";
|
|
12
|
+
|
|
13
|
+
//#region src/domain/auth/browser.ts
|
|
14
|
+
const TARGET_DOMAINS = new Set([
|
|
15
|
+
".byr.pt",
|
|
16
|
+
"byr.pt",
|
|
17
|
+
".bt.byr.cn",
|
|
18
|
+
"bt.byr.cn"
|
|
19
|
+
]);
|
|
20
|
+
async function importCookieFromBrowser(browser, profile) {
|
|
21
|
+
if (process.platform !== "darwin") throw new CliAppError({
|
|
22
|
+
code: "E_AUTH_REQUIRED",
|
|
23
|
+
message: `Browser cookie import is only supported on macOS (requested: ${browser})`
|
|
24
|
+
});
|
|
25
|
+
if (browser === "chrome") return importCookieFromChrome(profile);
|
|
26
|
+
return importCookieFromSafari();
|
|
27
|
+
}
|
|
28
|
+
function importCookieFromChrome(profile) {
|
|
29
|
+
const profilesRoot = join(process.env.HOME ?? "", "Library", "Application Support", "Google", "Chrome");
|
|
30
|
+
const profileName = profile?.trim().length ? profile.trim() : "Default";
|
|
31
|
+
const selectedDb = join(profilesRoot, profileName, "Cookies");
|
|
32
|
+
const fallbackDb = join(profilesRoot, "Default", "Cookies");
|
|
33
|
+
const dbPath = existsSync(selectedDb) ? selectedDb : fallbackDb;
|
|
34
|
+
if (!existsSync(dbPath)) throw new CliAppError({
|
|
35
|
+
code: "E_AUTH_REQUIRED",
|
|
36
|
+
message: "Chrome cookies database not found",
|
|
37
|
+
details: { checked: [selectedDb, fallbackDb] }
|
|
38
|
+
});
|
|
39
|
+
const tempDir = join(tmpdir(), `byr-cli-${randomUUID()}`);
|
|
40
|
+
const tempDb = join(tempDir, "Cookies");
|
|
41
|
+
spawnSync("mkdir", ["-p", tempDir], { stdio: "ignore" });
|
|
42
|
+
copyFileSync(dbPath, tempDb);
|
|
43
|
+
const wal = `${dbPath}-wal`;
|
|
44
|
+
const shm = `${dbPath}-shm`;
|
|
45
|
+
if (existsSync(wal)) copyFileSync(wal, `${tempDb}-wal`);
|
|
46
|
+
if (existsSync(shm)) copyFileSync(shm, `${tempDb}-shm`);
|
|
47
|
+
const sqlite = spawnSync("sqlite3", [
|
|
48
|
+
"-separator",
|
|
49
|
+
" ",
|
|
50
|
+
tempDb,
|
|
51
|
+
"SELECT name, value, hex(encrypted_value) FROM cookies WHERE host_key IN ('.byr.pt','byr.pt','.bt.byr.cn','bt.byr.cn') AND name IN ('uid','pass')"
|
|
52
|
+
], { encoding: "utf8" });
|
|
53
|
+
if (sqlite.status !== 0) throw new CliAppError({
|
|
54
|
+
code: "E_AUTH_REQUIRED",
|
|
55
|
+
message: "Failed to read Chrome cookies with sqlite3",
|
|
56
|
+
details: { stderr: sqlite.stderr?.trim() }
|
|
57
|
+
});
|
|
58
|
+
const decrypted = /* @__PURE__ */ new Map();
|
|
59
|
+
const lines = (sqlite.stdout ?? "").split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
60
|
+
let keyHex;
|
|
61
|
+
for (const line of lines) {
|
|
62
|
+
const [name, value, encryptedHex] = line.split(" ");
|
|
63
|
+
if (name !== "uid" && name !== "pass") continue;
|
|
64
|
+
if (value && value.length > 0) {
|
|
65
|
+
decrypted.set(name, value);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (!keyHex) keyHex = getChromeSafeStorageKeyHex();
|
|
69
|
+
if (!encryptedHex || encryptedHex.length === 0 || !keyHex) continue;
|
|
70
|
+
const resolved = decryptChromeCookieHex(encryptedHex, keyHex);
|
|
71
|
+
if (resolved) decrypted.set(name, resolved);
|
|
72
|
+
}
|
|
73
|
+
const uid = decrypted.get("uid");
|
|
74
|
+
const pass = decrypted.get("pass");
|
|
75
|
+
if (!uid || !pass) throw new CliAppError({
|
|
76
|
+
code: "E_AUTH_REQUIRED",
|
|
77
|
+
message: "Unable to extract uid/pass from Chrome cookies",
|
|
78
|
+
details: { profile: profileName }
|
|
79
|
+
});
|
|
80
|
+
return {
|
|
81
|
+
cookie: `uid=${uid}; pass=${pass}`,
|
|
82
|
+
source: `chrome:${profileName}`
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function getChromeSafeStorageKeyHex() {
|
|
86
|
+
for (const [service, account] of [["Chrome Safe Storage", "Chrome"], ["Chrome Safe Storage", "Google Chrome"]]) {
|
|
87
|
+
const command = spawnSync("security", [
|
|
88
|
+
"find-generic-password",
|
|
89
|
+
"-w",
|
|
90
|
+
"-s",
|
|
91
|
+
service,
|
|
92
|
+
"-a",
|
|
93
|
+
account
|
|
94
|
+
], { encoding: "utf8" });
|
|
95
|
+
if (command.status !== 0) continue;
|
|
96
|
+
const password = command.stdout.trim();
|
|
97
|
+
if (password.length === 0) continue;
|
|
98
|
+
return pbkdf2Sync(password, "saltysalt", 1003, 16, "sha1").toString("hex");
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function decryptChromeCookieHex(encryptedHex, keyHex) {
|
|
102
|
+
try {
|
|
103
|
+
let encrypted = Buffer.from(encryptedHex, "hex");
|
|
104
|
+
if (encrypted.length === 0) return;
|
|
105
|
+
if (encrypted.subarray(0, 3).toString("utf8") === "v10") encrypted = encrypted.subarray(3);
|
|
106
|
+
const decipher = createDecipheriv("aes-128-cbc", Buffer.from(keyHex, "hex"), Buffer.alloc(16, 32));
|
|
107
|
+
return Buffer.concat([decipher.update(encrypted), decipher.final()]).toString("utf8").replaceAll("\0", "").trim();
|
|
108
|
+
} catch {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function importCookieFromSafari() {
|
|
113
|
+
const home = process.env.HOME ?? "";
|
|
114
|
+
const sqliteCandidates = [join(home, "Library", "Cookies", "Cookies.sqlite"), join(home, "Library", "Containers", "com.apple.Safari", "Data", "Library", "Cookies", "Cookies.sqlite")];
|
|
115
|
+
for (const sqlitePath of sqliteCandidates) {
|
|
116
|
+
if (!existsSync(sqlitePath)) continue;
|
|
117
|
+
const result = tryReadSafariSqlite(sqlitePath);
|
|
118
|
+
if (result) return result;
|
|
119
|
+
}
|
|
120
|
+
const binaryCandidates = [join(home, "Library", "Cookies", "Cookies.binarycookies"), join(home, "Library", "Containers", "com.apple.Safari", "Data", "Library", "Cookies", "Cookies.binarycookies")];
|
|
121
|
+
for (const binaryPath of binaryCandidates) {
|
|
122
|
+
if (!existsSync(binaryPath)) continue;
|
|
123
|
+
const result = tryReadSafariBinaryCookies(binaryPath);
|
|
124
|
+
if (result) return result;
|
|
125
|
+
}
|
|
126
|
+
throw new CliAppError({
|
|
127
|
+
code: "E_AUTH_REQUIRED",
|
|
128
|
+
message: "Safari cookie import failed (best effort)",
|
|
129
|
+
details: {
|
|
130
|
+
hint: "Use `byr auth import-cookie --cookie \"uid=...; pass=...\"` as fallback.",
|
|
131
|
+
checked: [...sqliteCandidates, ...binaryCandidates]
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
function tryReadSafariSqlite(path) {
|
|
136
|
+
const sqlite = spawnSync("sqlite3", [
|
|
137
|
+
"-separator",
|
|
138
|
+
" ",
|
|
139
|
+
path,
|
|
140
|
+
"SELECT name, value, host FROM cookies WHERE host IN ('.byr.pt','byr.pt','.bt.byr.cn','bt.byr.cn') AND name IN ('uid','pass')"
|
|
141
|
+
], { encoding: "utf8" });
|
|
142
|
+
if (sqlite.status !== 0) return;
|
|
143
|
+
const records = [];
|
|
144
|
+
for (const line of (sqlite.stdout ?? "").split("\n")) {
|
|
145
|
+
const trimmed = line.trim();
|
|
146
|
+
if (trimmed.length === 0) continue;
|
|
147
|
+
const [name, value, domain] = trimmed.split(" ");
|
|
148
|
+
if (!name || !value || !domain) continue;
|
|
149
|
+
records.push({
|
|
150
|
+
name,
|
|
151
|
+
value,
|
|
152
|
+
domain
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
return extractByrCookie(records, `safari-sqlite:${path}`);
|
|
156
|
+
}
|
|
157
|
+
function tryReadSafariBinaryCookies(path) {
|
|
158
|
+
let buffer;
|
|
159
|
+
try {
|
|
160
|
+
if (statSync(path).size <= 0) return;
|
|
161
|
+
buffer = readFileSync(path);
|
|
162
|
+
} catch {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
return extractByrCookie(parseBinaryCookies(buffer), `safari-binarycookies:${path}`);
|
|
166
|
+
}
|
|
167
|
+
function extractByrCookie(records, source) {
|
|
168
|
+
const map = /* @__PURE__ */ new Map();
|
|
169
|
+
for (const record of records) {
|
|
170
|
+
if (!TARGET_DOMAINS.has(record.domain)) continue;
|
|
171
|
+
if (record.name === "uid" || record.name === "pass") map.set(record.name, record.value);
|
|
172
|
+
}
|
|
173
|
+
const uid = map.get("uid");
|
|
174
|
+
const pass = map.get("pass");
|
|
175
|
+
if (!uid || !pass) return;
|
|
176
|
+
return {
|
|
177
|
+
cookie: `uid=${uid}; pass=${pass}`,
|
|
178
|
+
source
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function parseBinaryCookies(buffer) {
|
|
182
|
+
if (buffer.length < 8 || buffer.subarray(0, 4).toString("ascii") !== "cook") return [];
|
|
183
|
+
const numPages = buffer.readUInt32BE(4);
|
|
184
|
+
const pageSizes = [];
|
|
185
|
+
let cursor = 8;
|
|
186
|
+
for (let index = 0; index < numPages; index += 1) {
|
|
187
|
+
if (cursor + 4 > buffer.length) return [];
|
|
188
|
+
pageSizes.push(buffer.readUInt32BE(cursor));
|
|
189
|
+
cursor += 4;
|
|
190
|
+
}
|
|
191
|
+
const records = [];
|
|
192
|
+
let pageOffset = cursor;
|
|
193
|
+
for (const pageSize of pageSizes) {
|
|
194
|
+
if (pageOffset + pageSize > buffer.length) break;
|
|
195
|
+
const page = buffer.subarray(pageOffset, pageOffset + pageSize);
|
|
196
|
+
records.push(...parseBinaryCookiePage(page));
|
|
197
|
+
pageOffset += pageSize;
|
|
198
|
+
}
|
|
199
|
+
return records;
|
|
200
|
+
}
|
|
201
|
+
function parseBinaryCookiePage(page) {
|
|
202
|
+
if (page.length < 8) return [];
|
|
203
|
+
const count = page.readUInt32LE(4);
|
|
204
|
+
const records = [];
|
|
205
|
+
let offsetTable = 8;
|
|
206
|
+
for (let index = 0; index < count; index += 1) {
|
|
207
|
+
if (offsetTable + 4 > page.length) break;
|
|
208
|
+
const cookieOffset = page.readUInt32LE(offsetTable);
|
|
209
|
+
offsetTable += 4;
|
|
210
|
+
const parsed = parseBinaryCookie(page, cookieOffset);
|
|
211
|
+
if (parsed !== void 0) records.push(parsed);
|
|
212
|
+
}
|
|
213
|
+
return records;
|
|
214
|
+
}
|
|
215
|
+
function parseBinaryCookie(page, cookieOffset) {
|
|
216
|
+
if (cookieOffset + 40 > page.length) return;
|
|
217
|
+
const size = page.readUInt32LE(cookieOffset);
|
|
218
|
+
if (size <= 0 || cookieOffset + size > page.length) return;
|
|
219
|
+
const slice = page.subarray(cookieOffset, cookieOffset + size);
|
|
220
|
+
const domainOffset = slice.readUInt32LE(16);
|
|
221
|
+
const nameOffset = slice.readUInt32LE(20);
|
|
222
|
+
const valueOffset = slice.readUInt32LE(28);
|
|
223
|
+
const domain = readCString(slice, domainOffset);
|
|
224
|
+
const name = readCString(slice, nameOffset);
|
|
225
|
+
const value = readCString(slice, valueOffset);
|
|
226
|
+
if (!domain || !name || !value) return;
|
|
227
|
+
return {
|
|
228
|
+
domain,
|
|
229
|
+
name,
|
|
230
|
+
value
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function readCString(buffer, offset) {
|
|
234
|
+
if (offset <= 0 || offset >= buffer.length) return "";
|
|
235
|
+
let end = offset;
|
|
236
|
+
while (end < buffer.length && buffer[end] !== 0) end += 1;
|
|
237
|
+
return buffer.subarray(offset, end).toString("utf8").trim();
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
//#endregion
|
|
241
|
+
//#region src/domain/auth/store.ts
|
|
242
|
+
function getByrConfigDir() {
|
|
243
|
+
return join(homedir(), ".config", "byr-cli");
|
|
244
|
+
}
|
|
245
|
+
function getByrGlobalConfigPath() {
|
|
246
|
+
return join(getByrConfigDir(), "config.json");
|
|
247
|
+
}
|
|
248
|
+
function getByrAuthStorePath() {
|
|
249
|
+
return join(getByrConfigDir(), "auth.json");
|
|
250
|
+
}
|
|
251
|
+
async function readJsonFile(path) {
|
|
252
|
+
try {
|
|
253
|
+
const content = await readFile(path, "utf8");
|
|
254
|
+
return JSON.parse(content);
|
|
255
|
+
} catch (error) {
|
|
256
|
+
const nodeError = error;
|
|
257
|
+
if (nodeError.code === "ENOENT") return;
|
|
258
|
+
throw new CliAppError({
|
|
259
|
+
code: "E_AUTH_INVALID",
|
|
260
|
+
message: `Failed to read JSON file: ${path}`,
|
|
261
|
+
details: {
|
|
262
|
+
path,
|
|
263
|
+
reason: nodeError.message
|
|
264
|
+
},
|
|
265
|
+
cause: error
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
async function writeJsonFile(path, data) {
|
|
270
|
+
await mkdir(dirname(path), { recursive: true });
|
|
271
|
+
await writeFile(path, `${JSON.stringify(data, null, 2)}\n`, "utf8");
|
|
272
|
+
}
|
|
273
|
+
async function readAuthStore() {
|
|
274
|
+
const store = await readJsonFile(getByrAuthStorePath());
|
|
275
|
+
if (store === void 0) return;
|
|
276
|
+
if (typeof store.cookie !== "string" || typeof store.source !== "string" || typeof store.updatedAt !== "string") return;
|
|
277
|
+
return {
|
|
278
|
+
cookie: store.cookie,
|
|
279
|
+
source: store.source,
|
|
280
|
+
updatedAt: store.updatedAt
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
async function writeAuthStore(cookie, source) {
|
|
284
|
+
const store = {
|
|
285
|
+
cookie,
|
|
286
|
+
source,
|
|
287
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
288
|
+
};
|
|
289
|
+
await writeJsonFile(getByrAuthStorePath(), store);
|
|
290
|
+
return store;
|
|
291
|
+
}
|
|
292
|
+
async function clearAuthStore() {
|
|
293
|
+
try {
|
|
294
|
+
await rm(getByrAuthStorePath(), { force: true });
|
|
295
|
+
return true;
|
|
296
|
+
} catch (error) {
|
|
297
|
+
throw new CliAppError({
|
|
298
|
+
code: "E_AUTH_INVALID",
|
|
299
|
+
message: "Failed to clear BYR auth store",
|
|
300
|
+
details: { reason: error.message },
|
|
301
|
+
cause: error
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
function parseCookieHeader(cookieHeader) {
|
|
306
|
+
const cookieMap = /* @__PURE__ */ new Map();
|
|
307
|
+
for (const part of cookieHeader.split(";")) {
|
|
308
|
+
const segment = part.trim();
|
|
309
|
+
if (segment.length === 0) continue;
|
|
310
|
+
const eqIndex = segment.indexOf("=");
|
|
311
|
+
if (eqIndex <= 0) continue;
|
|
312
|
+
const name = segment.slice(0, eqIndex).trim();
|
|
313
|
+
const value = segment.slice(eqIndex + 1).trim();
|
|
314
|
+
if (name.length > 0) cookieMap.set(name, value);
|
|
315
|
+
}
|
|
316
|
+
return {
|
|
317
|
+
uid: cookieMap.get("uid"),
|
|
318
|
+
pass: cookieMap.get("pass"),
|
|
319
|
+
sessionId: cookieMap.get("session_id"),
|
|
320
|
+
authToken: cookieMap.get("auth_token"),
|
|
321
|
+
refreshToken: cookieMap.get("refresh_token"),
|
|
322
|
+
cookieMap
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
function validateByrCookie(cookieHeader) {
|
|
326
|
+
const parsed = parseCookieHeader(cookieHeader);
|
|
327
|
+
const hasLegacyCookie = Boolean(parsed.uid && parsed.pass);
|
|
328
|
+
const hasSessionCookie = Boolean(parsed.sessionId && parsed.authToken);
|
|
329
|
+
if (!hasLegacyCookie && !hasSessionCookie) throw new CliAppError({
|
|
330
|
+
code: "E_AUTH_INVALID",
|
|
331
|
+
message: "BYR cookie must include uid/pass or session_id/auth_token",
|
|
332
|
+
details: { requiredAnyOf: [["uid", "pass"], ["session_id", "auth_token"]] }
|
|
333
|
+
});
|
|
334
|
+
return parsed;
|
|
335
|
+
}
|
|
336
|
+
function maskCookieValue(value, visible = 4) {
|
|
337
|
+
if (value.length <= visible) return "*".repeat(value.length);
|
|
338
|
+
return `${value.slice(0, visible)}${"*".repeat(Math.max(4, value.length - visible))}`;
|
|
339
|
+
}
|
|
340
|
+
function maskCookieHeader(cookieHeader) {
|
|
341
|
+
const parsed = parseCookieHeader(cookieHeader);
|
|
342
|
+
return Array.from(parsed.cookieMap.entries()).map(([name, value]) => name === "uid" || name === "pass" ? `${name}=${maskCookieValue(value)}` : `${name}=***`).join("; ");
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
//#endregion
|
|
346
|
+
//#region src/domain/auth/config.ts
|
|
347
|
+
function firstString(value) {
|
|
348
|
+
if (value === void 0) return;
|
|
349
|
+
if (Array.isArray(value)) {
|
|
350
|
+
const candidate = value.at(-1);
|
|
351
|
+
return typeof candidate === "string" ? candidate : void 0;
|
|
352
|
+
}
|
|
353
|
+
return typeof value === "string" ? value : void 0;
|
|
354
|
+
}
|
|
355
|
+
function toValidTimeout(value) {
|
|
356
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) return Math.floor(value);
|
|
357
|
+
if (typeof value === "string") {
|
|
358
|
+
const parsed = Number.parseInt(value, 10);
|
|
359
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
function nonEmpty(value) {
|
|
363
|
+
if (typeof value !== "string") return;
|
|
364
|
+
const trimmed = value.trim();
|
|
365
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
366
|
+
}
|
|
367
|
+
function getProjectConfigPath(cwd) {
|
|
368
|
+
return join(cwd, ".byrrc.json");
|
|
369
|
+
}
|
|
370
|
+
async function resolveClientConfig(input) {
|
|
371
|
+
const env = input.env ?? process.env;
|
|
372
|
+
const projectConfig = await readJsonFile(getProjectConfigPath(input.cwd)) ?? {};
|
|
373
|
+
const globalConfig = await readJsonFile(getByrGlobalConfigPath()) ?? {};
|
|
374
|
+
const authStore = await readAuthStore();
|
|
375
|
+
const cookieFromFlag = nonEmpty(firstString(input.getFlag("cookie")));
|
|
376
|
+
const usernameFromFlag = nonEmpty(firstString(input.getFlag("username")));
|
|
377
|
+
const passwordFromFlag = nonEmpty(firstString(input.getFlag("password")));
|
|
378
|
+
const baseUrlFromFlag = nonEmpty(firstString(input.getFlag("base-url")));
|
|
379
|
+
const timeoutFromFlag = toValidTimeout(firstString(input.getFlag("timeout-ms")));
|
|
380
|
+
const cookieFromEnv = nonEmpty(env.BYR_COOKIE);
|
|
381
|
+
const usernameFromEnv = nonEmpty(env.BYR_USERNAME);
|
|
382
|
+
const passwordFromEnv = nonEmpty(env.BYR_PASSWORD);
|
|
383
|
+
const baseUrlFromEnv = nonEmpty(env.BYR_BASE_URL);
|
|
384
|
+
const timeoutFromEnv = toValidTimeout(env.BYR_TIMEOUT_MS);
|
|
385
|
+
const cookieFromProject = nonEmpty(projectConfig.cookie);
|
|
386
|
+
const usernameFromProject = nonEmpty(projectConfig.username);
|
|
387
|
+
const passwordFromProject = nonEmpty(projectConfig.password);
|
|
388
|
+
const baseUrlFromProject = nonEmpty(projectConfig.baseUrl);
|
|
389
|
+
const timeoutFromProject = toValidTimeout(projectConfig.timeoutMs);
|
|
390
|
+
const cookieFromGlobal = nonEmpty(globalConfig.cookie);
|
|
391
|
+
const usernameFromGlobal = nonEmpty(globalConfig.username);
|
|
392
|
+
const passwordFromGlobal = nonEmpty(globalConfig.password);
|
|
393
|
+
const baseUrlFromGlobal = nonEmpty(globalConfig.baseUrl);
|
|
394
|
+
const timeoutFromGlobal = toValidTimeout(globalConfig.timeoutMs);
|
|
395
|
+
const cookieFromStore = nonEmpty(authStore?.cookie);
|
|
396
|
+
const cookie = cookieFromFlag ?? cookieFromEnv ?? cookieFromProject ?? cookieFromGlobal ?? cookieFromStore;
|
|
397
|
+
const cookieSource = cookie === void 0 ? void 0 : cookie === cookieFromFlag ? "flag" : cookie === cookieFromEnv ? "env" : cookie === cookieFromProject ? "project-config" : cookie === cookieFromGlobal ? "global-config" : authStore?.source ?? "auth-store";
|
|
398
|
+
return {
|
|
399
|
+
cookie,
|
|
400
|
+
username: usernameFromFlag ?? usernameFromEnv ?? usernameFromProject ?? usernameFromGlobal,
|
|
401
|
+
password: passwordFromFlag ?? passwordFromEnv ?? passwordFromProject ?? passwordFromGlobal,
|
|
402
|
+
baseUrl: baseUrlFromFlag ?? baseUrlFromEnv ?? baseUrlFromProject ?? baseUrlFromGlobal,
|
|
403
|
+
timeoutMs: timeoutFromFlag ?? timeoutFromEnv ?? timeoutFromProject ?? timeoutFromGlobal,
|
|
404
|
+
cookieSource
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
//#endregion
|
|
409
|
+
//#region src/cli.ts
|
|
410
|
+
async function runCli(argv, deps = {}) {
|
|
411
|
+
const stdout = deps.stdout ?? process.stdout;
|
|
412
|
+
const stderr = deps.stderr ?? process.stderr;
|
|
413
|
+
const parsed = parseArgs(argv);
|
|
414
|
+
const json = getBooleanFlag(parsed.flags, "json");
|
|
415
|
+
const verbose = getBooleanFlag(parsed.flags, "verbose");
|
|
416
|
+
const context = createCommandContext({
|
|
417
|
+
clock: deps.clock,
|
|
418
|
+
now: deps.now,
|
|
419
|
+
requestIdFactory: deps.requestIdFactory,
|
|
420
|
+
verbose
|
|
421
|
+
});
|
|
422
|
+
if (parsed.command === void 0 || parsed.command === "help") {
|
|
423
|
+
stdout.write(`${renderHelp()}\n`);
|
|
424
|
+
return EXIT_CODES.SUCCESS;
|
|
425
|
+
}
|
|
426
|
+
let resolvedClient;
|
|
427
|
+
const getClient = async () => {
|
|
428
|
+
if (deps.client) return deps.client;
|
|
429
|
+
if (resolvedClient) return resolvedClient;
|
|
430
|
+
const resolved = await resolveClientConfig({
|
|
431
|
+
cwd: process.cwd(),
|
|
432
|
+
env: process.env,
|
|
433
|
+
getFlag: (key) => parsed.flags.get(key)
|
|
434
|
+
});
|
|
435
|
+
resolvedClient = createByrClient({
|
|
436
|
+
baseUrl: resolved.baseUrl,
|
|
437
|
+
timeoutMs: resolved.timeoutMs,
|
|
438
|
+
cookie: resolved.cookie,
|
|
439
|
+
username: resolved.username,
|
|
440
|
+
password: resolved.password
|
|
441
|
+
});
|
|
442
|
+
return resolvedClient;
|
|
443
|
+
};
|
|
444
|
+
try {
|
|
445
|
+
const result = await dispatch(parsed, {
|
|
446
|
+
getClient,
|
|
447
|
+
fileWriter: deps.fileWriter ?? writeFile
|
|
448
|
+
});
|
|
449
|
+
if (json) stdout.write(`${JSON.stringify(createSuccessEnvelope(result.data, context.toMeta()))}\n`);
|
|
450
|
+
else stdout.write(`${result.humanOutput}\n`);
|
|
451
|
+
return EXIT_CODES.SUCCESS;
|
|
452
|
+
} catch (error) {
|
|
453
|
+
const appError = toCliAppError(error);
|
|
454
|
+
const exitCode = mapErrorCodeToExitCode(appError.code);
|
|
455
|
+
if (json) stdout.write(`${JSON.stringify(createErrorEnvelope(appError.code, appError.message, appError.details))}\n`);
|
|
456
|
+
else stderr.write(formatHumanError(appError));
|
|
457
|
+
return exitCode;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
async function dispatch(parsed, deps) {
|
|
461
|
+
switch (parsed.command) {
|
|
462
|
+
case "search": return dispatchSearch(parsed, deps);
|
|
463
|
+
case "get": return dispatchGet(parsed, deps);
|
|
464
|
+
case "download": return dispatchDownload(parsed, deps);
|
|
465
|
+
case "user": return dispatchUser(parsed, deps);
|
|
466
|
+
case "meta": return dispatchMeta(parsed, deps);
|
|
467
|
+
case "auth": return dispatchAuth(parsed, deps);
|
|
468
|
+
default: throw createArgumentError("E_ARG_UNSUPPORTED", `Unknown command: ${parsed.command}`, { command: parsed.command });
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
async function dispatchSearch(parsed, deps) {
|
|
472
|
+
const query = getOptionalString(parsed.flags, "query") ?? "";
|
|
473
|
+
const imdb = getOptionalString(parsed.flags, "imdb");
|
|
474
|
+
const limit = getPositiveInteger(parsed.flags, "limit", 10);
|
|
475
|
+
if (query.trim().length === 0 && (imdb === void 0 || imdb.trim().length === 0)) throw createArgumentError("E_ARG_MISSING", "--query or --imdb is required", { arg: "query|imdb" });
|
|
476
|
+
if (query.trim().length > 0 && imdb !== void 0) throw createArgumentError("E_ARG_CONFLICT", "--query and --imdb cannot be used together", { args: ["query", "imdb"] });
|
|
477
|
+
const categoryParsed = parseCategoryAliases(getStringValues(parsed.flags, "category"));
|
|
478
|
+
if (categoryParsed.invalid.length > 0) throw createArgumentError("E_ARG_INVALID", "--category contains invalid values", {
|
|
479
|
+
invalid: categoryParsed.invalid,
|
|
480
|
+
allowed: getByrMetadata().category.options.map((option) => option.aliases).flat()
|
|
481
|
+
});
|
|
482
|
+
const incldead = parseSingleFacetFlag(parsed.flags, "incldead", BYR_INCLDEAD_FACET);
|
|
483
|
+
const spstate = parseSingleFacetFlag(parsed.flags, "spstate", BYR_SPSTATE_FACET);
|
|
484
|
+
const bookmarked = parseSingleFacetFlag(parsed.flags, "bookmarked", BYR_BOOKMARKED_FACET);
|
|
485
|
+
const page = getOptionalPositiveInteger(parsed.flags, "page");
|
|
486
|
+
const options = {
|
|
487
|
+
categoryIds: categoryParsed.values.length > 0 ? categoryParsed.values : void 0,
|
|
488
|
+
incldead,
|
|
489
|
+
spstate,
|
|
490
|
+
bookmarked,
|
|
491
|
+
imdb,
|
|
492
|
+
page
|
|
493
|
+
};
|
|
494
|
+
const output = await runSearchCommand(await deps.getClient(), {
|
|
495
|
+
query,
|
|
496
|
+
limit,
|
|
497
|
+
options
|
|
498
|
+
});
|
|
499
|
+
return {
|
|
500
|
+
data: output,
|
|
501
|
+
humanOutput: renderSearchOutput(output)
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
async function dispatchGet(parsed, deps) {
|
|
505
|
+
const id = getRequiredString(parsed.flags, "id");
|
|
506
|
+
const output = await runGetCommand(await deps.getClient(), { id });
|
|
507
|
+
return {
|
|
508
|
+
data: output,
|
|
509
|
+
humanOutput: renderGetOutput(output)
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
async function dispatchDownload(parsed, deps) {
|
|
513
|
+
const id = getRequiredString(parsed.flags, "id");
|
|
514
|
+
const outputPath = getRequiredString(parsed.flags, "output");
|
|
515
|
+
const dryRun = getBooleanFlag(parsed.flags, "dry-run");
|
|
516
|
+
const output = await runDownloadCommand(await deps.getClient(), {
|
|
517
|
+
id,
|
|
518
|
+
outputPath,
|
|
519
|
+
dryRun,
|
|
520
|
+
writeFile: deps.fileWriter
|
|
521
|
+
});
|
|
522
|
+
return {
|
|
523
|
+
data: output,
|
|
524
|
+
humanOutput: renderDownloadOutput(output)
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
async function dispatchUser(parsed, deps) {
|
|
528
|
+
const subcommand = parsed.positional[0];
|
|
529
|
+
if (subcommand !== "info") throw createArgumentError("E_ARG_UNSUPPORTED", "user subcommand must be: info", { subcommand });
|
|
530
|
+
const client = await deps.getClient();
|
|
531
|
+
if (typeof client.getUserInfo !== "function") throw createArgumentError("E_ARG_UNSUPPORTED", "Current client does not support user info", {});
|
|
532
|
+
const info = await client.getUserInfo();
|
|
533
|
+
return {
|
|
534
|
+
data: info,
|
|
535
|
+
humanOutput: [
|
|
536
|
+
`User: ${info.name} (${info.id})`,
|
|
537
|
+
`Level: ${info.levelName}${info.levelId ? ` (#${info.levelId})` : ""}`,
|
|
538
|
+
`Ratio: ${info.ratio}`,
|
|
539
|
+
`Uploaded/Downloaded: ${info.trueUploadedBytes}/${info.trueDownloadedBytes}`,
|
|
540
|
+
`Messages: ${info.messageCount}`,
|
|
541
|
+
`Bonus/h: ${info.bonusPerHour}`,
|
|
542
|
+
`Seeding: ${info.seeding} (${info.seedingSizeBytes} bytes)`,
|
|
543
|
+
`Uploads: ${info.uploads}`,
|
|
544
|
+
`Last access: ${info.lastAccessAt}`
|
|
545
|
+
].join("\n")
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
async function dispatchMeta(parsed, deps) {
|
|
549
|
+
const subcommand = parsed.positional[0];
|
|
550
|
+
const client = await deps.getClient();
|
|
551
|
+
if (subcommand === "categories") {
|
|
552
|
+
const categories = typeof client.getCategories === "function" ? await client.getCategories() : getByrMetadata();
|
|
553
|
+
return {
|
|
554
|
+
data: categories,
|
|
555
|
+
humanOutput: ["BYR categories:", ...categories.category.options.map((item) => ` ${item.value}: ${item.name}`)].join("\n")
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
if (subcommand === "levels") {
|
|
559
|
+
const levels = typeof client.getLevelRequirements === "function" ? await client.getLevelRequirements() : getByrMetadata().levels;
|
|
560
|
+
return {
|
|
561
|
+
data: levels,
|
|
562
|
+
humanOutput: ["BYR levels:", ...levels.map((level) => ` #${level.id} ${level.name}`)].join("\n")
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
throw createArgumentError("E_ARG_UNSUPPORTED", "meta subcommand must be: categories|levels", { subcommand });
|
|
566
|
+
}
|
|
567
|
+
async function dispatchAuth(parsed, deps) {
|
|
568
|
+
const subcommand = parsed.positional[0];
|
|
569
|
+
switch (subcommand) {
|
|
570
|
+
case "status": {
|
|
571
|
+
const verify = getBooleanFlag(parsed.flags, "verify");
|
|
572
|
+
const resolved = await resolveClientConfig({
|
|
573
|
+
cwd: process.cwd(),
|
|
574
|
+
env: process.env,
|
|
575
|
+
getFlag: (key) => parsed.flags.get(key)
|
|
576
|
+
});
|
|
577
|
+
const hasCredentials = typeof resolved.cookie === "string" && resolved.cookie.trim().length > 0;
|
|
578
|
+
const result = {
|
|
579
|
+
hasCredentials,
|
|
580
|
+
source: resolved.cookieSource ?? "none",
|
|
581
|
+
cookie: hasCredentials ? maskCookieHeader(resolved.cookie) : void 0
|
|
582
|
+
};
|
|
583
|
+
if (verify && hasCredentials) {
|
|
584
|
+
const verifier = (await deps.getClient()).verifyAuth;
|
|
585
|
+
if (typeof verifier === "function") result.verify = await verifier();
|
|
586
|
+
else result.verify = { authenticated: false };
|
|
587
|
+
}
|
|
588
|
+
return {
|
|
589
|
+
data: result,
|
|
590
|
+
humanOutput: [
|
|
591
|
+
`Credentials: ${hasCredentials ? "present" : "missing"}`,
|
|
592
|
+
`Source: ${String(result.source)}`,
|
|
593
|
+
verify ? `Verified: ${String(result.verify?.authenticated ?? false)}` : void 0
|
|
594
|
+
].filter((line) => Boolean(line)).join("\n")
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
case "import-cookie": {
|
|
598
|
+
const manualCookie = getOptionalString(parsed.flags, "cookie");
|
|
599
|
+
const fromBrowser = getOptionalString(parsed.flags, "from-browser");
|
|
600
|
+
const profile = getOptionalString(parsed.flags, "profile");
|
|
601
|
+
if (manualCookie && fromBrowser || !manualCookie && !fromBrowser) throw createArgumentError("E_ARG_CONFLICT", "Use either --cookie or --from-browser", { args: ["cookie", "from-browser"] });
|
|
602
|
+
let cookieToStore;
|
|
603
|
+
let source;
|
|
604
|
+
if (manualCookie) {
|
|
605
|
+
validateByrCookie(manualCookie);
|
|
606
|
+
cookieToStore = manualCookie;
|
|
607
|
+
source = "manual";
|
|
608
|
+
} else {
|
|
609
|
+
if (fromBrowser !== "chrome" && fromBrowser !== "safari") throw createArgumentError("E_ARG_INVALID", "--from-browser must be chrome|safari", { value: fromBrowser });
|
|
610
|
+
const imported = await importCookieFromBrowser(fromBrowser, profile);
|
|
611
|
+
validateByrCookie(imported.cookie);
|
|
612
|
+
cookieToStore = imported.cookie;
|
|
613
|
+
source = imported.source;
|
|
614
|
+
}
|
|
615
|
+
const saved = await writeAuthStore(cookieToStore, source);
|
|
616
|
+
return {
|
|
617
|
+
data: {
|
|
618
|
+
source: saved.source,
|
|
619
|
+
updatedAt: saved.updatedAt,
|
|
620
|
+
cookie: maskCookieHeader(saved.cookie)
|
|
621
|
+
},
|
|
622
|
+
humanOutput: [
|
|
623
|
+
"BYR cookie imported.",
|
|
624
|
+
`Source: ${saved.source}`,
|
|
625
|
+
`Updated: ${saved.updatedAt}`
|
|
626
|
+
].join("\n")
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
case "logout": {
|
|
630
|
+
const deleted = await clearAuthStore();
|
|
631
|
+
return {
|
|
632
|
+
data: { deleted },
|
|
633
|
+
humanOutput: deleted ? "BYR auth store cleared." : "BYR auth store was already empty."
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
default: throw createArgumentError("E_ARG_UNSUPPORTED", "auth subcommand must be: status|import-cookie|logout", { subcommand });
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
function parseArgs(argv) {
|
|
640
|
+
const command = argv[0];
|
|
641
|
+
const flags = /* @__PURE__ */ new Map();
|
|
642
|
+
const positional = [];
|
|
643
|
+
for (let index = 1; index < argv.length; index += 1) {
|
|
644
|
+
const token = argv[index];
|
|
645
|
+
if (!token.startsWith("--")) {
|
|
646
|
+
positional.push(token);
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
649
|
+
const stripped = token.slice(2);
|
|
650
|
+
const eqIndex = stripped.indexOf("=");
|
|
651
|
+
if (eqIndex >= 0) {
|
|
652
|
+
appendFlagValue(flags, stripped.slice(0, eqIndex), stripped.slice(eqIndex + 1));
|
|
653
|
+
continue;
|
|
654
|
+
}
|
|
655
|
+
const next = argv[index + 1];
|
|
656
|
+
if (next !== void 0 && !next.startsWith("--")) {
|
|
657
|
+
appendFlagValue(flags, stripped, next);
|
|
658
|
+
index += 1;
|
|
659
|
+
continue;
|
|
660
|
+
}
|
|
661
|
+
flags.set(stripped, true);
|
|
662
|
+
}
|
|
663
|
+
return {
|
|
664
|
+
command,
|
|
665
|
+
flags,
|
|
666
|
+
positional
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
function appendFlagValue(flags, key, value) {
|
|
670
|
+
const previous = flags.get(key);
|
|
671
|
+
if (previous === void 0) {
|
|
672
|
+
flags.set(key, value);
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
if (Array.isArray(previous)) {
|
|
676
|
+
flags.set(key, [...previous, value]);
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
if (typeof previous === "string") {
|
|
680
|
+
flags.set(key, [previous, value]);
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
flags.set(key, value);
|
|
684
|
+
}
|
|
685
|
+
function getBooleanFlag(flags, key) {
|
|
686
|
+
const value = flags.get(key);
|
|
687
|
+
if (value === void 0) return false;
|
|
688
|
+
if (typeof value === "boolean") return value;
|
|
689
|
+
if (Array.isArray(value)) {
|
|
690
|
+
const candidate = value.at(-1);
|
|
691
|
+
if (candidate === "true") return true;
|
|
692
|
+
if (candidate === "false") return false;
|
|
693
|
+
throw createArgumentError("E_ARG_INVALID", `--${key} must be true or false`, {
|
|
694
|
+
arg: key,
|
|
695
|
+
value: candidate
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
if (value === "true") return true;
|
|
699
|
+
if (value === "false") return false;
|
|
700
|
+
throw createArgumentError("E_ARG_INVALID", `--${key} must be true or false`, {
|
|
701
|
+
arg: key,
|
|
702
|
+
value
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
function getRequiredString(flags, key) {
|
|
706
|
+
const value = getOptionalString(flags, key);
|
|
707
|
+
if (value === void 0 || value.trim().length === 0) throw createArgumentError("E_ARG_MISSING", `--${key} is required`, { arg: key });
|
|
708
|
+
return value;
|
|
709
|
+
}
|
|
710
|
+
function getOptionalString(flags, key) {
|
|
711
|
+
const value = flags.get(key);
|
|
712
|
+
if (value === void 0 || typeof value === "boolean") return;
|
|
713
|
+
if (Array.isArray(value)) {
|
|
714
|
+
const candidate = value.at(-1);
|
|
715
|
+
return candidate !== void 0 && candidate.trim().length > 0 ? candidate : void 0;
|
|
716
|
+
}
|
|
717
|
+
return value.trim().length > 0 ? value : void 0;
|
|
718
|
+
}
|
|
719
|
+
function getStringValues(flags, key) {
|
|
720
|
+
const value = flags.get(key);
|
|
721
|
+
if (value === void 0 || typeof value === "boolean") return [];
|
|
722
|
+
return (Array.isArray(value) ? value : [value]).filter((item) => item.trim().length > 0);
|
|
723
|
+
}
|
|
724
|
+
function getPositiveInteger(flags, key, fallback) {
|
|
725
|
+
const value = getOptionalString(flags, key);
|
|
726
|
+
if (value === void 0) return fallback;
|
|
727
|
+
const parsed = Number.parseInt(value, 10);
|
|
728
|
+
if (!Number.isFinite(parsed) || parsed <= 0) throw createArgumentError("E_ARG_INVALID", `--${key} must be a positive integer`, {
|
|
729
|
+
arg: key,
|
|
730
|
+
value
|
|
731
|
+
});
|
|
732
|
+
return parsed;
|
|
733
|
+
}
|
|
734
|
+
function getOptionalPositiveInteger(flags, key) {
|
|
735
|
+
const value = getOptionalString(flags, key);
|
|
736
|
+
if (value === void 0) return;
|
|
737
|
+
const parsed = Number.parseInt(value, 10);
|
|
738
|
+
if (!Number.isFinite(parsed) || parsed <= 0) throw createArgumentError("E_ARG_INVALID", `--${key} must be a positive integer`, {
|
|
739
|
+
arg: key,
|
|
740
|
+
value
|
|
741
|
+
});
|
|
742
|
+
return parsed;
|
|
743
|
+
}
|
|
744
|
+
function parseSingleFacetFlag(flags, key, facet) {
|
|
745
|
+
const values = getStringValues(flags, key);
|
|
746
|
+
if (values.length === 0) return;
|
|
747
|
+
const parsed = parseSimpleFacetAliases(facet, values);
|
|
748
|
+
if (parsed.invalid.length > 0) throw createArgumentError("E_ARG_INVALID", `--${key} contains invalid values`, {
|
|
749
|
+
invalid: parsed.invalid,
|
|
750
|
+
allowed: facet.options.map((option) => option.aliases).flat()
|
|
751
|
+
});
|
|
752
|
+
if (parsed.values.length !== 1) throw createArgumentError("E_ARG_INVALID", `--${key} expects exactly one value`, { values });
|
|
753
|
+
return parsed.values[0];
|
|
754
|
+
}
|
|
755
|
+
function renderHelp() {
|
|
756
|
+
return [
|
|
757
|
+
"byr CLI",
|
|
758
|
+
"",
|
|
759
|
+
"Usage:",
|
|
760
|
+
" byr search --query <text> [--limit <n>] [--category <alias|id>] [--incldead <alias|id>] [--spstate <alias|id>] [--bookmarked <alias|id>] [--page <n>] [--json]",
|
|
761
|
+
" byr search --imdb <tt-id> [--limit <n>] [--json]",
|
|
762
|
+
" byr get --id <torrent-id> [--json]",
|
|
763
|
+
" byr download --id <torrent-id> --output <path> [--dry-run] [--json]",
|
|
764
|
+
" byr user info [--json]",
|
|
765
|
+
" byr meta categories [--json]",
|
|
766
|
+
" byr meta levels [--json]",
|
|
767
|
+
" byr auth status [--verify] [--json]",
|
|
768
|
+
" byr auth import-cookie --cookie \"uid=...; pass=...\" [--json]",
|
|
769
|
+
" byr auth import-cookie --from-browser <chrome|safari> [--profile <name>] [--json]",
|
|
770
|
+
" byr auth logout [--json]",
|
|
771
|
+
"",
|
|
772
|
+
"Auth priority:",
|
|
773
|
+
" CLI flags > ENV > ./.byrrc.json > ~/.config/byr-cli/config.json > ~/.config/byr-cli/auth.json",
|
|
774
|
+
"",
|
|
775
|
+
"Flags:",
|
|
776
|
+
" --json Output CliEnvelope JSON",
|
|
777
|
+
" --dry-run Validate and show write plan without writing files",
|
|
778
|
+
" --verbose Include verbose mode in metadata"
|
|
779
|
+
].join("\n");
|
|
780
|
+
}
|
|
781
|
+
function createArgumentError(code, message, details) {
|
|
782
|
+
return new CliAppError({
|
|
783
|
+
code,
|
|
784
|
+
message,
|
|
785
|
+
details
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
function formatHumanError(error) {
|
|
789
|
+
const details = error.details === void 0 ? "" : `\nDetails: ${JSON.stringify(error.details)}`;
|
|
790
|
+
return `Error (${error.code}): ${error.message}${details}\n`;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
//#endregion
|
|
794
|
+
export { runCli as t };
|