@rhseung/ps-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,606 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getProblem,
4
+ getTierColor,
5
+ getTierImageUrl,
6
+ getTierName,
7
+ source_default
8
+ } from "../chunk-2E4VSP6O.js";
9
+ import {
10
+ getAutoOpenEditor,
11
+ getEditor
12
+ } from "../chunk-KFQFQJYT.js";
13
+ import {
14
+ getProblemId
15
+ } from "../chunk-OOTPZD7O.js";
16
+ import {
17
+ getLanguageConfig,
18
+ getSupportedLanguages,
19
+ getSupportedLanguagesString
20
+ } from "../chunk-TQXMB7XV.js";
21
+ import {
22
+ LoadingSpinner
23
+ } from "../chunk-IJLJBKLK.js";
24
+ import "../chunk-FYS2JH42.js";
25
+
26
+ // src/commands/fetch.tsx
27
+ import { useState, useEffect } from "react";
28
+ import { render, Text as Text2, Box as Box2 } from "ink";
29
+
30
+ // src/services/scraper.ts
31
+ import * as cheerio from "cheerio";
32
+ var BOJ_BASE_URL = "https://www.acmicpc.net";
33
+ function htmlToMarkdown($, element) {
34
+ if (element.length === 0) return "";
35
+ let result = "";
36
+ const contents = element.contents();
37
+ if (contents.length === 0) {
38
+ return element.text().trim();
39
+ }
40
+ contents.each((_, node) => {
41
+ if (node.type === "text") {
42
+ const text = node.data || "";
43
+ if (text.trim()) {
44
+ result += text;
45
+ }
46
+ } else if (node.type === "tag") {
47
+ const tagName = node.name.toLowerCase();
48
+ const $node = $(node);
49
+ switch (tagName) {
50
+ case "sup":
51
+ result += `^${htmlToMarkdown($, $node)}`;
52
+ break;
53
+ case "sub":
54
+ result += `<sub>${htmlToMarkdown($, $node)}</sub>`;
55
+ break;
56
+ case "strong":
57
+ case "b":
58
+ result += `**${htmlToMarkdown($, $node)}**`;
59
+ break;
60
+ case "em":
61
+ case "i":
62
+ result += `*${htmlToMarkdown($, $node)}*`;
63
+ break;
64
+ case "br":
65
+ result += "\n";
66
+ break;
67
+ case "p":
68
+ const pContent = htmlToMarkdown($, $node);
69
+ if (pContent) {
70
+ result += pContent + "\n\n";
71
+ }
72
+ break;
73
+ case "div":
74
+ const divContent = htmlToMarkdown($, $node);
75
+ if (divContent) {
76
+ result += divContent + "\n";
77
+ }
78
+ break;
79
+ case "span":
80
+ result += htmlToMarkdown($, $node);
81
+ break;
82
+ case "code":
83
+ result += `\`${htmlToMarkdown($, $node)}\``;
84
+ break;
85
+ case "pre":
86
+ const preContent = htmlToMarkdown($, $node);
87
+ if (preContent) {
88
+ result += `
89
+ \`\`\`
90
+ ${preContent}
91
+ \`\`\`
92
+ `;
93
+ }
94
+ break;
95
+ case "ul":
96
+ case "ol":
97
+ $node.find("li").each((i, li) => {
98
+ const liContent = htmlToMarkdown($, $(li));
99
+ if (liContent) {
100
+ result += `- ${liContent}
101
+ `;
102
+ }
103
+ });
104
+ break;
105
+ case "li":
106
+ result += htmlToMarkdown($, $node);
107
+ break;
108
+ case "img":
109
+ const imgSrc = $node.attr("src") || "";
110
+ const imgAlt = $node.attr("alt") || "";
111
+ if (imgSrc) {
112
+ let imageUrl = imgSrc;
113
+ if (imgSrc.startsWith("/")) {
114
+ imageUrl = `${BOJ_BASE_URL}${imgSrc}`;
115
+ } else if (!imgSrc.startsWith("http") && !imgSrc.startsWith("data:")) {
116
+ imageUrl = `${BOJ_BASE_URL}/${imgSrc}`;
117
+ }
118
+ result += `![${imgAlt}](${imageUrl})`;
119
+ }
120
+ break;
121
+ default:
122
+ result += htmlToMarkdown($, $node);
123
+ }
124
+ }
125
+ });
126
+ return result.trim();
127
+ }
128
+ async function scrapeProblem(problemId) {
129
+ const url = `${BOJ_BASE_URL}/problem/${problemId}`;
130
+ const response = await fetch(url, {
131
+ headers: {
132
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
133
+ }
134
+ });
135
+ if (!response.ok) {
136
+ throw new Error(`Failed to fetch problem page: HTTP ${response.status}`);
137
+ }
138
+ const html = await response.text();
139
+ const $ = cheerio.load(html);
140
+ const title = $("#problem_title").text().trim();
141
+ const descriptionEl = $("#problem_description");
142
+ let description = "";
143
+ if (descriptionEl.length > 0) {
144
+ description = htmlToMarkdown($, descriptionEl).trim();
145
+ if (!description) {
146
+ description = descriptionEl.text().trim();
147
+ }
148
+ } else {
149
+ const altDesc = $('[id*="description"]').first();
150
+ if (altDesc.length > 0) {
151
+ description = altDesc.text().trim();
152
+ }
153
+ }
154
+ const inputEl = $("#problem_input");
155
+ let inputFormat = "";
156
+ if (inputEl.length > 0) {
157
+ inputFormat = htmlToMarkdown($, inputEl).trim();
158
+ if (!inputFormat) {
159
+ inputFormat = inputEl.text().trim();
160
+ }
161
+ } else {
162
+ const altInput = $('[id*="input"]').first();
163
+ if (altInput.length > 0) {
164
+ inputFormat = altInput.text().trim();
165
+ }
166
+ }
167
+ const outputEl = $("#problem_output");
168
+ let outputFormat = "";
169
+ if (outputEl.length > 0) {
170
+ outputFormat = htmlToMarkdown($, outputEl).trim();
171
+ if (!outputFormat) {
172
+ outputFormat = outputEl.text().trim();
173
+ }
174
+ } else {
175
+ const altOutput = $('[id*="output"]').first();
176
+ if (altOutput.length > 0) {
177
+ outputFormat = altOutput.text().trim();
178
+ }
179
+ }
180
+ const problemInfo = {};
181
+ const problemInfoTable = $("#problem-info");
182
+ const tableInResponsive = $(".table-responsive table");
183
+ const targetTable = problemInfoTable.length > 0 ? problemInfoTable : tableInResponsive;
184
+ if (targetTable.length > 0) {
185
+ const headerRow = targetTable.find("thead tr");
186
+ const dataRow = targetTable.find("tbody tr");
187
+ if (headerRow.length > 0 && dataRow.length > 0) {
188
+ const headers = headerRow.find("th").map((_, th) => $(th).text().trim()).get();
189
+ const values = dataRow.find("td").map((_, td) => $(td).text().trim()).get();
190
+ headers.forEach((header, index) => {
191
+ if (values[index]) {
192
+ problemInfo[header] = values[index];
193
+ }
194
+ });
195
+ } else {
196
+ targetTable.find("tr").each((_, row) => {
197
+ const tds = $(row).find("td");
198
+ if (tds.length >= 2) {
199
+ const label = $(tds[0]).text().trim();
200
+ const value = $(tds[1]).text().trim();
201
+ problemInfo[label] = value;
202
+ }
203
+ });
204
+ }
205
+ }
206
+ const timeLimit = problemInfo["\uC2DC\uAC04 \uC81C\uD55C"] || problemInfo["Time Limit"] || void 0;
207
+ const memoryLimit = problemInfo["\uBA54\uBAA8\uB9AC \uC81C\uD55C"] || problemInfo["Memory Limit"] || void 0;
208
+ const submissions = problemInfo["\uC81C\uCD9C"] || problemInfo["Submit"] || void 0;
209
+ const accepted = problemInfo["\uC815\uB2F5"] || problemInfo["Accepted"] || void 0;
210
+ const acceptedUsers = problemInfo["\uB9DE\uD78C \uC0AC\uB78C"] || problemInfo["Accepted Users"] || void 0;
211
+ const acceptedRate = problemInfo["\uC815\uB2F5 \uBE44\uC728"] || problemInfo["Accepted Rate"] || void 0;
212
+ const testCases = [];
213
+ const sampleInputs = $(".sampledata").filter((_, el) => {
214
+ const id = $(el).attr("id");
215
+ return id?.startsWith("sample-input-") ?? false;
216
+ });
217
+ sampleInputs.each((_, el) => {
218
+ const inputId = $(el).attr("id");
219
+ if (!inputId) return;
220
+ const match = inputId.match(/sample-input-(\d+)/);
221
+ if (!match) return;
222
+ const sampleNumber = match[1];
223
+ const outputId = `sample-output-${sampleNumber}`;
224
+ const outputEl2 = $(`#${outputId}`);
225
+ if (outputEl2.length > 0) {
226
+ testCases.push({
227
+ input: $(el).text(),
228
+ output: outputEl2.text()
229
+ });
230
+ }
231
+ });
232
+ if (testCases.length === 0) {
233
+ $("pre").each((_, el) => {
234
+ const text = $(el).text().trim();
235
+ const prevText = $(el).prev().text().toLowerCase();
236
+ if (prevText.includes("\uC785\uB825") || prevText.includes("input")) {
237
+ const nextPre = $(el).next("pre");
238
+ if (nextPre.length > 0) {
239
+ testCases.push({
240
+ input: text,
241
+ output: nextPre.text().trim()
242
+ });
243
+ }
244
+ }
245
+ });
246
+ }
247
+ if (!title) {
248
+ throw new Error(
249
+ `\uBB38\uC81C ${problemId}\uC758 \uC81C\uBAA9\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. BOJ \uD398\uC774\uC9C0 \uAD6C\uC870\uAC00 \uBCC0\uACBD\uB418\uC5C8\uAC70\uB098 \uBB38\uC81C\uAC00 \uC874\uC7AC\uD558\uC9C0 \uC54A\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.`
250
+ );
251
+ }
252
+ if (!description && !inputFormat && !outputFormat) {
253
+ throw new Error(
254
+ `\uBB38\uC81C ${problemId}\uC758 \uB0B4\uC6A9\uC744 \uAC00\uC838\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. BOJ \uD398\uC774\uC9C0 \uAD6C\uC870\uAC00 \uBCC0\uACBD\uB418\uC5C8\uAC70\uB098 API \uC81C\uD55C\uC5D0 \uAC78\uB838\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uC7A0\uC2DC \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694.`
255
+ );
256
+ }
257
+ return {
258
+ title,
259
+ description,
260
+ inputFormat,
261
+ outputFormat,
262
+ testCases,
263
+ timeLimit,
264
+ memoryLimit,
265
+ submissions,
266
+ accepted,
267
+ acceptedUsers,
268
+ acceptedRate
269
+ };
270
+ }
271
+
272
+ // src/services/file-generator.ts
273
+ import { mkdir, writeFile, readFile } from "fs/promises";
274
+ import { join, dirname } from "path";
275
+ import { fileURLToPath } from "url";
276
+ function parseTimeLimitToMs(timeLimit) {
277
+ if (!timeLimit) return void 0;
278
+ const match = timeLimit.match(/([\d.]+)/);
279
+ if (!match) return void 0;
280
+ const seconds = parseFloat(match[1]);
281
+ if (Number.isNaN(seconds)) return void 0;
282
+ return Math.round(seconds * 1e3);
283
+ }
284
+ function getProjectRoot() {
285
+ const __filename = fileURLToPath(import.meta.url);
286
+ const __dirname = dirname(__filename);
287
+ if (__dirname.includes("dist")) {
288
+ return join(__dirname, "../..");
289
+ }
290
+ return join(__dirname, "../..");
291
+ }
292
+ async function generateProblemFiles(problem, language = "python") {
293
+ const problemDir = join(process.cwd(), "problems", problem.id.toString());
294
+ await mkdir(problemDir, { recursive: true });
295
+ const langConfig = getLanguageConfig(language);
296
+ const projectRoot = getProjectRoot();
297
+ const templatePath = join(projectRoot, "templates", langConfig.templateFile);
298
+ const solutionPath = join(problemDir, `solution.${langConfig.extension}`);
299
+ try {
300
+ const templateContent = await readFile(templatePath, "utf-8");
301
+ await writeFile(solutionPath, templateContent, "utf-8");
302
+ } catch (error) {
303
+ await writeFile(
304
+ solutionPath,
305
+ `// Problem ${problem.id}: ${problem.title}
306
+ `,
307
+ "utf-8"
308
+ );
309
+ }
310
+ for (let i = 0; i < problem.testCases.length; i++) {
311
+ const testCase = problem.testCases[i];
312
+ const inputPath = join(problemDir, `input${i + 1}.txt`);
313
+ const outputPath = join(problemDir, `output${i + 1}.txt`);
314
+ await writeFile(inputPath, testCase.input, "utf-8");
315
+ await writeFile(outputPath, testCase.output, "utf-8");
316
+ }
317
+ const tierName = getTierName(problem.level);
318
+ const tierImageUrl = getTierImageUrl(problem.level);
319
+ const tags = problem.tags.length > 0 ? problem.tags.join(", ") : "\uC5C6\uC74C";
320
+ const headers = [];
321
+ const values = [];
322
+ headers.push("\uB09C\uC774\uB3C4");
323
+ values.push(`<img src="${tierImageUrl}" alt="${tierName}" width="20" />`);
324
+ if (problem.timeLimit) {
325
+ headers.push("\uC2DC\uAC04 \uC81C\uD55C");
326
+ values.push(problem.timeLimit);
327
+ }
328
+ if (problem.memoryLimit) {
329
+ headers.push("\uBA54\uBAA8\uB9AC \uC81C\uD55C");
330
+ values.push(problem.memoryLimit);
331
+ }
332
+ if (problem.submissions) {
333
+ headers.push("\uC81C\uCD9C");
334
+ values.push(problem.submissions);
335
+ }
336
+ if (problem.accepted) {
337
+ headers.push("\uC815\uB2F5");
338
+ values.push(problem.accepted);
339
+ }
340
+ if (problem.acceptedUsers) {
341
+ headers.push("\uB9DE\uD78C \uC0AC\uB78C");
342
+ values.push(problem.acceptedUsers);
343
+ }
344
+ if (problem.acceptedRate) {
345
+ headers.push("\uC815\uB2F5 \uBE44\uC728");
346
+ values.push(problem.acceptedRate);
347
+ }
348
+ let infoTable = "";
349
+ if (headers.length > 0) {
350
+ const headerRow = `| ${headers.join(" | ")} |`;
351
+ const separatorRow = `|${headers.map(() => "---").join("|")}|`;
352
+ const valueRow = `| ${values.join(" | ")} |`;
353
+ infoTable = `
354
+ ${headerRow}
355
+ ${separatorRow}
356
+ ${valueRow}
357
+ `;
358
+ }
359
+ const readmeContent = `# [${problem.id}: ${problem.title}](https://www.acmicpc.net/problem/${problem.id})
360
+
361
+ ${infoTable}## \uBB38\uC81C \uC124\uBA85
362
+ ${problem.description || "\uC124\uBA85 \uC5C6\uC74C"}
363
+
364
+ ## \uC785\uB825
365
+ ${problem.inputFormat || "\uC785\uB825 \uD615\uC2DD \uC5C6\uC74C"}
366
+
367
+ ## \uCD9C\uB825
368
+ ${problem.outputFormat || "\uCD9C\uB825 \uD615\uC2DD \uC5C6\uC74C"}
369
+
370
+ ## \uC608\uC81C
371
+ ${problem.testCases.map(
372
+ (tc, i) => `### \uC608\uC81C ${i + 1}
373
+
374
+ **\uC785\uB825:**
375
+ \`\`\`
376
+ ${tc.input.trimEnd()}
377
+ \`\`\`
378
+
379
+ **\uCD9C\uB825:**
380
+ \`\`\`
381
+ ${tc.output.trimEnd()}
382
+ \`\`\`
383
+ `
384
+ ).join("\n")}
385
+
386
+ ## \uD0DC\uADF8
387
+ ${tags}
388
+ `;
389
+ const readmePath = join(problemDir, "README.md");
390
+ await writeFile(readmePath, readmeContent, "utf-8");
391
+ const meta = {
392
+ id: problem.id,
393
+ title: problem.title,
394
+ level: problem.level,
395
+ timeLimit: problem.timeLimit,
396
+ timeLimitMs: parseTimeLimitToMs(problem.timeLimit),
397
+ memoryLimit: problem.memoryLimit
398
+ };
399
+ const metaPath = join(problemDir, "meta.json");
400
+ await writeFile(metaPath, JSON.stringify(meta, null, 2), "utf-8");
401
+ return problemDir;
402
+ }
403
+
404
+ // src/components/problem-dashboard.tsx
405
+ import { Box, Text } from "ink";
406
+ import { jsx, jsxs } from "react/jsx-runtime";
407
+ function ProblemDashboard({ problem }) {
408
+ const tierName = getTierName(problem.level);
409
+ const tierColor = getTierColor(problem.level);
410
+ return /* @__PURE__ */ jsx(
411
+ Box,
412
+ {
413
+ flexDirection: "column",
414
+ borderStyle: "round",
415
+ borderColor: tierColor,
416
+ paddingX: 1,
417
+ alignSelf: "flex-start",
418
+ children: /* @__PURE__ */ jsxs(Text, { bold: true, children: [
419
+ source_default.hex(tierColor)(tierName),
420
+ " ",
421
+ /* @__PURE__ */ jsxs(Text, { color: "white", children: [
422
+ "#",
423
+ problem.id,
424
+ ": ",
425
+ problem.title
426
+ ] })
427
+ ] })
428
+ }
429
+ );
430
+ }
431
+
432
+ // src/commands/fetch.tsx
433
+ import { execaCommand } from "execa";
434
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
435
+ function FetchCommand({
436
+ problemId,
437
+ language = "python",
438
+ onComplete
439
+ }) {
440
+ const [status, setStatus] = useState(
441
+ "loading"
442
+ );
443
+ const [problem, setProblem] = useState(null);
444
+ const [error, setError] = useState(null);
445
+ const [message, setMessage] = useState("\uBB38\uC81C \uC815\uBCF4\uB97C \uAC00\uC838\uC624\uB294 \uC911...");
446
+ useEffect(() => {
447
+ async function fetchProblem() {
448
+ try {
449
+ setMessage("Solved.ac\uC5D0\uC11C \uBB38\uC81C \uC815\uBCF4\uB97C \uAC00\uC838\uC624\uB294 \uC911...");
450
+ const solvedAcData = await getProblem(problemId);
451
+ setMessage("BOJ\uC5D0\uC11C \uBB38\uC81C \uC0C1\uC138 \uC815\uBCF4\uB97C \uAC00\uC838\uC624\uB294 \uC911...");
452
+ const scrapedData = await scrapeProblem(problemId);
453
+ if (!scrapedData.title && !solvedAcData.titleKo) {
454
+ throw new Error(
455
+ `\uBB38\uC81C ${problemId}\uC758 \uC81C\uBAA9\uC744 \uAC00\uC838\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uBB38\uC81C\uAC00 \uC874\uC7AC\uD558\uC9C0 \uC54A\uAC70\uB098 \uC811\uADFC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
456
+ );
457
+ }
458
+ const combinedProblem = {
459
+ id: problemId,
460
+ title: solvedAcData.titleKo || scrapedData.title,
461
+ level: solvedAcData.level,
462
+ tier: getTierName(solvedAcData.level),
463
+ tags: solvedAcData.tags.map(
464
+ (tag) => tag.displayNames.find((d) => d.language === "ko")?.name || tag.displayNames[0]?.name || tag.key
465
+ ),
466
+ timeLimit: scrapedData.timeLimit,
467
+ memoryLimit: scrapedData.memoryLimit,
468
+ submissions: scrapedData.submissions,
469
+ accepted: scrapedData.accepted,
470
+ acceptedUsers: scrapedData.acceptedUsers,
471
+ acceptedRate: scrapedData.acceptedRate,
472
+ description: scrapedData.description,
473
+ inputFormat: scrapedData.inputFormat,
474
+ outputFormat: scrapedData.outputFormat,
475
+ testCases: scrapedData.testCases
476
+ };
477
+ setProblem(combinedProblem);
478
+ setMessage("\uD30C\uC77C\uC744 \uC0DD\uC131\uD558\uB294 \uC911...");
479
+ const problemDir = await generateProblemFiles(
480
+ combinedProblem,
481
+ language
482
+ );
483
+ setStatus("success");
484
+ setMessage(`\u2713 \uBB38\uC81C \uD30C\uC77C\uC774 \uC0DD\uC131\uB418\uC5C8\uC2B5\uB2C8\uB2E4: ${problemDir}`);
485
+ if (getAutoOpenEditor()) {
486
+ try {
487
+ const editor = getEditor();
488
+ await execaCommand(`${editor} ${problemDir}`, {
489
+ shell: true,
490
+ detached: true,
491
+ stdio: "ignore"
492
+ });
493
+ setMessage(
494
+ `\u2713 \uBB38\uC81C \uD30C\uC77C\uC774 \uC0DD\uC131\uB418\uC5C8\uC2B5\uB2C8\uB2E4: ${problemDir}
495
+ \u2713 ${editor}\uB85C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4.`
496
+ );
497
+ } catch (err) {
498
+ console.warn(
499
+ `\uC5D0\uB514\uD130\uB97C \uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${err instanceof Error ? err.message : String(err)}`
500
+ );
501
+ }
502
+ }
503
+ setTimeout(() => {
504
+ onComplete?.();
505
+ }, 2e3);
506
+ } catch (err) {
507
+ setStatus("error");
508
+ setError(err instanceof Error ? err.message : String(err));
509
+ setTimeout(() => {
510
+ onComplete?.();
511
+ }, 2e3);
512
+ }
513
+ }
514
+ fetchProblem();
515
+ }, [problemId, language, onComplete]);
516
+ if (status === "loading") {
517
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
518
+ /* @__PURE__ */ jsx2(LoadingSpinner, { message }),
519
+ problem && /* @__PURE__ */ jsx2(ProblemDashboard, { problem })
520
+ ] });
521
+ }
522
+ if (status === "error") {
523
+ return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: /* @__PURE__ */ jsxs2(Text2, { color: "red", children: [
524
+ "\u2717 \uC624\uB958 \uBC1C\uC0DD: ",
525
+ error
526
+ ] }) });
527
+ }
528
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: "100%", children: [
529
+ problem && /* @__PURE__ */ jsx2(Box2, { alignSelf: "flex-start", children: /* @__PURE__ */ jsx2(ProblemDashboard, { problem }) }),
530
+ /* @__PURE__ */ jsx2(Text2, { color: "green", children: message })
531
+ ] });
532
+ }
533
+ async function fetchCommand(problemId, language) {
534
+ return new Promise((resolve) => {
535
+ const { unmount } = render(
536
+ /* @__PURE__ */ jsx2(
537
+ FetchCommand,
538
+ {
539
+ problemId,
540
+ language,
541
+ onComplete: () => {
542
+ unmount();
543
+ resolve();
544
+ }
545
+ }
546
+ )
547
+ );
548
+ });
549
+ }
550
+ var fetchHelp = `
551
+ \uC0AC\uC6A9\uBC95:
552
+ $ ps fetch <\uBB38\uC81C\uBC88\uD638> [\uC635\uC158]
553
+
554
+ \uC124\uBA85:
555
+ \uBC31\uC900 \uBB38\uC81C\uB97C \uAC00\uC838\uC640\uC11C \uB85C\uCEEC\uC5D0 \uD30C\uC77C\uC744 \uC0DD\uC131\uD569\uB2C8\uB2E4.
556
+ - Solved.ac API\uC640 BOJ \uD06C\uB864\uB9C1\uC744 \uD1B5\uD574 \uBB38\uC81C \uC815\uBCF4 \uC218\uC9D1
557
+ - \uBB38\uC81C \uC124\uBA85, \uC785\uCD9C\uB825 \uD615\uC2DD, \uC608\uC81C \uC785\uCD9C\uB825 \uD30C\uC77C \uC790\uB3D9 \uC0DD\uC131
558
+ - \uC120\uD0DD\uD55C \uC5B8\uC5B4\uC758 \uC194\uB8E8\uC158 \uD15C\uD50C\uB9BF \uD30C\uC77C \uC0DD\uC131
559
+ - README.md\uC5D0 \uBB38\uC81C \uC815\uBCF4, \uD1B5\uACC4, \uD0DC\uADF8 \uB4F1 \uD3EC\uD568
560
+
561
+ \uC635\uC158:
562
+ --language, -l \uC5B8\uC5B4 \uC120\uD0DD (${getSupportedLanguagesString()})
563
+ \uAE30\uBCF8\uAC12: python
564
+
565
+ \uC608\uC81C:
566
+ $ ps fetch 1000
567
+ $ ps fetch 1000 --language python
568
+ $ ps fetch 1000 -l cpp
569
+ `;
570
+ async function fetchExecute(args, flags) {
571
+ if (flags.help) {
572
+ console.log(fetchHelp.trim());
573
+ process.exit(0);
574
+ return;
575
+ }
576
+ const problemId = getProblemId(args);
577
+ if (problemId === null) {
578
+ console.error("\uC624\uB958: \uBB38\uC81C \uBC88\uD638\uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694.");
579
+ console.error(`\uC0AC\uC6A9\uBC95: ps fetch <\uBB38\uC81C\uBC88\uD638> [\uC635\uC158]`);
580
+ console.error(`\uB3C4\uC6C0\uB9D0: ps fetch --help`);
581
+ console.error(
582
+ `\uD78C\uD2B8: problems/{\uBB38\uC81C\uBC88\uD638} \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uC2E4\uD589\uD558\uBA74 \uC790\uB3D9\uC73C\uB85C \uBB38\uC81C \uBC88\uD638\uB97C \uCD94\uB860\uD569\uB2C8\uB2E4.`
583
+ );
584
+ process.exit(1);
585
+ }
586
+ const validLanguages = getSupportedLanguages();
587
+ const language = flags.language;
588
+ if (language && !validLanguages.includes(language)) {
589
+ console.error(
590
+ `\uC624\uB958: \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uC5B8\uC5B4\uC785\uB2C8\uB2E4. (${getSupportedLanguagesString()})`
591
+ );
592
+ process.exit(1);
593
+ }
594
+ await fetchCommand(problemId, language || "python");
595
+ }
596
+ var fetchCommandDef = {
597
+ name: "fetch",
598
+ help: fetchHelp,
599
+ execute: fetchExecute
600
+ };
601
+ var fetch_default = fetchCommandDef;
602
+ export {
603
+ fetch_default as default,
604
+ fetchExecute,
605
+ fetchHelp
606
+ };