@rhseung/ps-cli 1.3.2 → 1.4.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/dist/chunk-7MQMPJ3X.js +88 -0
- package/dist/{chunk-CIG2LEJC.js → chunk-7SVCS23X.js} +392 -108
- package/dist/{chunk-2E4VSP6O.js → chunk-NB4OIWND.js} +139 -139
- package/dist/{chunk-EIFFWFLS.js → chunk-OJZLQ6FK.js} +1 -1
- package/dist/chunk-QGMWUOJ3.js +23 -0
- package/dist/commands/config.js +298 -262
- package/dist/commands/fetch.js +262 -254
- package/dist/commands/init.js +138 -101
- package/dist/commands/open.js +74 -78
- package/dist/commands/run.js +99 -98
- package/dist/commands/stats.js +71 -65
- package/dist/commands/submit.js +202 -421
- package/dist/commands/test.js +203 -203
- package/dist/index.js +7 -6
- package/package.json +16 -6
- package/dist/chunk-FYS2JH42.js +0 -31
- package/dist/chunk-MOPDUECU.js +0 -84
- package/dist/chunk-TQXMB7XV.js +0 -52
package/dist/commands/submit.js
CHANGED
|
@@ -1,475 +1,256 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
getSupportedLanguages,
|
|
6
|
-
getSupportedLanguagesString
|
|
7
|
-
} from "../chunk-TQXMB7XV.js";
|
|
3
|
+
openBrowser
|
|
4
|
+
} from "../chunk-QGMWUOJ3.js";
|
|
8
5
|
import {
|
|
6
|
+
Command,
|
|
7
|
+
CommandBuilder,
|
|
8
|
+
CommandDef,
|
|
9
9
|
detectProblemIdFromPath,
|
|
10
|
-
|
|
11
|
-
getProblemId
|
|
12
|
-
|
|
10
|
+
findSolutionFile,
|
|
11
|
+
getProblemId,
|
|
12
|
+
resolveLanguage,
|
|
13
|
+
resolveProblemContext
|
|
14
|
+
} from "../chunk-7SVCS23X.js";
|
|
13
15
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
} from "../chunk-
|
|
17
|
-
import "../chunk-FYS2JH42.js";
|
|
16
|
+
__decorateClass,
|
|
17
|
+
getSupportedLanguagesString
|
|
18
|
+
} from "../chunk-7MQMPJ3X.js";
|
|
18
19
|
|
|
19
20
|
// src/commands/submit.tsx
|
|
20
|
-
import { useState, useEffect } from "react";
|
|
21
|
-
import { render, Text, Box } from "ink";
|
|
22
21
|
import { Badge, StatusMessage, Alert } from "@inkjs/ui";
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
22
|
+
import { Spinner } from "@inkjs/ui";
|
|
23
|
+
import { Text, Box } from "ink";
|
|
24
|
+
|
|
25
|
+
// src/hooks/use-submit.ts
|
|
25
26
|
import { readFile } from "fs/promises";
|
|
27
|
+
import { useEffect, useState } from "react";
|
|
26
28
|
|
|
27
|
-
// src/
|
|
28
|
-
import
|
|
29
|
-
|
|
30
|
-
async function pollSubmitResult(problemId, submitId, sessionCookie) {
|
|
31
|
-
const maxAttempts = 60;
|
|
32
|
-
const pollInterval = 2e3;
|
|
33
|
-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
34
|
-
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
35
|
-
try {
|
|
36
|
-
const response = await fetch(
|
|
37
|
-
`${BOJ_BASE_URL}/status?from_mine=1&problem_id=${problemId}`,
|
|
38
|
-
{
|
|
39
|
-
headers: {
|
|
40
|
-
Cookie: sessionCookie,
|
|
41
|
-
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
);
|
|
45
|
-
if (!response.ok) {
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
const html = await response.text();
|
|
49
|
-
const $ = cheerio.load(html);
|
|
50
|
-
let targetRow = null;
|
|
51
|
-
$("table#status-table tbody tr").each((_, element) => {
|
|
52
|
-
const row = $(element);
|
|
53
|
-
const rowSubmitId = parseInt(
|
|
54
|
-
row.find("td:first-child").text().trim(),
|
|
55
|
-
10
|
|
56
|
-
);
|
|
57
|
-
if (rowSubmitId === submitId) {
|
|
58
|
-
targetRow = row;
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
if (!targetRow) {
|
|
63
|
-
targetRow = $("table#status-table tbody tr").first();
|
|
64
|
-
}
|
|
65
|
-
if (!targetRow || targetRow.length === 0) {
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
const statusText = targetRow.find("td.result").text().trim();
|
|
69
|
-
const status = parseSubmitStatus(statusText);
|
|
70
|
-
if (status !== "WAITING" && status !== "JUDGING") {
|
|
71
|
-
const timeText = targetRow.find("td.time").text().trim();
|
|
72
|
-
const memoryText = targetRow.find("td.memory").text().trim();
|
|
73
|
-
const languageText = targetRow.find("td.language").text().trim();
|
|
74
|
-
const time = parseTime(timeText);
|
|
75
|
-
const memory = parseMemory(memoryText);
|
|
76
|
-
return {
|
|
77
|
-
problemId,
|
|
78
|
-
submitId: parseInt(
|
|
79
|
-
targetRow.find("td:first-child").text().trim(),
|
|
80
|
-
10
|
|
81
|
-
),
|
|
82
|
-
status,
|
|
83
|
-
time,
|
|
84
|
-
memory,
|
|
85
|
-
submittedAt: /* @__PURE__ */ new Date(),
|
|
86
|
-
language: languageText
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
} catch (error) {
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
throw new Error(
|
|
94
|
-
"\uC81C\uCD9C \uACB0\uACFC \uD655\uC778 \uC2DC\uAC04\uC774 \uCD08\uACFC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. BOJ \uC6F9\uC0AC\uC774\uD2B8\uC5D0\uC11C \uC9C1\uC811 \uD655\uC778\uD574\uC8FC\uC138\uC694."
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
function parseSubmitStatus(statusText) {
|
|
98
|
-
const upper = statusText.toUpperCase();
|
|
99
|
-
if (upper.includes("ACCEPTED") || upper.includes("\uB9DE\uC558\uC2B5\uB2C8\uB2E4")) {
|
|
100
|
-
return "AC";
|
|
101
|
-
}
|
|
102
|
-
if (upper.includes("WRONG ANSWER") || upper.includes("\uD2C0\uB838\uC2B5\uB2C8\uB2E4")) {
|
|
103
|
-
return "WA";
|
|
104
|
-
}
|
|
105
|
-
if (upper.includes("TIME LIMIT") || upper.includes("\uC2DC\uAC04 \uCD08\uACFC")) {
|
|
106
|
-
return "TLE";
|
|
107
|
-
}
|
|
108
|
-
if (upper.includes("MEMORY LIMIT") || upper.includes("\uBA54\uBAA8\uB9AC \uCD08\uACFC")) {
|
|
109
|
-
return "MLE";
|
|
110
|
-
}
|
|
111
|
-
if (upper.includes("RUNTIME ERROR") || upper.includes("\uB7F0\uD0C0\uC784 \uC5D0\uB7EC")) {
|
|
112
|
-
return "RE";
|
|
113
|
-
}
|
|
114
|
-
if (upper.includes("COMPILE ERROR") || upper.includes("\uCEF4\uD30C\uC77C \uC5D0\uB7EC")) {
|
|
115
|
-
return "CE";
|
|
116
|
-
}
|
|
117
|
-
if (upper.includes("OUTPUT LIMIT") || upper.includes("\uCD9C\uB825 \uCD08\uACFC")) {
|
|
118
|
-
return "OLE";
|
|
119
|
-
}
|
|
120
|
-
if (upper.includes("PRESENTATION ERROR") || upper.includes("\uCD9C\uB825 \uD615\uC2DD")) {
|
|
121
|
-
return "PE";
|
|
122
|
-
}
|
|
123
|
-
if (upper.includes("WAITING") || upper.includes("\uAE30\uB2E4\uB9AC\uB294 \uC911")) {
|
|
124
|
-
return "WAITING";
|
|
125
|
-
}
|
|
126
|
-
if (upper.includes("JUDGING") || upper.includes("\uCC44\uC810 \uC911")) {
|
|
127
|
-
return "JUDGING";
|
|
128
|
-
}
|
|
129
|
-
return "WAITING";
|
|
130
|
-
}
|
|
131
|
-
function parseTime(timeText) {
|
|
132
|
-
const match = timeText.match(/(\d+)\s*ms/);
|
|
133
|
-
if (match) {
|
|
134
|
-
return parseInt(match[1], 10);
|
|
135
|
-
}
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
function parseMemory(memoryText) {
|
|
139
|
-
const match = memoryText.match(/(\d+)\s*KB/);
|
|
140
|
-
if (match) {
|
|
141
|
-
return parseInt(match[1], 10);
|
|
142
|
-
}
|
|
143
|
-
return null;
|
|
144
|
-
}
|
|
145
|
-
async function getCsrfToken(problemId, sessionCookie) {
|
|
146
|
-
const response = await fetch(`${BOJ_BASE_URL}/submit/${problemId}`, {
|
|
147
|
-
headers: {
|
|
148
|
-
Cookie: sessionCookie,
|
|
149
|
-
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
if (!response.ok) {
|
|
153
|
-
throw new Error(
|
|
154
|
-
`\uC81C\uCD9C \uD398\uC774\uC9C0\uB97C \uBD88\uB7EC\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. (HTTP ${response.status})`
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
|
-
const html = await response.text();
|
|
158
|
-
const $ = cheerio.load(html);
|
|
159
|
-
let csrfToken;
|
|
160
|
-
csrfToken = $('input[name="csrf_key"]').val()?.toString() || $('input[name="_csrf"]').val()?.toString() || $('input[name="csrf"]').val()?.toString() || $('input[type="hidden"][name*="csrf"]').val()?.toString();
|
|
161
|
-
if (!csrfToken) {
|
|
162
|
-
csrfToken = $('meta[name="csrf-token"]').attr("content") || $('meta[name="_csrf"]').attr("content") || $('meta[name="csrf"]').attr("content");
|
|
163
|
-
}
|
|
164
|
-
if (!csrfToken) {
|
|
165
|
-
const scriptTags = $("script").toArray();
|
|
166
|
-
for (const script of scriptTags) {
|
|
167
|
-
const scriptContent = $(script).html() || "";
|
|
168
|
-
const match = scriptContent.match(/csrf[_-]?key["\s:=]+["']([^"']+)["']/) || scriptContent.match(/csrf[_-]?token["\s:=]+["']([^"']+)["']/) || scriptContent.match(/window\.csrf[_-]?key\s*=\s*["']([^"']+)["']/);
|
|
169
|
-
if (match && match[1]) {
|
|
170
|
-
csrfToken = match[1];
|
|
171
|
-
break;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
if (!csrfToken) {
|
|
176
|
-
const formAction = $('form[action*="submit"]').attr("action");
|
|
177
|
-
if (formAction) {
|
|
178
|
-
const match = formAction.match(/csrf[_-]?key=([^&]+)/);
|
|
179
|
-
if (match && match[1]) {
|
|
180
|
-
csrfToken = decodeURIComponent(match[1]);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
if (!csrfToken) {
|
|
185
|
-
const formHtml = $("form").html()?.substring(0, 500) || "";
|
|
186
|
-
throw new Error(
|
|
187
|
-
"CSRF \uD1A0\uD070\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. BOJ \uD398\uC774\uC9C0 \uAD6C\uC870\uAC00 \uBCC0\uACBD\uB418\uC5C8\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.\n\uB2E4\uC74C \uC704\uCE58\uB4E4\uC744 \uD655\uC778\uD588\uC2B5\uB2C8\uB2E4:\n- input[name='csrf_key'], input[name='_csrf'], input[name='csrf']\n- meta[name='csrf-token'], meta[name='_csrf']\n- JavaScript \uBCC0\uC218 (csrf_key, csrf_token)\n- \uD3FC action URL\n\n\uC81C\uCD9C \uD398\uC774\uC9C0 HTML \uAD6C\uC870\uB97C \uD655\uC778\uD574\uC8FC\uC138\uC694."
|
|
188
|
-
);
|
|
189
|
-
}
|
|
190
|
-
return String(csrfToken);
|
|
191
|
-
}
|
|
192
|
-
async function submitSolution({
|
|
193
|
-
problemId,
|
|
194
|
-
language,
|
|
195
|
-
sourceCode,
|
|
196
|
-
dryRun = false
|
|
197
|
-
}) {
|
|
198
|
-
if (dryRun) {
|
|
199
|
-
const langConfig2 = getLanguageConfig(language);
|
|
200
|
-
if (!langConfig2.bojLangId) {
|
|
201
|
-
throw new Error(
|
|
202
|
-
`\uC5B8\uC5B4 ${language}\uC5D0 \uB300\uD55C BOJ \uC5B8\uC5B4 ID\uAC00 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`
|
|
203
|
-
);
|
|
204
|
-
}
|
|
205
|
-
return {
|
|
206
|
-
problemId,
|
|
207
|
-
status: "WAITING",
|
|
208
|
-
language,
|
|
209
|
-
message: `[DRY RUN] \uBB38\uC81C ${problemId}, \uC5B8\uC5B4: ${language} (BOJ ID: ${langConfig2.bojLangId}), \uCF54\uB4DC \uAE38\uC774: ${sourceCode.length}\uC790`
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
const sessionCookie = getBojSessionCookie();
|
|
213
|
-
if (!sessionCookie) {
|
|
214
|
-
throw new Error(
|
|
215
|
-
"BOJ \uC138\uC158 \uCFE0\uD0A4\uAC00 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.\n\n\u{1F4CB} \uCFE0\uD0A4 \uBCF5\uC0AC \uBC29\uBC95:\n1. \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C https://www.acmicpc.net \uC5D0 \uB85C\uADF8\uC778\n2. \uAC1C\uBC1C\uC790 \uB3C4\uAD6C(F12) \uC5F4\uAE30\n3. \uBC29\uBC95 A - Network \uD0ED \uC0AC\uC6A9 (\uCD94\uCC9C):\n - Network \uD0ED \uC5F4\uAE30 \u2192 \uD398\uC774\uC9C0 \uC0C8\uB85C\uACE0\uCE68(F5)\n - \uC544\uBB34 \uC694\uCCAD \uD074\uB9AD \u2192 Headers \uD0ED\n - Request Headers \uC139\uC158\uC5D0\uC11C 'Cookie:' \uAC12\uC744 \uBCF5\uC0AC\n (\uC804\uCCB4 Cookie \uD5E4\uB354 \uAC12 \uBCF5\uC0AC, \uC608: 'OnlineJudge=xxx; __ga=yyy; ...')\n\n4. \uBC29\uBC95 B - Application \uD0ED \uC0AC\uC6A9:\n - Application/\uC800\uC7A5\uC18C \uD0ED \u2192 Cookies \u2192 https://www.acmicpc.net\n - 'OnlineJudge' \uCFE0\uD0A4\uC758 Name\uACFC Value\uB97C \uBCF5\uC0AC\n - \uD615\uC2DD: 'OnlineJudge=\uAC12'\n\n\u{1F4A1} \uD301: Network \uD0ED\uC5D0\uC11C \uBCF5\uC0AC\uD558\uB294 \uAC83\uC774 \uAC00\uC7A5 \uC815\uD655\uD569\uB2C8\uB2E4!\n\n\u2699\uFE0F \uC124\uC815 \uBC29\uBC95:\n export PS_CLI_BOJ_COOKIE='\uBCF5\uC0AC\uD55C_\uCFE0\uD0A4_\uAC12'\n\n\uC608\uC2DC:\n export PS_CLI_BOJ_COOKIE='OnlineJudge=abc123; __ga=xyz789; ...'"
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
const langConfig = getLanguageConfig(language);
|
|
219
|
-
if (!langConfig.bojLangId) {
|
|
220
|
-
throw new Error(
|
|
221
|
-
`\uC5B8\uC5B4 ${language}\uC5D0 \uB300\uD55C BOJ \uC5B8\uC5B4 ID\uAC00 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
let csrfToken;
|
|
29
|
+
// src/utils/clipboard.ts
|
|
30
|
+
import { execa, execaCommand } from "execa";
|
|
31
|
+
async function copyToClipboard(text) {
|
|
225
32
|
try {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
body: formData.toString(),
|
|
249
|
-
redirect: "manual"
|
|
250
|
-
// 리다이렉트를 수동으로 처리
|
|
251
|
-
});
|
|
252
|
-
const responseText = await submitResponse.text();
|
|
253
|
-
const $response = cheerio.load(responseText);
|
|
254
|
-
const errorMessage = $response(".alert-danger, .error, .warning").text().trim();
|
|
255
|
-
if (!submitResponse.ok) {
|
|
256
|
-
if (submitResponse.status === 401 || submitResponse.status === 403) {
|
|
257
|
-
throw new Error(
|
|
258
|
-
"\uB85C\uADF8\uC778\uC774 \uD544\uC694\uD558\uAC70\uB098 \uC138\uC158\uC774 \uB9CC\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uCFE0\uD0A4\uB97C \uB2E4\uC2DC \uC124\uC815\uD574\uC8FC\uC138\uC694."
|
|
259
|
-
);
|
|
260
|
-
}
|
|
261
|
-
const error = errorMessage || `HTTP ${submitResponse.status} ${submitResponse.statusText}`;
|
|
262
|
-
throw new Error(`\uC81C\uCD9C \uC2E4\uD328: ${error}`);
|
|
263
|
-
}
|
|
264
|
-
const location = submitResponse.headers.get("location");
|
|
265
|
-
let submitId;
|
|
266
|
-
if (location) {
|
|
267
|
-
const match = location.match(/\/status\/(\d+)/);
|
|
268
|
-
if (match) {
|
|
269
|
-
submitId = parseInt(match[1], 10);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
if (!submitId) {
|
|
273
|
-
if (errorMessage) {
|
|
274
|
-
throw new Error(`\uC81C\uCD9C \uC2E4\uD328: ${errorMessage}`);
|
|
275
|
-
}
|
|
276
|
-
const statusResponse = await fetch(
|
|
277
|
-
`${BOJ_BASE_URL}/status?from_mine=1&problem_id=${problemId}`,
|
|
278
|
-
{
|
|
279
|
-
headers: {
|
|
280
|
-
Cookie: sessionCookie,
|
|
281
|
-
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
|
33
|
+
if (process.platform === "win32") {
|
|
34
|
+
await execaCommand("clip", {
|
|
35
|
+
shell: true,
|
|
36
|
+
input: text
|
|
37
|
+
});
|
|
38
|
+
} else if (process.platform === "darwin") {
|
|
39
|
+
await execaCommand("pbcopy", {
|
|
40
|
+
shell: false,
|
|
41
|
+
input: text
|
|
42
|
+
});
|
|
43
|
+
} else {
|
|
44
|
+
try {
|
|
45
|
+
await execa("xclip", ["-selection", "clipboard"], {
|
|
46
|
+
input: text
|
|
47
|
+
});
|
|
48
|
+
} catch {
|
|
49
|
+
try {
|
|
50
|
+
await execa("xsel", ["--clipboard", "--input"], {
|
|
51
|
+
input: text
|
|
52
|
+
});
|
|
53
|
+
} catch {
|
|
54
|
+
return false;
|
|
282
55
|
}
|
|
283
56
|
}
|
|
284
|
-
);
|
|
285
|
-
if (statusResponse.ok) {
|
|
286
|
-
const html = await statusResponse.text();
|
|
287
|
-
const $ = cheerio.load(html);
|
|
288
|
-
const firstRow = $("table#status-table tbody tr").first();
|
|
289
|
-
if (firstRow.length > 0) {
|
|
290
|
-
const idText = firstRow.find("td:first-child").text().trim();
|
|
291
|
-
submitId = parseInt(idText, 10);
|
|
292
|
-
}
|
|
293
57
|
}
|
|
58
|
+
return true;
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
294
61
|
}
|
|
295
|
-
if (!submitId) {
|
|
296
|
-
throw new Error(
|
|
297
|
-
"\uC81C\uCD9C\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. \uC81C\uCD9C ID\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.\n\uAC00\uB2A5\uD55C \uC6D0\uC778:\n- CSRF \uD1A0\uD070\uC774 \uD544\uC694\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4\n- \uB85C\uADF8\uC778 \uC138\uC158\uC774 \uB9CC\uB8CC\uB418\uC5C8\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4\n- BOJ \uD398\uC774\uC9C0 \uAD6C\uC870\uAC00 \uBCC0\uACBD\uB418\uC5C8\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4\n\n\uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uC9C1\uC811 \uC81C\uCD9C\uD574\uBCF4\uC2DC\uACE0, \uBB38\uC81C\uAC00 \uACC4\uC18D\uB418\uBA74 \uC774\uC288\uB97C \uB4F1\uB85D\uD574\uC8FC\uC138\uC694."
|
|
298
|
-
);
|
|
299
|
-
}
|
|
300
|
-
const result = await pollSubmitResult(problemId, submitId, sessionCookie);
|
|
301
|
-
return {
|
|
302
|
-
...result,
|
|
303
|
-
problemId,
|
|
304
|
-
submitId
|
|
305
|
-
};
|
|
306
62
|
}
|
|
307
63
|
|
|
308
|
-
// src/
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
function SubmitCommand({
|
|
64
|
+
// src/hooks/use-submit.ts
|
|
65
|
+
var BOJ_BASE_URL = "https://www.acmicpc.net";
|
|
66
|
+
function useSubmit({
|
|
312
67
|
problemId,
|
|
313
|
-
language,
|
|
68
|
+
language: _language,
|
|
314
69
|
sourcePath,
|
|
315
|
-
dryRun,
|
|
316
70
|
onComplete
|
|
317
71
|
}) {
|
|
318
72
|
const [status, setStatus] = useState(
|
|
319
73
|
"loading"
|
|
320
74
|
);
|
|
321
75
|
const [message, setMessage] = useState("\uC81C\uCD9C \uC900\uBE44 \uC911...");
|
|
322
|
-
const [result, setResult] = useState(null);
|
|
323
76
|
const [error, setError] = useState(null);
|
|
77
|
+
const [submitUrl, setSubmitUrl] = useState("");
|
|
78
|
+
const [clipboardSuccess, setClipboardSuccess] = useState(false);
|
|
79
|
+
const [clipboardError, setClipboardError] = useState(null);
|
|
324
80
|
useEffect(() => {
|
|
325
81
|
async function submit() {
|
|
326
82
|
try {
|
|
327
|
-
setMessage("\uCF54\uB4DC\uB97C \uC77D\uB294 \uC911...");
|
|
83
|
+
setMessage("\uC18C\uC2A4 \uCF54\uB4DC\uB97C \uC77D\uB294 \uC911...");
|
|
328
84
|
const sourceCode = await readFile(sourcePath, "utf-8");
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
85
|
+
setMessage("\uD074\uB9BD\uBCF4\uB4DC\uC5D0 \uBCF5\uC0AC\uD558\uB294 \uC911...");
|
|
86
|
+
const clipboardResult = await copyToClipboard(sourceCode);
|
|
87
|
+
setClipboardSuccess(clipboardResult);
|
|
88
|
+
if (!clipboardResult) {
|
|
89
|
+
setClipboardError("\uD074\uB9BD\uBCF4\uB4DC \uBCF5\uC0AC\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
|
|
333
90
|
}
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
dryRun
|
|
339
|
-
});
|
|
340
|
-
setResult(submitResult);
|
|
91
|
+
const url = `${BOJ_BASE_URL}/submit/${problemId}`;
|
|
92
|
+
setSubmitUrl(url);
|
|
93
|
+
setMessage("\uBE0C\uB77C\uC6B0\uC800\uB97C \uC5EC\uB294 \uC911...");
|
|
94
|
+
await openBrowser(url);
|
|
341
95
|
setStatus("success");
|
|
342
|
-
setMessage("\uC81C\uCD9C \uC644\uB8CC");
|
|
343
96
|
setTimeout(() => {
|
|
344
97
|
onComplete();
|
|
345
|
-
},
|
|
98
|
+
}, 2e3);
|
|
346
99
|
} catch (err) {
|
|
347
100
|
setStatus("error");
|
|
348
101
|
setError(err instanceof Error ? err.message : String(err));
|
|
349
102
|
setTimeout(() => {
|
|
350
103
|
onComplete();
|
|
351
|
-
},
|
|
104
|
+
}, 2e3);
|
|
352
105
|
}
|
|
353
106
|
}
|
|
354
|
-
submit();
|
|
355
|
-
}, [problemId,
|
|
107
|
+
void submit();
|
|
108
|
+
}, [problemId, sourcePath, onComplete]);
|
|
109
|
+
return {
|
|
110
|
+
status,
|
|
111
|
+
message,
|
|
112
|
+
error,
|
|
113
|
+
submitUrl,
|
|
114
|
+
clipboardSuccess,
|
|
115
|
+
clipboardError
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/commands/submit.tsx
|
|
120
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
121
|
+
function SubmitView({
|
|
122
|
+
problemId,
|
|
123
|
+
language,
|
|
124
|
+
sourcePath,
|
|
125
|
+
onComplete
|
|
126
|
+
}) {
|
|
127
|
+
const {
|
|
128
|
+
status,
|
|
129
|
+
message,
|
|
130
|
+
error,
|
|
131
|
+
submitUrl,
|
|
132
|
+
clipboardSuccess,
|
|
133
|
+
clipboardError
|
|
134
|
+
} = useSubmit({
|
|
135
|
+
problemId,
|
|
136
|
+
language,
|
|
137
|
+
sourcePath,
|
|
138
|
+
onComplete
|
|
139
|
+
});
|
|
356
140
|
if (status === "loading") {
|
|
357
|
-
return /* @__PURE__ */
|
|
141
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
142
|
+
/* @__PURE__ */ jsx(Spinner, { label: message }),
|
|
143
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
144
|
+
"\uBB38\uC81C #",
|
|
145
|
+
problemId
|
|
146
|
+
] }) })
|
|
147
|
+
] });
|
|
358
148
|
}
|
|
359
149
|
if (status === "error") {
|
|
360
|
-
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsxs(Alert, { variant: "error", children: [
|
|
361
|
-
"\uC81C\uCD9C \uC2E4\uD328: ",
|
|
362
|
-
error
|
|
363
|
-
] }) });
|
|
364
|
-
}
|
|
365
|
-
if (result) {
|
|
366
|
-
const badgeColor = result.status === "AC" ? "green" : result.status === "WA" || result.status === "CE" || result.status === "RE" ? "red" : result.status === "TLE" || result.status === "MLE" ? "yellow" : "blue";
|
|
367
|
-
const statusVariant = result.status === "AC" ? "success" : result.status === "WA" || result.status === "CE" || result.status === "RE" ? "error" : result.status === "TLE" || result.status === "MLE" ? "warning" : "info";
|
|
368
|
-
const resultDetails = [
|
|
369
|
-
`\uBB38\uC81C: ${result.problemId}`,
|
|
370
|
-
`\uC5B8\uC5B4: ${result.language}`,
|
|
371
|
-
result.time !== null && result.time !== void 0 ? `\uC2DC\uAC04: ${result.time}ms` : null,
|
|
372
|
-
result.memory !== null && result.memory !== void 0 ? `\uBA54\uBAA8\uB9AC: ${result.memory}KB` : null
|
|
373
|
-
].filter(Boolean).join(" | ");
|
|
374
150
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
375
|
-
/* @__PURE__ */
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
151
|
+
/* @__PURE__ */ jsxs(Alert, { variant: "error", children: [
|
|
152
|
+
"\uC624\uB958 \uBC1C\uC0DD: ",
|
|
153
|
+
error
|
|
154
|
+
] }),
|
|
155
|
+
submitUrl && /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
156
|
+
"URL: ",
|
|
157
|
+
submitUrl
|
|
158
|
+
] }) })
|
|
381
159
|
] });
|
|
382
160
|
}
|
|
383
|
-
return
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
});
|
|
161
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: "100%", children: [
|
|
162
|
+
/* @__PURE__ */ jsx(StatusMessage, { variant: "success", children: "\uC81C\uCD9C \uD398\uC774\uC9C0\uB97C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4!" }),
|
|
163
|
+
/* @__PURE__ */ jsxs(
|
|
164
|
+
Box,
|
|
165
|
+
{
|
|
166
|
+
flexDirection: "column",
|
|
167
|
+
borderStyle: "round",
|
|
168
|
+
borderColor: "gray",
|
|
169
|
+
marginTop: 1,
|
|
170
|
+
paddingX: 1,
|
|
171
|
+
paddingY: 0,
|
|
172
|
+
alignSelf: "flex-start",
|
|
173
|
+
children: [
|
|
174
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
175
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\uBB38\uC81C \uBC88\uD638:" }),
|
|
176
|
+
" ",
|
|
177
|
+
problemId
|
|
178
|
+
] }),
|
|
179
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
180
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\uC5B8\uC5B4:" }),
|
|
181
|
+
" ",
|
|
182
|
+
/* @__PURE__ */ jsx(Text, { color: "gray", children: language })
|
|
183
|
+
] }),
|
|
184
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
185
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "URL:" }),
|
|
186
|
+
" ",
|
|
187
|
+
/* @__PURE__ */ jsx(Text, { color: "blue", underline: true, children: submitUrl })
|
|
188
|
+
] }),
|
|
189
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: clipboardSuccess ? /* @__PURE__ */ jsx(Badge, { color: "green", children: "\uD074\uB9BD\uBCF4\uB4DC\uC5D0 \uBCF5\uC0AC\uB428" }) : /* @__PURE__ */ jsx(Badge, { color: "yellow", children: "\uD074\uB9BD\uBCF4\uB4DC \uBCF5\uC0AC \uC2E4\uD328" }) }),
|
|
190
|
+
clipboardError && !clipboardSuccess && /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Alert, { variant: "warning", children: clipboardError }) })
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
)
|
|
194
|
+
] });
|
|
418
195
|
}
|
|
419
|
-
var
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
- \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC \uB610\uB294 \uC9C0\uC815\uD55C \uBB38\uC81C \uBC88\uD638\uC758 \uC194\uB8E8\uC158 \uD30C\uC77C \uC81C\uCD9C
|
|
426
|
-
- solution.* \uD30C\uC77C\uC744 \uC790\uB3D9\uC73C\uB85C \uCC3E\uC544 \uC5B8\uC5B4 \uAC10\uC9C0
|
|
427
|
-
- \uC81C\uCD9C \uD6C4 \uCC44\uC810 \uACB0\uACFC\uB97C \uC790\uB3D9\uC73C\uB85C \uD655\uC778
|
|
428
|
-
|
|
429
|
-
\uC635\uC158:
|
|
430
|
-
--language, -l \uC5B8\uC5B4 \uC120\uD0DD (\uC9C0\uC815 \uC2DC \uC790\uB3D9 \uAC10\uC9C0 \uBB34\uC2DC)
|
|
431
|
-
\uC9C0\uC6D0 \uC5B8\uC5B4: ${getSupportedLanguagesString()}
|
|
432
|
-
--dry-run \uC2E4\uC81C \uC81C\uCD9C \uC5C6\uC774 \uAC80\uC99D\uB9CC \uC218\uD589
|
|
433
|
-
|
|
434
|
-
\uC608\uC81C:
|
|
435
|
-
$ ps submit # \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uC81C\uCD9C
|
|
436
|
-
$ ps submit 1000 # 1000\uBC88 \uBB38\uC81C \uC81C\uCD9C
|
|
437
|
-
$ ps submit --language python # Python\uC73C\uB85C \uC81C\uCD9C
|
|
438
|
-
`;
|
|
439
|
-
async function submitExecute(args, flags) {
|
|
440
|
-
if (flags.help) {
|
|
441
|
-
console.log(submitHelp.trim());
|
|
442
|
-
process.exit(0);
|
|
443
|
-
return;
|
|
444
|
-
}
|
|
445
|
-
const problemId = getProblemId(args);
|
|
446
|
-
if (problemId === null) {
|
|
447
|
-
console.error("\uC624\uB958: \uBB38\uC81C \uBC88\uD638\uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694.");
|
|
448
|
-
console.error(`\uC0AC\uC6A9\uBC95: ps submit [\uBB38\uC81C\uBC88\uD638] [\uC635\uC158]`);
|
|
449
|
-
console.error(`\uB3C4\uC6C0\uB9D0: ps submit --help`);
|
|
450
|
-
console.error(
|
|
451
|
-
`\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.`
|
|
196
|
+
var SubmitCommand = class extends Command {
|
|
197
|
+
async execute(args, flags) {
|
|
198
|
+
const problemId = getProblemId(args);
|
|
199
|
+
const context = await resolveProblemContext(
|
|
200
|
+
problemId !== null ? [problemId.toString()] : [],
|
|
201
|
+
{ requireId: true }
|
|
452
202
|
);
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
if (flags.language && language && !validLanguages.includes(language)) {
|
|
458
|
-
console.error(
|
|
459
|
-
`\uC624\uB958: \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uC5B8\uC5B4\uC785\uB2C8\uB2E4. (${getSupportedLanguagesString()})`
|
|
203
|
+
const sourcePath = await findSolutionFile(context.problemDir);
|
|
204
|
+
const detectedLanguage = await resolveLanguage(
|
|
205
|
+
context.problemDir,
|
|
206
|
+
flags.language
|
|
460
207
|
);
|
|
461
|
-
|
|
208
|
+
let finalProblemId = context.problemId;
|
|
209
|
+
if (finalProblemId === null) {
|
|
210
|
+
finalProblemId = detectProblemIdFromPath(context.problemDir);
|
|
211
|
+
}
|
|
212
|
+
if (finalProblemId === null) {
|
|
213
|
+
throw new Error(
|
|
214
|
+
"\uBB38\uC81C \uBC88\uD638\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uBB38\uC81C \uBC88\uD638\uB97C \uC778\uC790\uB85C \uC804\uB2EC\uD558\uAC70\uB098 \uBB38\uC81C \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uC2E4\uD589\uD574\uC8FC\uC138\uC694."
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
await this.renderView(SubmitView, {
|
|
218
|
+
problemId: finalProblemId,
|
|
219
|
+
language: detectedLanguage,
|
|
220
|
+
sourcePath
|
|
221
|
+
});
|
|
462
222
|
}
|
|
463
|
-
await submitCommand(problemId, language, Boolean(flags["dry-run"]));
|
|
464
|
-
}
|
|
465
|
-
var submitCommandDef = {
|
|
466
|
-
name: "submit",
|
|
467
|
-
help: submitHelp,
|
|
468
|
-
execute: submitExecute
|
|
469
223
|
};
|
|
470
|
-
|
|
224
|
+
SubmitCommand = __decorateClass([
|
|
225
|
+
CommandDef({
|
|
226
|
+
name: "submit",
|
|
227
|
+
description: `\uBC31\uC900 \uC81C\uCD9C \uD398\uC774\uC9C0\uB97C \uBE0C\uB77C\uC6B0\uC800\uB85C \uC5F4\uACE0 \uC18C\uC2A4 \uCF54\uB4DC\uB97C \uD074\uB9BD\uBCF4\uB4DC\uC5D0 \uBCF5\uC0AC\uD569\uB2C8\uB2E4.
|
|
228
|
+
- \uBB38\uC81C \uBC88\uD638\uB97C \uC778\uC790\uB85C \uC804\uB2EC\uD558\uAC70\uB098 \uBB38\uC81C \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uC2E4\uD589\uD558\uBA74 \uC790\uB3D9\uC73C\uB85C \uBB38\uC81C \uBC88\uD638\uB97C \uCD94\uB860
|
|
229
|
+
- solution.* \uD30C\uC77C\uC744 \uC790\uB3D9\uC73C\uB85C \uCC3E\uC544 \uC5B8\uC5B4 \uAC10\uC9C0
|
|
230
|
+
- \uC18C\uC2A4 \uCF54\uB4DC\uB97C \uD074\uB9BD\uBCF4\uB4DC\uC5D0 \uC790\uB3D9 \uBCF5\uC0AC
|
|
231
|
+
- \uC81C\uCD9C \uD398\uC774\uC9C0\uB97C \uBE0C\uB77C\uC6B0\uC800\uB85C \uC790\uB3D9 \uC5F4\uAE30`,
|
|
232
|
+
flags: [
|
|
233
|
+
{
|
|
234
|
+
name: "language",
|
|
235
|
+
options: {
|
|
236
|
+
shortFlag: "l",
|
|
237
|
+
description: `\uC5B8\uC5B4 \uC120\uD0DD (\uC9C0\uC815 \uC2DC \uC790\uB3D9 \uAC10\uC9C0 \uBB34\uC2DC)
|
|
238
|
+
\uC9C0\uC6D0 \uC5B8\uC5B4: ${getSupportedLanguagesString()}`
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
],
|
|
242
|
+
autoDetectProblemId: true,
|
|
243
|
+
autoDetectLanguage: true,
|
|
244
|
+
requireProblemId: true,
|
|
245
|
+
examples: [
|
|
246
|
+
"submit # \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uC81C\uCD9C",
|
|
247
|
+
"submit 1000 # 1000\uBC88 \uBB38\uC81C \uC81C\uCD9C",
|
|
248
|
+
"submit --language python # Python\uC73C\uB85C \uC81C\uCD9C"
|
|
249
|
+
]
|
|
250
|
+
})
|
|
251
|
+
], SubmitCommand);
|
|
252
|
+
var submit_default = CommandBuilder.fromClass(SubmitCommand);
|
|
471
253
|
export {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
submitHelp
|
|
254
|
+
SubmitCommand,
|
|
255
|
+
submit_default as default
|
|
475
256
|
};
|