@rhseung/ps-cli 1.7.3 → 1.7.5
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 +277 -24
- package/dist/{chunk-TNGUME4H.js → chunk-F4LZ6ENP.js} +23 -24
- package/dist/{chunk-OJZLQ6FK.js → chunk-VIHXBCOZ.js} +30 -4
- package/dist/commands/{solve.js → archive.js} +37 -37
- package/dist/commands/config.js +16 -16
- package/dist/commands/fetch.js +42 -15
- package/dist/commands/init.js +51 -37
- package/dist/commands/open.js +2 -7
- package/dist/commands/run.js +44 -28
- package/dist/commands/search.js +4 -4
- package/dist/commands/stats.js +1 -1
- package/dist/commands/submit.js +5 -10
- package/dist/commands/test.js +21 -21
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -24,99 +24,316 @@ ps test
|
|
|
24
24
|
|
|
25
25
|
# 4. 제출
|
|
26
26
|
ps submit
|
|
27
|
+
|
|
28
|
+
# 5. 커밋 및 아카이빙
|
|
29
|
+
ps archive
|
|
27
30
|
```
|
|
28
31
|
|
|
29
32
|
## 명령어
|
|
30
33
|
|
|
31
34
|
### `init` - 프로젝트 초기화
|
|
32
35
|
|
|
33
|
-
프로젝트를 초기화하고 설정을 구성합니다.
|
|
36
|
+
프로젝트를 대화형으로 초기화하고 설정을 구성합니다.
|
|
37
|
+
|
|
38
|
+
**사용법:**
|
|
34
39
|
|
|
35
40
|
```bash
|
|
36
41
|
ps init
|
|
37
42
|
```
|
|
38
43
|
|
|
44
|
+
**설명:**
|
|
45
|
+
|
|
46
|
+
- 단계별로 설정을 물어봅니다
|
|
47
|
+
- 아카이브 디렉토리, solving 디렉토리, 아카이빙 전략, 기본 언어, 에디터 등을 설정할 수 있습니다
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
39
51
|
### `fetch` - 문제 가져오기
|
|
40
52
|
|
|
41
53
|
백준 문제를 가져와서 로컬에 파일을 생성합니다.
|
|
42
54
|
|
|
55
|
+
**사용법:**
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
ps fetch <문제번호> [옵션]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**옵션:**
|
|
62
|
+
|
|
63
|
+
- `--language`, `-l`: 언어 선택 (python, javascript, typescript, cpp)
|
|
64
|
+
- 기본값: python 또는 설정 파일의 `default-language`
|
|
65
|
+
|
|
66
|
+
**예제:**
|
|
67
|
+
|
|
43
68
|
```bash
|
|
44
69
|
ps fetch 1000
|
|
45
70
|
ps fetch 1000 --language python
|
|
71
|
+
ps fetch 1000 -l cpp
|
|
46
72
|
```
|
|
47
73
|
|
|
74
|
+
**설명:**
|
|
75
|
+
|
|
76
|
+
- Solved.ac API와 BOJ 크롤링을 통해 문제 정보 수집
|
|
77
|
+
- 문제 설명, 입출력 형식, 예제 입출력 파일 자동 생성
|
|
78
|
+
- 선택한 언어의 솔루션 템플릿 파일 생성
|
|
79
|
+
- README.md에 문제 정보, 통계, 태그 등 포함
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
48
83
|
### `test` - 로컬 테스트
|
|
49
84
|
|
|
50
85
|
예제 입출력으로 테스트를 실행합니다.
|
|
51
86
|
|
|
87
|
+
**사용법:**
|
|
88
|
+
|
|
52
89
|
```bash
|
|
53
|
-
ps test
|
|
54
|
-
ps test 1000
|
|
55
|
-
ps test --watch # 파일 변경 시 자동 재테스트
|
|
90
|
+
ps test [문제번호] [옵션]
|
|
56
91
|
```
|
|
57
92
|
|
|
93
|
+
**옵션:**
|
|
94
|
+
|
|
95
|
+
- `--language`, `-l`: 언어 선택 (지정 시 자동 감지 무시)
|
|
96
|
+
- 지원 언어: python, javascript, typescript, cpp
|
|
97
|
+
- `--watch`, `-w`: watch 모드 (파일 변경 시 자동 재테스트)
|
|
98
|
+
- solution._, testcases/\*\*/_.txt 파일 변경 감지
|
|
99
|
+
|
|
100
|
+
**예제:**
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
ps test # 현재 디렉토리에서 테스트
|
|
104
|
+
ps test 1000 # 1000번 문제 테스트
|
|
105
|
+
ps test --watch # watch 모드로 테스트
|
|
106
|
+
ps test 1000 --watch # 1000번 문제를 watch 모드로 테스트
|
|
107
|
+
ps test --language python # Python으로 테스트
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**설명:**
|
|
111
|
+
|
|
112
|
+
- 현재 디렉토리 또는 지정한 문제 번호의 테스트 실행
|
|
113
|
+
- solution.\* 파일을 자동으로 찾아 언어 감지
|
|
114
|
+
- testcases/{번호}/input.txt와 testcases/{번호}/output.txt 파일을 기반으로 테스트
|
|
115
|
+
- 문제의 시간 제한을 자동으로 적용
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
58
119
|
### `run` - 코드 실행
|
|
59
120
|
|
|
60
121
|
테스트 없이 코드를 실행합니다.
|
|
61
122
|
|
|
123
|
+
**사용법:**
|
|
124
|
+
|
|
62
125
|
```bash
|
|
63
|
-
ps run
|
|
64
|
-
ps run 1000
|
|
126
|
+
ps run [문제번호] [옵션]
|
|
65
127
|
```
|
|
66
128
|
|
|
129
|
+
**옵션:**
|
|
130
|
+
|
|
131
|
+
- `--language`, `-l`: 언어 선택 (지정 시 자동 감지 무시)
|
|
132
|
+
- 지원 언어: python, javascript, typescript, cpp
|
|
133
|
+
- `--input`, `-i`: 입력 파일 지정
|
|
134
|
+
- 숫자만 입력 시 testcases/{숫자}/input.txt로 자동 변환 (예: `--input 1`)
|
|
135
|
+
- 전체 경로도 지원 (예: testcases/1/input.txt)
|
|
136
|
+
|
|
137
|
+
**예제:**
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
ps run # 현재 디렉토리에서 표준 입력으로 실행
|
|
141
|
+
ps run 1000 # 1000번 문제 표준 입력으로 실행
|
|
142
|
+
ps run --language python # Python으로 표준 입력으로 실행
|
|
143
|
+
ps run --input 1 # 테스트 케이스 1번 사용
|
|
144
|
+
ps run --input testcases/1/input.txt # 전체 경로로 입력 파일 지정
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**설명:**
|
|
148
|
+
|
|
149
|
+
- 현재 디렉토리 또는 지정한 문제 번호의 코드 실행
|
|
150
|
+
- solution.\* 파일을 자동으로 찾아 언어 감지
|
|
151
|
+
- --input 옵션으로 입력 파일 지정 가능 (예: `--input 1` 또는 `--input testcases/1/input.txt`)
|
|
152
|
+
- 옵션 없이 실행 시 표준 입력으로 입력 받기
|
|
153
|
+
- 테스트 케이스 검증 없이 단순 실행
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
67
157
|
### `submit` - 제출
|
|
68
158
|
|
|
69
159
|
백준 제출 페이지를 열고 소스 코드를 클립보드에 복사합니다.
|
|
70
160
|
|
|
161
|
+
**사용법:**
|
|
162
|
+
|
|
71
163
|
```bash
|
|
72
|
-
ps submit
|
|
73
|
-
ps submit 1000
|
|
164
|
+
ps submit [문제번호] [옵션]
|
|
74
165
|
```
|
|
75
166
|
|
|
76
|
-
|
|
167
|
+
**옵션:**
|
|
77
168
|
|
|
78
|
-
|
|
169
|
+
- `--language`, `-l`: 언어 선택 (지정 시 자동 감지 무시)
|
|
170
|
+
- 지원 언어: python, javascript, typescript, cpp
|
|
171
|
+
|
|
172
|
+
**예제:**
|
|
79
173
|
|
|
80
174
|
```bash
|
|
81
|
-
ps
|
|
175
|
+
ps submit # 현재 디렉토리에서 제출
|
|
176
|
+
ps submit 1000 # 1000번 문제 제출
|
|
177
|
+
ps submit --language python # Python으로 제출
|
|
82
178
|
```
|
|
83
179
|
|
|
180
|
+
**설명:**
|
|
181
|
+
|
|
182
|
+
- 문제 번호를 인자로 전달하거나 문제 디렉토리에서 실행하면 자동으로 문제 번호를 추론
|
|
183
|
+
- solution.\* 파일을 자동으로 찾아 언어 감지
|
|
184
|
+
- 소스 코드를 클립보드에 자동 복사
|
|
185
|
+
- 제출 페이지를 브라우저로 자동 열기
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
### `archive` - 아카이빙
|
|
190
|
+
|
|
191
|
+
solving 디렉토리의 문제를 archive 디렉토리로 이동하고 Git 커밋을 생성합니다.
|
|
192
|
+
|
|
193
|
+
**사용법:**
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
ps archive [문제번호]
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**예제:**
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
ps archive 1000 # 1000번 문제 아카이빙
|
|
203
|
+
ps archive # 현재 디렉토리에서 문제 번호 자동 감지
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**설명:**
|
|
207
|
+
|
|
208
|
+
- solving 디렉토리에서 문제를 찾아 archive 디렉토리로 이동
|
|
209
|
+
- Git add 및 commit 실행
|
|
210
|
+
- 커밋 메시지: "solve: {문제번호} - {문제이름}"
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
84
214
|
### `open` - 문제 페이지 열기
|
|
85
215
|
|
|
86
216
|
백준 문제 페이지를 브라우저로 엽니다.
|
|
87
217
|
|
|
218
|
+
**사용법:**
|
|
219
|
+
|
|
88
220
|
```bash
|
|
89
|
-
ps open
|
|
221
|
+
ps open [문제번호]
|
|
90
222
|
```
|
|
91
223
|
|
|
224
|
+
**예제:**
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
ps open 1000 # 1000번 문제 열기
|
|
228
|
+
ps open # 문제 디렉토리에서 실행 시 자동 추론
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**설명:**
|
|
232
|
+
|
|
233
|
+
- 문제 번호를 인자로 전달하거나 문제 디렉토리에서 실행하면 자동으로 문제 번호를 추론
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
92
237
|
### `search` - 문제 검색
|
|
93
238
|
|
|
94
|
-
solved.ac에서 문제를
|
|
239
|
+
solved.ac에서 문제를 검색하거나 백준 문제집의 문제 목록을 표시합니다.
|
|
240
|
+
|
|
241
|
+
**사용법:**
|
|
95
242
|
|
|
96
243
|
```bash
|
|
97
|
-
ps search
|
|
98
|
-
ps search --workbook
|
|
244
|
+
ps search <쿼리> [옵션]
|
|
245
|
+
ps search --workbook <문제집ID>
|
|
99
246
|
```
|
|
100
247
|
|
|
248
|
+
**옵션:**
|
|
249
|
+
|
|
250
|
+
- `--workbook`: 문제집 ID를 지정하여 해당 문제집의 문제 목록을 표시
|
|
251
|
+
|
|
252
|
+
**예제:**
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
ps search "*g1...g5" # Gold 1-5 문제 검색
|
|
256
|
+
ps search "tier:g1...g5" # Gold 1-5 문제 검색 (tier: 문법)
|
|
257
|
+
ps search "#dp" # DP 태그 문제 검색
|
|
258
|
+
ps search "tag:dp" # DP 태그 문제 검색 (tag: 문법)
|
|
259
|
+
ps search "*g1...g5 #dp" # Gold 1-5 티어의 DP 태그 문제 검색
|
|
260
|
+
ps search --workbook 25052 # 문제집 25052의 문제 목록 표시
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**설명:**
|
|
264
|
+
|
|
265
|
+
- solved.ac 검색어 문법을 지원합니다
|
|
266
|
+
- 문제 목록에서 선택하면 자동으로 브라우저에서 문제 페이지를 엽니다
|
|
267
|
+
- 페이지네이션을 통해 여러 페이지의 결과를 탐색할 수 있습니다
|
|
268
|
+
- `--workbook` 옵션으로 백준 문제집의 문제 목록을 볼 수 있습니다
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
101
272
|
### `stats` - 통계 조회
|
|
102
273
|
|
|
103
|
-
Solved.ac 사용자 통계를 조회합니다.
|
|
274
|
+
Solved.ac에서 사용자 통계를 조회합니다.
|
|
275
|
+
|
|
276
|
+
**사용법:**
|
|
104
277
|
|
|
105
278
|
```bash
|
|
106
|
-
ps stats
|
|
107
|
-
ps stats myhandle
|
|
279
|
+
ps stats [핸들] [옵션]
|
|
108
280
|
```
|
|
109
281
|
|
|
282
|
+
**옵션:**
|
|
283
|
+
|
|
284
|
+
- `--handle`, `-h`: Solved.ac 핸들
|
|
285
|
+
- 설정에 저장된 값 사용 가능
|
|
286
|
+
- 인자로 전달하거나 플래그로 지정 가능
|
|
287
|
+
|
|
288
|
+
**예제:**
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
ps stats myhandle # myhandle의 통계 조회
|
|
292
|
+
ps stats --handle myhandle # 플래그로 핸들 지정
|
|
293
|
+
ps stats # 설정에 저장된 핸들 사용
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
**설명:**
|
|
297
|
+
|
|
298
|
+
- 티어, 레이팅, 해결한 문제 수 등 표시
|
|
299
|
+
- 그라데이션으로 시각적으로 표시
|
|
300
|
+
- 핸들 우선순위: 인자 > 플래그 > 설정 파일
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
110
304
|
### `config` - 설정 관리
|
|
111
305
|
|
|
112
|
-
프로젝트
|
|
306
|
+
프로젝트 설정 파일(.ps-cli.json)을 관리합니다.
|
|
307
|
+
|
|
308
|
+
**사용법:**
|
|
113
309
|
|
|
114
310
|
```bash
|
|
115
|
-
ps config
|
|
116
|
-
ps config set default-language python
|
|
117
|
-
ps config get archive-strategy
|
|
311
|
+
ps config <명령어> [키] [값]
|
|
118
312
|
```
|
|
119
313
|
|
|
314
|
+
**명령어:**
|
|
315
|
+
|
|
316
|
+
- `get [키]`: 설정 값 조회 (키 없으면 대화형 선택)
|
|
317
|
+
- `set [키] [값]`: 설정 값 설정 (키/값 없으면 대화형 선택)
|
|
318
|
+
- `list`: 모든 설정 조회
|
|
319
|
+
- `clear`: .ps-cli.json 파일 삭제
|
|
320
|
+
|
|
321
|
+
**예제:**
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
ps config get # 대화형으로 키 선택 후 값 조회
|
|
325
|
+
ps config get default-language # default-language 값 조회
|
|
326
|
+
ps config set # 대화형으로 키 선택 후 값 설정
|
|
327
|
+
ps config set editor cursor # editor를 cursor로 설정
|
|
328
|
+
ps config list # 모든 설정 조회
|
|
329
|
+
ps config clear # .ps-cli.json 파일 삭제
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
**설명:**
|
|
333
|
+
|
|
334
|
+
- 설정은 현재 프로젝트의 .ps-cli.json 파일에 저장됩니다
|
|
335
|
+
- 대화형 모드로 키와 값을 선택할 수 있습니다
|
|
336
|
+
|
|
120
337
|
## 설정
|
|
121
338
|
|
|
122
339
|
프로젝트 루트의 `.ps-cli.json` 파일에 저장됩니다.
|
|
@@ -127,7 +344,7 @@ ps config get archive-strategy
|
|
|
127
344
|
- `editor`: 에디터 명령어 (code, cursor, vim 등)
|
|
128
345
|
- `auto-open-editor`: fetch 후 자동으로 에디터 열기 (true/false)
|
|
129
346
|
- `solved-ac-handle`: Solved.ac 핸들
|
|
130
|
-
- `
|
|
347
|
+
- `archive-dir`: 아카이브된 문제 디렉토리 (기본값: problems)
|
|
131
348
|
- `solving-dir`: 푸는 중인 문제 디렉토리 (기본값: solving)
|
|
132
349
|
- `archive-strategy`: 아카이빙 전략
|
|
133
350
|
|
|
@@ -173,7 +390,43 @@ ps config get archive-strategy
|
|
|
173
390
|
3. **작성**: `solving/1000/`에서 코드 작성
|
|
174
391
|
4. **테스트**: `ps test`로 로컬 테스트
|
|
175
392
|
5. **제출**: `ps submit`으로 제출
|
|
176
|
-
6. **아카이빙**: `ps
|
|
393
|
+
6. **아카이빙**: `ps archive`로 archive 디렉토리로 이동
|
|
394
|
+
|
|
395
|
+
## 개발
|
|
396
|
+
|
|
397
|
+
로컬에서 개발하거나 테스트할 때는 글로벌로 설치된 `ps` 명령어와 충돌을 피하기 위해 다음 방법을 사용할 수 있습니다:
|
|
398
|
+
|
|
399
|
+
### 방법 1: 절대 경로로 직접 실행 (외부 폴더 테스트 가능)
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
# 빌드
|
|
403
|
+
bun run build
|
|
404
|
+
|
|
405
|
+
# 프로젝트 디렉토리에서 절대 경로로 실행
|
|
406
|
+
/path/to/ps-cli/dist/index.js init
|
|
407
|
+
/path/to/ps-cli/dist/index.js fetch 1000
|
|
408
|
+
|
|
409
|
+
# 또는 프로젝트 디렉토리로 이동 후
|
|
410
|
+
cd /path/to/ps-cli
|
|
411
|
+
node dist/index.js init
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### 방법 2: npm link 사용 (주의 필요)
|
|
415
|
+
|
|
416
|
+
```bash
|
|
417
|
+
# 프로젝트 디렉토리에서
|
|
418
|
+
bun run build
|
|
419
|
+
npm link
|
|
420
|
+
|
|
421
|
+
# 외부 폴더에서 테스트
|
|
422
|
+
cd /path/to/test-project
|
|
423
|
+
ps init # 로컬 버전이 사용됨
|
|
424
|
+
|
|
425
|
+
# 테스트 후 링크 해제
|
|
426
|
+
npm unlink -g @rhseung/ps-cli
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
**주의:** `npm link`를 사용하면 글로벌 설치된 버전이 링크된 버전으로 대체됩니다.
|
|
177
430
|
|
|
178
431
|
## 라이선스
|
|
179
432
|
|
|
@@ -9924,7 +9924,7 @@ var config = new Conf({
|
|
|
9924
9924
|
autoOpenEditor: false,
|
|
9925
9925
|
// 기본값: 자동 열기 비활성화
|
|
9926
9926
|
solvedAcHandle: void 0,
|
|
9927
|
-
|
|
9927
|
+
archiveDir: "problems",
|
|
9928
9928
|
// 기본값: problems 디렉토리
|
|
9929
9929
|
solvingDir: "solving"
|
|
9930
9930
|
// 기본값: solving 디렉토리
|
|
@@ -10009,12 +10009,12 @@ function getSolvedAcHandle() {
|
|
|
10009
10009
|
}
|
|
10010
10010
|
return config.get("solvedAcHandle");
|
|
10011
10011
|
}
|
|
10012
|
-
function
|
|
10012
|
+
function getArchiveDir() {
|
|
10013
10013
|
const projectConfig = getProjectConfigSync();
|
|
10014
|
-
if (projectConfig?.
|
|
10015
|
-
return projectConfig.
|
|
10014
|
+
if (projectConfig?.archiveDir !== void 0) {
|
|
10015
|
+
return projectConfig.archiveDir;
|
|
10016
10016
|
}
|
|
10017
|
-
return config.get("
|
|
10017
|
+
return config.get("archiveDir") ?? "problems";
|
|
10018
10018
|
}
|
|
10019
10019
|
function getSolvingDir() {
|
|
10020
10020
|
const projectConfig = getProjectConfigSync();
|
|
@@ -10264,11 +10264,11 @@ function getArchiveSubPath(problemId, strategy = "flat", problem) {
|
|
|
10264
10264
|
}
|
|
10265
10265
|
}
|
|
10266
10266
|
function detectProblemIdFromPath(cwd = process.cwd()) {
|
|
10267
|
-
const
|
|
10267
|
+
const archiveDir = getArchiveDir();
|
|
10268
10268
|
const solvingDir = getSolvingDir();
|
|
10269
10269
|
const archiveStrategy = getArchiveStrategy();
|
|
10270
10270
|
const normalizedPath = cwd.replace(/\\/g, "/");
|
|
10271
|
-
const dirsToCheck = [
|
|
10271
|
+
const dirsToCheck = [archiveDir, solvingDir].filter(
|
|
10272
10272
|
(dir) => dir && dir !== "." && dir !== ""
|
|
10273
10273
|
);
|
|
10274
10274
|
if (dirsToCheck.length === 0) {
|
|
@@ -10359,22 +10359,22 @@ function getProblemId(args, cwd = process.cwd()) {
|
|
|
10359
10359
|
}
|
|
10360
10360
|
return detectProblemIdFromPath(cwd);
|
|
10361
10361
|
}
|
|
10362
|
-
function
|
|
10363
|
-
const
|
|
10362
|
+
function getArchiveDirPath(problemId, cwd = process.cwd(), problem) {
|
|
10363
|
+
const archiveDir = getArchiveDir();
|
|
10364
10364
|
const archiveStrategy = getArchiveStrategy();
|
|
10365
10365
|
const projectRoot = findProjectRoot(cwd);
|
|
10366
10366
|
const baseDir = projectRoot || cwd;
|
|
10367
10367
|
const subPath = getArchiveSubPath(problemId, archiveStrategy, problem);
|
|
10368
|
-
if (
|
|
10368
|
+
if (archiveDir === "." || archiveDir === "") {
|
|
10369
10369
|
if (subPath) {
|
|
10370
10370
|
return join2(baseDir, subPath, problemId.toString());
|
|
10371
10371
|
}
|
|
10372
10372
|
return join2(baseDir, problemId.toString());
|
|
10373
10373
|
}
|
|
10374
10374
|
if (subPath) {
|
|
10375
|
-
return join2(baseDir,
|
|
10375
|
+
return join2(baseDir, archiveDir, subPath, problemId.toString());
|
|
10376
10376
|
}
|
|
10377
|
-
return join2(baseDir,
|
|
10377
|
+
return join2(baseDir, archiveDir, problemId.toString());
|
|
10378
10378
|
}
|
|
10379
10379
|
function getSolvingDirPath(problemId, cwd = process.cwd(), _) {
|
|
10380
10380
|
const solvingDir = getSolvingDir();
|
|
@@ -10408,27 +10408,27 @@ async function resolveProblemContext(args, options = {}) {
|
|
|
10408
10408
|
);
|
|
10409
10409
|
}
|
|
10410
10410
|
const isCurrentDir = problemId === null || problemId !== null && currentPathProblemId === problemId;
|
|
10411
|
-
let
|
|
10411
|
+
let archiveDir;
|
|
10412
10412
|
if (problemId && !isCurrentDir) {
|
|
10413
10413
|
const solvingDirPath = getSolvingDirPath(problemId);
|
|
10414
|
-
const
|
|
10414
|
+
const archiveDirPath = getArchiveDirPath(problemId);
|
|
10415
10415
|
const solvingDirExists = await directoryExists(solvingDirPath);
|
|
10416
10416
|
if (solvingDirExists) {
|
|
10417
|
-
|
|
10417
|
+
archiveDir = solvingDirPath;
|
|
10418
10418
|
} else {
|
|
10419
|
-
const
|
|
10420
|
-
if (
|
|
10421
|
-
|
|
10419
|
+
const archiveDirExists = await directoryExists(archiveDirPath);
|
|
10420
|
+
if (archiveDirExists) {
|
|
10421
|
+
archiveDir = archiveDirPath;
|
|
10422
10422
|
} else {
|
|
10423
|
-
|
|
10423
|
+
archiveDir = solvingDirPath;
|
|
10424
10424
|
}
|
|
10425
10425
|
}
|
|
10426
10426
|
} else {
|
|
10427
|
-
|
|
10427
|
+
archiveDir = process.cwd();
|
|
10428
10428
|
}
|
|
10429
10429
|
return {
|
|
10430
10430
|
problemId,
|
|
10431
|
-
|
|
10431
|
+
archiveDir,
|
|
10432
10432
|
isCurrentDir
|
|
10433
10433
|
};
|
|
10434
10434
|
}
|
|
@@ -10594,7 +10594,7 @@ export {
|
|
|
10594
10594
|
getEditor,
|
|
10595
10595
|
getAutoOpenEditor,
|
|
10596
10596
|
getSolvedAcHandle,
|
|
10597
|
-
|
|
10597
|
+
getArchiveDir,
|
|
10598
10598
|
getSolvingDir,
|
|
10599
10599
|
getArchiveStrategy,
|
|
10600
10600
|
getNextTierMinRating,
|
|
@@ -10603,8 +10603,7 @@ export {
|
|
|
10603
10603
|
getTierColor,
|
|
10604
10604
|
getTierImageUrl,
|
|
10605
10605
|
detectProblemIdFromPath,
|
|
10606
|
-
|
|
10607
|
-
getProblemDirPath,
|
|
10606
|
+
getArchiveDirPath,
|
|
10608
10607
|
getSolvingDirPath,
|
|
10609
10608
|
resolveProblemContext,
|
|
10610
10609
|
resolveLanguage,
|
|
@@ -6,7 +6,23 @@ import {
|
|
|
6
6
|
// src/services/runner.ts
|
|
7
7
|
import { readFile } from "fs/promises";
|
|
8
8
|
import { join } from "path";
|
|
9
|
+
import { createInterface } from "readline";
|
|
9
10
|
import { execa, execaCommand } from "execa";
|
|
11
|
+
async function readStdin() {
|
|
12
|
+
return new Promise((resolve) => {
|
|
13
|
+
const lines = [];
|
|
14
|
+
const rl = createInterface({
|
|
15
|
+
input: process.stdin,
|
|
16
|
+
output: process.stdout
|
|
17
|
+
});
|
|
18
|
+
rl.on("line", (line) => {
|
|
19
|
+
lines.push(line);
|
|
20
|
+
});
|
|
21
|
+
rl.on("close", () => {
|
|
22
|
+
resolve(lines.join("\n"));
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
}
|
|
10
26
|
async function runSolution({
|
|
11
27
|
problemDir,
|
|
12
28
|
language,
|
|
@@ -16,7 +32,15 @@ async function runSolution({
|
|
|
16
32
|
const langConfig = getLanguageConfig(language);
|
|
17
33
|
const solutionFile = `solution.${langConfig.extension}`;
|
|
18
34
|
const solutionPath = join(problemDir, solutionFile);
|
|
19
|
-
|
|
35
|
+
let input;
|
|
36
|
+
let capturedInput;
|
|
37
|
+
if (inputPath) {
|
|
38
|
+
input = await readFile(inputPath, "utf-8");
|
|
39
|
+
capturedInput = input;
|
|
40
|
+
} else {
|
|
41
|
+
capturedInput = await readStdin();
|
|
42
|
+
input = capturedInput;
|
|
43
|
+
}
|
|
20
44
|
const start = Date.now();
|
|
21
45
|
try {
|
|
22
46
|
if (langConfig.compileCommand) {
|
|
@@ -27,7 +51,7 @@ async function runSolution({
|
|
|
27
51
|
}
|
|
28
52
|
const child = execa(langConfig.runCommand, [solutionPath], {
|
|
29
53
|
cwd: problemDir,
|
|
30
|
-
input,
|
|
54
|
+
...input !== void 0 ? { input } : { stdin: "inherit" },
|
|
31
55
|
timeout: timeoutMs
|
|
32
56
|
});
|
|
33
57
|
const result = await child;
|
|
@@ -39,7 +63,8 @@ async function runSolution({
|
|
|
39
63
|
stderr,
|
|
40
64
|
exitCode,
|
|
41
65
|
timedOut: false,
|
|
42
|
-
durationMs
|
|
66
|
+
durationMs,
|
|
67
|
+
...capturedInput !== void 0 && { input: capturedInput }
|
|
43
68
|
};
|
|
44
69
|
} catch (error) {
|
|
45
70
|
const durationMs = Date.now() - start;
|
|
@@ -50,7 +75,8 @@ async function runSolution({
|
|
|
50
75
|
stderr: err.stderr ?? err.shortMessage ?? err.message,
|
|
51
76
|
exitCode: err.exitCode ?? null,
|
|
52
77
|
timedOut: Boolean(err.timedOut),
|
|
53
|
-
durationMs
|
|
78
|
+
durationMs,
|
|
79
|
+
...capturedInput !== void 0 && { input: capturedInput }
|
|
54
80
|
};
|
|
55
81
|
}
|
|
56
82
|
return {
|