@rhseung/ps-cli 1.3.2 → 1.3.3
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-MOPDUECU.js → chunk-6ENX5K3C.js} +1 -1
- package/dist/{chunk-CIG2LEJC.js → chunk-PNIGP6LX.js} +0 -12
- package/dist/commands/config.js +1 -1
- package/dist/commands/fetch.js +2 -2
- package/dist/commands/init.js +1 -1
- package/dist/commands/open.js +2 -2
- package/dist/commands/run.js +2 -2
- package/dist/commands/stats.js +1 -1
- package/dist/commands/submit.js +131 -336
- package/dist/commands/test.js +2 -2
- package/package.json +1 -1
|
@@ -9924,13 +9924,6 @@ function getProjectConfigSync() {
|
|
|
9924
9924
|
return null;
|
|
9925
9925
|
}
|
|
9926
9926
|
}
|
|
9927
|
-
function getBojSessionCookie() {
|
|
9928
|
-
const envCookie = process.env.PS_CLI_BOJ_COOKIE;
|
|
9929
|
-
if (envCookie) {
|
|
9930
|
-
return envCookie;
|
|
9931
|
-
}
|
|
9932
|
-
return config.get("bojSessionCookie");
|
|
9933
|
-
}
|
|
9934
9927
|
function getDefaultLanguage() {
|
|
9935
9928
|
const projectConfig = getProjectConfigSync();
|
|
9936
9929
|
if (projectConfig?.defaultLanguage) {
|
|
@@ -9938,9 +9931,6 @@ function getDefaultLanguage() {
|
|
|
9938
9931
|
}
|
|
9939
9932
|
return config.get("defaultLanguage") ?? "python";
|
|
9940
9933
|
}
|
|
9941
|
-
function getCodeOpen() {
|
|
9942
|
-
return config.get("codeOpen") ?? false;
|
|
9943
|
-
}
|
|
9944
9934
|
function getEditor() {
|
|
9945
9935
|
const projectConfig = getProjectConfigSync();
|
|
9946
9936
|
if (projectConfig?.editor) {
|
|
@@ -9971,9 +9961,7 @@ function getProblemDir() {
|
|
|
9971
9961
|
}
|
|
9972
9962
|
|
|
9973
9963
|
export {
|
|
9974
|
-
getBojSessionCookie,
|
|
9975
9964
|
getDefaultLanguage,
|
|
9976
|
-
getCodeOpen,
|
|
9977
9965
|
getEditor,
|
|
9978
9966
|
getAutoOpenEditor,
|
|
9979
9967
|
getSolvedAcHandle,
|
package/dist/commands/config.js
CHANGED
package/dist/commands/fetch.js
CHANGED
|
@@ -14,11 +14,11 @@ import {
|
|
|
14
14
|
import {
|
|
15
15
|
getProblemDirPath,
|
|
16
16
|
getProblemId
|
|
17
|
-
} from "../chunk-
|
|
17
|
+
} from "../chunk-6ENX5K3C.js";
|
|
18
18
|
import {
|
|
19
19
|
getAutoOpenEditor,
|
|
20
20
|
getEditor
|
|
21
|
-
} from "../chunk-
|
|
21
|
+
} from "../chunk-PNIGP6LX.js";
|
|
22
22
|
import "../chunk-FYS2JH42.js";
|
|
23
23
|
|
|
24
24
|
// src/commands/fetch.tsx
|
package/dist/commands/init.js
CHANGED
package/dist/commands/open.js
CHANGED
package/dist/commands/run.js
CHANGED
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
detectProblemIdFromPath,
|
|
12
12
|
getProblemDirPath,
|
|
13
13
|
getProblemId
|
|
14
|
-
} from "../chunk-
|
|
15
|
-
import "../chunk-
|
|
14
|
+
} from "../chunk-6ENX5K3C.js";
|
|
15
|
+
import "../chunk-PNIGP6LX.js";
|
|
16
16
|
import "../chunk-FYS2JH42.js";
|
|
17
17
|
|
|
18
18
|
// src/commands/run.tsx
|
package/dist/commands/stats.js
CHANGED
package/dist/commands/submit.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
detectLanguageFromFile,
|
|
4
|
-
getLanguageConfig,
|
|
5
4
|
getSupportedLanguages,
|
|
6
5
|
getSupportedLanguagesString
|
|
7
6
|
} from "../chunk-TQXMB7XV.js";
|
|
@@ -9,11 +8,8 @@ import {
|
|
|
9
8
|
detectProblemIdFromPath,
|
|
10
9
|
getProblemDirPath,
|
|
11
10
|
getProblemId
|
|
12
|
-
} from "../chunk-
|
|
13
|
-
import
|
|
14
|
-
getBojSessionCookie,
|
|
15
|
-
getCodeOpen
|
|
16
|
-
} from "../chunk-CIG2LEJC.js";
|
|
11
|
+
} from "../chunk-6ENX5K3C.js";
|
|
12
|
+
import "../chunk-PNIGP6LX.js";
|
|
17
13
|
import "../chunk-FYS2JH42.js";
|
|
18
14
|
|
|
19
15
|
// src/commands/submit.tsx
|
|
@@ -23,364 +19,157 @@ import { Badge, StatusMessage, Alert } from "@inkjs/ui";
|
|
|
23
19
|
import { readdir } from "fs/promises";
|
|
24
20
|
import { join } from "path";
|
|
25
21
|
import { readFile } from "fs/promises";
|
|
22
|
+
import { Spinner } from "@inkjs/ui";
|
|
23
|
+
import { execaCommand as execaCommand2 } from "execa";
|
|
26
24
|
|
|
27
|
-
// src/
|
|
28
|
-
import
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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;
|
|
25
|
+
// src/utils/clipboard.ts
|
|
26
|
+
import { execa, execaCommand } from "execa";
|
|
27
|
+
async function copyToClipboard(text) {
|
|
28
|
+
try {
|
|
29
|
+
if (process.platform === "win32") {
|
|
30
|
+
await execaCommand("clip", {
|
|
31
|
+
shell: true,
|
|
32
|
+
input: text
|
|
33
|
+
});
|
|
34
|
+
} else if (process.platform === "darwin") {
|
|
35
|
+
await execaCommand("pbcopy", {
|
|
36
|
+
shell: false,
|
|
37
|
+
input: text
|
|
38
|
+
});
|
|
39
|
+
} else {
|
|
40
|
+
try {
|
|
41
|
+
await execa("xclip", ["-selection", "clipboard"], {
|
|
42
|
+
input: text
|
|
43
|
+
});
|
|
44
|
+
} catch {
|
|
45
|
+
try {
|
|
46
|
+
await execa("xsel", ["--clipboard", "--input"], {
|
|
47
|
+
input: text
|
|
48
|
+
});
|
|
49
|
+
} catch {
|
|
59
50
|
return false;
|
|
60
51
|
}
|
|
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
52
|
}
|
|
182
53
|
}
|
|
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;
|
|
225
|
-
try {
|
|
226
|
-
csrfToken = await getCsrfToken(problemId, sessionCookie);
|
|
54
|
+
return true;
|
|
227
55
|
} catch (error) {
|
|
228
|
-
|
|
229
|
-
csrfToken = void 0;
|
|
230
|
-
}
|
|
231
|
-
const codeOpen = getCodeOpen() ? "open" : "close";
|
|
232
|
-
const formData = new URLSearchParams();
|
|
233
|
-
formData.append("problem_id", String(problemId));
|
|
234
|
-
formData.append("language", String(langConfig.bojLangId));
|
|
235
|
-
formData.append("code_open", codeOpen);
|
|
236
|
-
formData.append("source", sourceCode);
|
|
237
|
-
if (csrfToken) {
|
|
238
|
-
formData.append("csrf_key", csrfToken);
|
|
239
|
-
}
|
|
240
|
-
const submitResponse = await fetch(`${BOJ_BASE_URL}/submit/${problemId}`, {
|
|
241
|
-
method: "POST",
|
|
242
|
-
headers: {
|
|
243
|
-
Cookie: sessionCookie,
|
|
244
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
245
|
-
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
|
246
|
-
Referer: `${BOJ_BASE_URL}/submit/${problemId}`
|
|
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"
|
|
282
|
-
}
|
|
283
|
-
}
|
|
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
|
-
}
|
|
294
|
-
}
|
|
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
|
-
);
|
|
56
|
+
return false;
|
|
299
57
|
}
|
|
300
|
-
const result = await pollSubmitResult(problemId, submitId, sessionCookie);
|
|
301
|
-
return {
|
|
302
|
-
...result,
|
|
303
|
-
problemId,
|
|
304
|
-
submitId
|
|
305
|
-
};
|
|
306
58
|
}
|
|
307
59
|
|
|
308
60
|
// src/commands/submit.tsx
|
|
309
|
-
import { Spinner } from "@inkjs/ui";
|
|
310
61
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
62
|
+
var BOJ_BASE_URL = "https://www.acmicpc.net";
|
|
311
63
|
function SubmitCommand({
|
|
312
64
|
problemId,
|
|
313
65
|
language,
|
|
314
66
|
sourcePath,
|
|
315
|
-
dryRun,
|
|
316
67
|
onComplete
|
|
317
68
|
}) {
|
|
318
69
|
const [status, setStatus] = useState(
|
|
319
70
|
"loading"
|
|
320
71
|
);
|
|
321
72
|
const [message, setMessage] = useState("\uC81C\uCD9C \uC900\uBE44 \uC911...");
|
|
322
|
-
const [result, setResult] = useState(null);
|
|
323
73
|
const [error, setError] = useState(null);
|
|
74
|
+
const [submitUrl, setSubmitUrl] = useState("");
|
|
75
|
+
const [clipboardSuccess, setClipboardSuccess] = useState(false);
|
|
76
|
+
const [clipboardError, setClipboardError] = useState(null);
|
|
324
77
|
useEffect(() => {
|
|
325
78
|
async function submit() {
|
|
326
79
|
try {
|
|
327
|
-
setMessage("\uCF54\uB4DC\uB97C \uC77D\uB294 \uC911...");
|
|
80
|
+
setMessage("\uC18C\uC2A4 \uCF54\uB4DC\uB97C \uC77D\uB294 \uC911...");
|
|
328
81
|
const sourceCode = await readFile(sourcePath, "utf-8");
|
|
329
|
-
|
|
330
|
-
|
|
82
|
+
setMessage("\uD074\uB9BD\uBCF4\uB4DC\uC5D0 \uBCF5\uC0AC\uD558\uB294 \uC911...");
|
|
83
|
+
const clipboardResult = await copyToClipboard(sourceCode);
|
|
84
|
+
setClipboardSuccess(clipboardResult);
|
|
85
|
+
if (!clipboardResult) {
|
|
86
|
+
setClipboardError("\uD074\uB9BD\uBCF4\uB4DC \uBCF5\uC0AC\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
|
|
87
|
+
}
|
|
88
|
+
const url = `${BOJ_BASE_URL}/submit/${problemId}`;
|
|
89
|
+
setSubmitUrl(url);
|
|
90
|
+
setMessage("\uBE0C\uB77C\uC6B0\uC800\uB97C \uC5EC\uB294 \uC911...");
|
|
91
|
+
let command;
|
|
92
|
+
if (process.platform === "win32") {
|
|
93
|
+
command = `start "" "${url}"`;
|
|
94
|
+
} else if (process.platform === "darwin") {
|
|
95
|
+
command = `open "${url}"`;
|
|
331
96
|
} else {
|
|
332
|
-
|
|
97
|
+
command = `xdg-open "${url}"`;
|
|
333
98
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
dryRun
|
|
99
|
+
await execaCommand2(command, {
|
|
100
|
+
shell: true,
|
|
101
|
+
detached: true,
|
|
102
|
+
stdio: "ignore"
|
|
339
103
|
});
|
|
340
|
-
setResult(submitResult);
|
|
341
104
|
setStatus("success");
|
|
342
|
-
setMessage("\uC81C\uCD9C \uC644\uB8CC");
|
|
343
105
|
setTimeout(() => {
|
|
344
106
|
onComplete();
|
|
345
|
-
},
|
|
107
|
+
}, 2e3);
|
|
346
108
|
} catch (err) {
|
|
347
109
|
setStatus("error");
|
|
348
110
|
setError(err instanceof Error ? err.message : String(err));
|
|
349
111
|
setTimeout(() => {
|
|
350
112
|
onComplete();
|
|
351
|
-
},
|
|
113
|
+
}, 2e3);
|
|
352
114
|
}
|
|
353
115
|
}
|
|
354
116
|
submit();
|
|
355
|
-
}, [problemId, language, sourcePath,
|
|
117
|
+
}, [problemId, language, sourcePath, onComplete]);
|
|
356
118
|
if (status === "loading") {
|
|
357
|
-
return /* @__PURE__ */
|
|
119
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
120
|
+
/* @__PURE__ */ jsx(Spinner, { label: message }),
|
|
121
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
122
|
+
"\uBB38\uC81C #",
|
|
123
|
+
problemId
|
|
124
|
+
] }) })
|
|
125
|
+
] });
|
|
358
126
|
}
|
|
359
127
|
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
128
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
375
|
-
/* @__PURE__ */
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
129
|
+
/* @__PURE__ */ jsxs(Alert, { variant: "error", children: [
|
|
130
|
+
"\uC624\uB958 \uBC1C\uC0DD: ",
|
|
131
|
+
error
|
|
132
|
+
] }),
|
|
133
|
+
submitUrl && /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
134
|
+
"URL: ",
|
|
135
|
+
submitUrl
|
|
136
|
+
] }) })
|
|
381
137
|
] });
|
|
382
138
|
}
|
|
383
|
-
return
|
|
139
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: "100%", children: [
|
|
140
|
+
/* @__PURE__ */ jsx(StatusMessage, { variant: "success", children: "\uC81C\uCD9C \uD398\uC774\uC9C0\uB97C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4!" }),
|
|
141
|
+
/* @__PURE__ */ jsxs(
|
|
142
|
+
Box,
|
|
143
|
+
{
|
|
144
|
+
flexDirection: "column",
|
|
145
|
+
borderStyle: "round",
|
|
146
|
+
borderColor: "gray",
|
|
147
|
+
marginTop: 1,
|
|
148
|
+
paddingX: 1,
|
|
149
|
+
paddingY: 0,
|
|
150
|
+
alignSelf: "flex-start",
|
|
151
|
+
children: [
|
|
152
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
153
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\uBB38\uC81C \uBC88\uD638:" }),
|
|
154
|
+
" ",
|
|
155
|
+
problemId
|
|
156
|
+
] }),
|
|
157
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
158
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\uC5B8\uC5B4:" }),
|
|
159
|
+
" ",
|
|
160
|
+
/* @__PURE__ */ jsx(Text, { color: "gray", children: language })
|
|
161
|
+
] }),
|
|
162
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
163
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "URL:" }),
|
|
164
|
+
" ",
|
|
165
|
+
/* @__PURE__ */ jsx(Text, { color: "blue", underline: true, children: submitUrl })
|
|
166
|
+
] }),
|
|
167
|
+
/* @__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" }) }),
|
|
168
|
+
clipboardError && !clipboardSuccess && /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Alert, { variant: "warning", children: clipboardError }) })
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
)
|
|
172
|
+
] });
|
|
384
173
|
}
|
|
385
174
|
async function detectSolutionFile(problemDir) {
|
|
386
175
|
const files = await readdir(problemDir);
|
|
@@ -390,23 +179,38 @@ async function detectSolutionFile(problemDir) {
|
|
|
390
179
|
}
|
|
391
180
|
return join(problemDir, solutionFile);
|
|
392
181
|
}
|
|
393
|
-
async function submitCommand(problemId, language
|
|
182
|
+
async function submitCommand(problemId, language) {
|
|
394
183
|
const currentPathProblemId = detectProblemIdFromPath(process.cwd());
|
|
395
|
-
const problemDir = currentPathProblemId
|
|
184
|
+
const problemDir = problemId && currentPathProblemId !== problemId ? getProblemDirPath(problemId) : process.cwd();
|
|
396
185
|
const sourcePath = await detectSolutionFile(problemDir);
|
|
397
186
|
const detectedLanguage = language ?? detectLanguageFromFile(sourcePath);
|
|
398
187
|
if (!detectedLanguage) {
|
|
399
188
|
throw new Error(`\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uC5B8\uC5B4\uC785\uB2C8\uB2E4: ${sourcePath}`);
|
|
400
189
|
}
|
|
190
|
+
let finalProblemId = problemId ?? detectProblemIdFromPath(problemDir);
|
|
191
|
+
if (finalProblemId === null) {
|
|
192
|
+
const segments = problemDir.split(/[/\\]/).filter(Boolean);
|
|
193
|
+
const lastSegment = segments[segments.length - 1];
|
|
194
|
+
if (lastSegment) {
|
|
195
|
+
const parsedId = parseInt(lastSegment, 10);
|
|
196
|
+
if (!isNaN(parsedId) && parsedId > 0 && lastSegment === parsedId.toString()) {
|
|
197
|
+
finalProblemId = parsedId;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (finalProblemId === null) {
|
|
202
|
+
throw new Error(
|
|
203
|
+
"\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."
|
|
204
|
+
);
|
|
205
|
+
}
|
|
401
206
|
return new Promise((resolve) => {
|
|
402
207
|
const { unmount } = render(
|
|
403
208
|
/* @__PURE__ */ jsx(
|
|
404
209
|
SubmitCommand,
|
|
405
210
|
{
|
|
406
|
-
problemId,
|
|
211
|
+
problemId: finalProblemId,
|
|
407
212
|
language: detectedLanguage,
|
|
408
213
|
sourcePath,
|
|
409
|
-
dryRun,
|
|
410
214
|
onComplete: () => {
|
|
411
215
|
unmount();
|
|
412
216
|
resolve();
|
|
@@ -421,15 +225,15 @@ var submitHelp = `
|
|
|
421
225
|
$ ps submit [\uBB38\uC81C\uBC88\uD638] [\uC635\uC158]
|
|
422
226
|
|
|
423
227
|
\uC124\uBA85:
|
|
424
|
-
\
|
|
425
|
-
- \
|
|
228
|
+
\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.
|
|
229
|
+
- \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
|
|
426
230
|
- solution.* \uD30C\uC77C\uC744 \uC790\uB3D9\uC73C\uB85C \uCC3E\uC544 \uC5B8\uC5B4 \uAC10\uC9C0
|
|
427
|
-
- \
|
|
231
|
+
- \uC18C\uC2A4 \uCF54\uB4DC\uB97C \uD074\uB9BD\uBCF4\uB4DC\uC5D0 \uC790\uB3D9 \uBCF5\uC0AC
|
|
232
|
+
- \uC81C\uCD9C \uD398\uC774\uC9C0\uB97C \uBE0C\uB77C\uC6B0\uC800\uB85C \uC790\uB3D9 \uC5F4\uAE30
|
|
428
233
|
|
|
429
234
|
\uC635\uC158:
|
|
430
235
|
--language, -l \uC5B8\uC5B4 \uC120\uD0DD (\uC9C0\uC815 \uC2DC \uC790\uB3D9 \uAC10\uC9C0 \uBB34\uC2DC)
|
|
431
236
|
\uC9C0\uC6D0 \uC5B8\uC5B4: ${getSupportedLanguagesString()}
|
|
432
|
-
--dry-run \uC2E4\uC81C \uC81C\uCD9C \uC5C6\uC774 \uAC80\uC99D\uB9CC \uC218\uD589
|
|
433
237
|
|
|
434
238
|
\uC608\uC81C:
|
|
435
239
|
$ ps submit # \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uC81C\uCD9C
|
|
@@ -443,15 +247,6 @@ async function submitExecute(args, flags) {
|
|
|
443
247
|
return;
|
|
444
248
|
}
|
|
445
249
|
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.`
|
|
452
|
-
);
|
|
453
|
-
process.exit(1);
|
|
454
|
-
}
|
|
455
250
|
const validLanguages = getSupportedLanguages();
|
|
456
251
|
const language = flags.language;
|
|
457
252
|
if (flags.language && language && !validLanguages.includes(language)) {
|
|
@@ -460,7 +255,7 @@ async function submitExecute(args, flags) {
|
|
|
460
255
|
);
|
|
461
256
|
process.exit(1);
|
|
462
257
|
}
|
|
463
|
-
await submitCommand(problemId, language
|
|
258
|
+
await submitCommand(problemId, language);
|
|
464
259
|
}
|
|
465
260
|
var submitCommandDef = {
|
|
466
261
|
name: "submit",
|
package/dist/commands/test.js
CHANGED
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
detectProblemIdFromPath,
|
|
12
12
|
getProblemDirPath,
|
|
13
13
|
getProblemId
|
|
14
|
-
} from "../chunk-
|
|
15
|
-
import "../chunk-
|
|
14
|
+
} from "../chunk-6ENX5K3C.js";
|
|
15
|
+
import "../chunk-PNIGP6LX.js";
|
|
16
16
|
import "../chunk-FYS2JH42.js";
|
|
17
17
|
|
|
18
18
|
// src/commands/test.tsx
|