opensoma 0.5.1 → 0.7.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/package.json +5 -1
- package/dist/src/agent-browser-launcher.d.ts +43 -0
- package/dist/src/agent-browser-launcher.d.ts.map +1 -0
- package/dist/src/agent-browser-launcher.js +97 -0
- package/dist/src/agent-browser-launcher.js.map +1 -0
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +8 -5
- package/dist/src/cli.js.map +1 -1
- package/dist/src/client.d.ts +34 -7
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/client.js +224 -52
- package/dist/src/client.js.map +1 -1
- package/dist/src/commands/agent-browser.d.ts +3 -0
- package/dist/src/commands/agent-browser.d.ts.map +1 -0
- package/dist/src/commands/agent-browser.js +27 -0
- package/dist/src/commands/agent-browser.js.map +1 -0
- package/dist/src/commands/auth.d.ts +1 -1
- package/dist/src/commands/auth.d.ts.map +1 -1
- package/dist/src/commands/auth.js +4 -2
- package/dist/src/commands/auth.js.map +1 -1
- package/dist/src/commands/dashboard.d.ts +13 -0
- package/dist/src/commands/dashboard.d.ts.map +1 -1
- package/dist/src/commands/dashboard.js +10 -18
- package/dist/src/commands/dashboard.js.map +1 -1
- package/dist/src/commands/helpers.d.ts +1 -1
- package/dist/src/commands/helpers.d.ts.map +1 -1
- package/dist/src/commands/helpers.js +2 -2
- package/dist/src/commands/helpers.js.map +1 -1
- package/dist/src/commands/index.d.ts +3 -1
- package/dist/src/commands/index.d.ts.map +1 -1
- package/dist/src/commands/index.js +3 -1
- package/dist/src/commands/index.js.map +1 -1
- package/dist/src/commands/mentoring.d.ts.map +1 -1
- package/dist/src/commands/mentoring.js +54 -29
- package/dist/src/commands/mentoring.js.map +1 -1
- package/dist/src/commands/notice.d.ts.map +1 -1
- package/dist/src/commands/notice.js +2 -1
- package/dist/src/commands/notice.js.map +1 -1
- package/dist/src/commands/report.d.ts.map +1 -1
- package/dist/src/commands/report.js +4 -2
- package/dist/src/commands/report.js.map +1 -1
- package/dist/src/commands/room.d.ts.map +1 -1
- package/dist/src/commands/room.js +125 -2
- package/dist/src/commands/room.js.map +1 -1
- package/dist/src/commands/schedule.d.ts +3 -0
- package/dist/src/commands/schedule.d.ts.map +1 -0
- package/dist/src/commands/schedule.js +27 -0
- package/dist/src/commands/schedule.js.map +1 -0
- package/dist/src/commands/team.d.ts.map +1 -1
- package/dist/src/commands/team.js +55 -4
- package/dist/src/commands/team.js.map +1 -1
- package/dist/src/commands/toz.d.ts +16 -0
- package/dist/src/commands/toz.d.ts.map +1 -0
- package/dist/src/commands/toz.js +488 -0
- package/dist/src/commands/toz.js.map +1 -0
- package/dist/src/constants.d.ts +5 -5
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +20 -8
- package/dist/src/constants.js.map +1 -1
- package/dist/src/credential-manager.d.ts +15 -0
- package/dist/src/credential-manager.d.ts.map +1 -1
- package/dist/src/credential-manager.js +46 -0
- package/dist/src/credential-manager.js.map +1 -1
- package/dist/src/formatters.d.ts +11 -3
- package/dist/src/formatters.d.ts.map +1 -1
- package/dist/src/formatters.js +281 -52
- package/dist/src/formatters.js.map +1 -1
- package/dist/src/http.d.ts +8 -0
- package/dist/src/http.d.ts.map +1 -1
- package/dist/src/http.js +29 -1
- package/dist/src/http.js.map +1 -1
- package/dist/src/index.d.ts +8 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/session-recovery.js +2 -0
- package/dist/src/session-recovery.js.map +1 -1
- package/dist/src/shared/utils/swmaestro.d.ts +34 -1
- package/dist/src/shared/utils/swmaestro.d.ts.map +1 -1
- package/dist/src/shared/utils/swmaestro.js +102 -39
- package/dist/src/shared/utils/swmaestro.js.map +1 -1
- package/dist/src/shared/utils/team-action-params.d.ts +3 -0
- package/dist/src/shared/utils/team-action-params.d.ts.map +1 -0
- package/dist/src/shared/utils/team-action-params.js +10 -0
- package/dist/src/shared/utils/team-action-params.js.map +1 -0
- package/dist/src/shared/utils/team-params.d.ts +12 -0
- package/dist/src/shared/utils/team-params.d.ts.map +1 -0
- package/dist/src/shared/utils/team-params.js +38 -0
- package/dist/src/shared/utils/team-params.js.map +1 -0
- package/dist/src/toz-client.d.ts +89 -0
- package/dist/src/toz-client.d.ts.map +1 -0
- package/dist/src/toz-client.js +204 -0
- package/dist/src/toz-client.js.map +1 -0
- package/dist/src/toz-pending-store.d.ts +30 -0
- package/dist/src/toz-pending-store.d.ts.map +1 -0
- package/dist/src/toz-pending-store.js +36 -0
- package/dist/src/toz-pending-store.js.map +1 -0
- package/dist/src/types.d.ts +147 -10
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +74 -6
- package/dist/src/types.js.map +1 -1
- package/package.json +5 -1
- package/src/agent-browser-launcher.test.ts +263 -0
- package/src/agent-browser-launcher.ts +159 -0
- package/src/cli.ts +10 -5
- package/src/client.test.ts +673 -30
- package/src/client.ts +287 -67
- package/src/commands/agent-browser.ts +33 -0
- package/src/commands/auth.test.ts +77 -26
- package/src/commands/auth.ts +5 -3
- package/src/commands/dashboard.test.ts +57 -0
- package/src/commands/dashboard.ts +22 -19
- package/src/commands/helpers.test.ts +76 -25
- package/src/commands/helpers.ts +3 -3
- package/src/commands/index.ts +3 -1
- package/src/commands/mentoring.ts +60 -29
- package/src/commands/notice.ts +2 -1
- package/src/commands/report.ts +4 -2
- package/src/commands/room.ts +160 -1
- package/src/commands/schedule.ts +32 -0
- package/src/commands/team.ts +73 -5
- package/src/commands/toz.test.ts +51 -0
- package/src/commands/toz.ts +607 -0
- package/src/constants.ts +20 -8
- package/src/credential-manager.test.ts +98 -0
- package/src/credential-manager.ts +50 -0
- package/src/formatters.test.ts +528 -33
- package/src/formatters.ts +309 -55
- package/src/http.test.ts +71 -2
- package/src/http.ts +41 -2
- package/src/index.ts +23 -1
- package/src/session-recovery.ts +2 -0
- package/src/shared/utils/swmaestro.test.ts +245 -9
- package/src/shared/utils/swmaestro.ts +150 -47
- package/src/shared/utils/team-action-params.test.ts +32 -0
- package/src/shared/utils/team-action-params.ts +10 -0
- package/src/shared/utils/team-params.test.ts +141 -0
- package/src/shared/utils/team-params.ts +53 -0
- package/src/toz-client.test.ts +243 -0
- package/src/toz-client.ts +311 -0
- package/src/toz-pending-store.test.ts +91 -0
- package/src/toz-pending-store.ts +62 -0
- package/src/types.test.ts +26 -13
- package/src/types.ts +87 -7
- package/dist/src/commands/event.d.ts +0 -3
- package/dist/src/commands/event.d.ts.map +0 -1
- package/dist/src/commands/event.js +0 -58
- package/dist/src/commands/event.js.map +0 -1
- package/src/commands/event.ts +0 -73
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
buildDeleteMentoringPayload,
|
|
12
12
|
buildMentoringPayload,
|
|
13
13
|
buildUpdateMentoringPayload,
|
|
14
|
-
|
|
14
|
+
type ReceiptType,
|
|
15
15
|
} from '../shared/utils/swmaestro'
|
|
16
16
|
import { getHttpOrExit } from './helpers'
|
|
17
17
|
|
|
@@ -32,7 +32,10 @@ type CreateOptions = {
|
|
|
32
32
|
venue: string
|
|
33
33
|
maxAttendees?: string
|
|
34
34
|
regStart?: string
|
|
35
|
+
regStartTime?: string
|
|
35
36
|
regEnd?: string
|
|
37
|
+
regEndTime?: string
|
|
38
|
+
receiptType?: string
|
|
36
39
|
content?: string
|
|
37
40
|
pretty?: boolean
|
|
38
41
|
}
|
|
@@ -45,7 +48,10 @@ type UpdateOptions = {
|
|
|
45
48
|
venue?: string
|
|
46
49
|
maxAttendees?: string
|
|
47
50
|
regStart?: string
|
|
51
|
+
regStartTime?: string
|
|
48
52
|
regEnd?: string
|
|
53
|
+
regEndTime?: string
|
|
54
|
+
receiptType?: string
|
|
49
55
|
content?: string
|
|
50
56
|
pretty?: boolean
|
|
51
57
|
}
|
|
@@ -67,11 +73,12 @@ async function listAction(options: ListOptions): Promise<void> {
|
|
|
67
73
|
user,
|
|
68
74
|
}),
|
|
69
75
|
)
|
|
76
|
+
const items = formatters.parseMentoringList(html)
|
|
70
77
|
console.log(
|
|
71
78
|
formatOutput(
|
|
72
79
|
{
|
|
73
|
-
items
|
|
74
|
-
pagination: formatters.parsePagination(html),
|
|
80
|
+
items,
|
|
81
|
+
pagination: formatters.parsePagination(html, { itemCount: items.length }),
|
|
75
82
|
},
|
|
76
83
|
options.pretty,
|
|
77
84
|
),
|
|
@@ -108,7 +115,10 @@ async function createAction(options: CreateOptions): Promise<void> {
|
|
|
108
115
|
venue: options.venue,
|
|
109
116
|
maxAttendees: options.maxAttendees ? Number.parseInt(options.maxAttendees, 10) : undefined,
|
|
110
117
|
regStart: options.regStart,
|
|
118
|
+
regStartTime: options.regStartTime,
|
|
111
119
|
regEnd: options.regEnd,
|
|
120
|
+
regEndTime: options.regEndTime,
|
|
121
|
+
receiptType: parseReceiptType(options.receiptType),
|
|
112
122
|
content: options.content,
|
|
113
123
|
}),
|
|
114
124
|
)
|
|
@@ -118,29 +128,43 @@ async function createAction(options: CreateOptions): Promise<void> {
|
|
|
118
128
|
}
|
|
119
129
|
}
|
|
120
130
|
|
|
131
|
+
function parseReceiptType(value: string | undefined): ReceiptType | undefined {
|
|
132
|
+
if (!value) return undefined
|
|
133
|
+
const normalized = value.toLowerCase()
|
|
134
|
+
if (normalized === 'direct') return 'DIRECT'
|
|
135
|
+
if (normalized === 'lecture' || normalized === 'until-lecture' || normalized === 'until_lecture')
|
|
136
|
+
return 'UNTIL_LECTURE'
|
|
137
|
+
throw new Error(`Invalid receipt type: ${value}. Expected "lecture" or "direct".`)
|
|
138
|
+
}
|
|
139
|
+
|
|
121
140
|
async function updateAction(id: string, options: UpdateOptions): Promise<void> {
|
|
122
141
|
try {
|
|
123
142
|
const http = await getHttpOrExit()
|
|
124
143
|
const numId = Number.parseInt(id, 10)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
qustnrSn: id,
|
|
128
|
-
|
|
129
|
-
|
|
144
|
+
|
|
145
|
+
const [editHtml, viewHtml] = await Promise.all([
|
|
146
|
+
http.get('/mypage/mentoLec/forUpdate.do', { menuNo: MENU_NO.MENTORING, qustnrSn: id }),
|
|
147
|
+
http.get('/mypage/mentoLec/view.do', { menuNo: MENU_NO.MENTORING, qustnrSn: id }),
|
|
148
|
+
])
|
|
149
|
+
const existing = formatters.parseMentoringEditForm(editHtml, numId)
|
|
150
|
+
const existingContent = formatters.parseMentoringDetail(viewHtml, numId).content
|
|
130
151
|
|
|
131
152
|
await http.postForm(
|
|
132
153
|
'/mypage/mentoLec/update.do',
|
|
133
154
|
buildUpdateMentoringPayload(numId, {
|
|
134
155
|
title: options.title ?? existing.title,
|
|
135
|
-
type: options.type ??
|
|
136
|
-
date: options.date ?? existing.
|
|
137
|
-
startTime: options.start ?? existing.
|
|
138
|
-
endTime: options.end ?? existing.
|
|
139
|
-
venue: options.venue ?? existing.
|
|
140
|
-
maxAttendees: options.maxAttendees ? Number.parseInt(options.maxAttendees, 10) : existing.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
156
|
+
type: options.type ?? (existing.reportCd === 'MRC020' ? 'lecture' : 'public'),
|
|
157
|
+
date: options.date ?? existing.eventDt,
|
|
158
|
+
startTime: options.start ?? existing.eventStime,
|
|
159
|
+
endTime: options.end ?? existing.eventEtime,
|
|
160
|
+
venue: options.venue ?? existing.place,
|
|
161
|
+
maxAttendees: options.maxAttendees ? Number.parseInt(options.maxAttendees, 10) : existing.applyCnt,
|
|
162
|
+
receiptType: parseReceiptType(options.receiptType) ?? existing.receiptType,
|
|
163
|
+
regStart: options.regStart ?? existing.bgndeDate,
|
|
164
|
+
regStartTime: options.regStartTime ?? existing.bgndeTime,
|
|
165
|
+
regEnd: options.regEnd ?? existing.enddeDate,
|
|
166
|
+
regEndTime: options.regEndTime ?? existing.enddeTime,
|
|
167
|
+
content: options.content ?? existingContent,
|
|
144
168
|
}),
|
|
145
169
|
)
|
|
146
170
|
console.log(formatOutput({ ok: true }, options.pretty))
|
|
@@ -192,11 +216,12 @@ async function historyAction(options: HistoryOptions): Promise<void> {
|
|
|
192
216
|
menuNo: MENU_NO.APPLICATION_HISTORY,
|
|
193
217
|
...(options.page ? { pageIndex: options.page } : {}),
|
|
194
218
|
})
|
|
219
|
+
const items = formatters.parseApplicationHistory(html)
|
|
195
220
|
console.log(
|
|
196
221
|
formatOutput(
|
|
197
222
|
{
|
|
198
|
-
items
|
|
199
|
-
pagination: formatters.parsePagination(html),
|
|
223
|
+
items,
|
|
224
|
+
pagination: formatters.parsePagination(html, { itemCount: items.length }),
|
|
200
225
|
},
|
|
201
226
|
options.pretty,
|
|
202
227
|
),
|
|
@@ -231,12 +256,15 @@ export const mentoringCommand = new Command('mentoring')
|
|
|
231
256
|
.requiredOption('--title <title>', 'Title')
|
|
232
257
|
.requiredOption('--type <type>', 'Mentoring type (public|lecture)')
|
|
233
258
|
.requiredOption('--date <date>', 'Session date')
|
|
234
|
-
.requiredOption('--start <time>', 'Start time')
|
|
235
|
-
.requiredOption('--end <time>', 'End time')
|
|
259
|
+
.requiredOption('--start <time>', 'Start time (HH:MM)')
|
|
260
|
+
.requiredOption('--end <time>', 'End time (HH:MM)')
|
|
236
261
|
.requiredOption('--venue <venue>', 'Venue')
|
|
237
|
-
.option('--max-attendees <count>', 'Maximum attendees')
|
|
238
|
-
.option('--
|
|
239
|
-
.option('--reg-
|
|
262
|
+
.option('--max-attendees <count>', 'Maximum attendees (public: 2-5, lecture: 6+)')
|
|
263
|
+
.option('--receipt-type <type>', 'Registration period type (lecture|direct, default: lecture)')
|
|
264
|
+
.option('--reg-start <date>', 'Registration start date (YYYY-MM-DD, default: session date)')
|
|
265
|
+
.option('--reg-start-time <time>', 'Registration start time (HH:MM, default: 00:00)')
|
|
266
|
+
.option('--reg-end <date>', 'Registration end date (YYYY-MM-DD, required when --receipt-type=direct)')
|
|
267
|
+
.option('--reg-end-time <time>', 'Registration end time (HH:MM, required when --receipt-type=direct)')
|
|
240
268
|
.option('--content <html>', 'HTML content')
|
|
241
269
|
.option('--pretty', 'Pretty print JSON output')
|
|
242
270
|
.action(createAction),
|
|
@@ -248,12 +276,15 @@ export const mentoringCommand = new Command('mentoring')
|
|
|
248
276
|
.option('--title <title>', 'Title')
|
|
249
277
|
.option('--type <type>', 'Mentoring type (public|lecture)')
|
|
250
278
|
.option('--date <date>', 'Session date')
|
|
251
|
-
.option('--start <time>', 'Start time')
|
|
252
|
-
.option('--end <time>', 'End time')
|
|
279
|
+
.option('--start <time>', 'Start time (HH:MM)')
|
|
280
|
+
.option('--end <time>', 'End time (HH:MM)')
|
|
253
281
|
.option('--venue <venue>', 'Venue')
|
|
254
|
-
.option('--max-attendees <count>', 'Maximum attendees')
|
|
255
|
-
.option('--
|
|
256
|
-
.option('--reg-
|
|
282
|
+
.option('--max-attendees <count>', 'Maximum attendees (public: 2-5, lecture: 6+)')
|
|
283
|
+
.option('--receipt-type <type>', 'Registration period type (lecture|direct)')
|
|
284
|
+
.option('--reg-start <date>', 'Registration start date (YYYY-MM-DD)')
|
|
285
|
+
.option('--reg-start-time <time>', 'Registration start time (HH:MM)')
|
|
286
|
+
.option('--reg-end <date>', 'Registration end date (YYYY-MM-DD)')
|
|
287
|
+
.option('--reg-end-time <time>', 'Registration end time (HH:MM)')
|
|
257
288
|
.option('--content <html>', 'HTML content')
|
|
258
289
|
.option('--pretty', 'Pretty print JSON output')
|
|
259
290
|
.action(updateAction),
|
package/src/commands/notice.ts
CHANGED
|
@@ -16,9 +16,10 @@ async function listAction(options: ListOptions): Promise<void> {
|
|
|
16
16
|
menuNo: MENU_NO.NOTICE,
|
|
17
17
|
...(options.page ? { pageIndex: options.page } : {}),
|
|
18
18
|
})
|
|
19
|
+
const items = formatters.parseNoticeList(html)
|
|
19
20
|
console.log(
|
|
20
21
|
formatOutput(
|
|
21
|
-
{ items: formatters.
|
|
22
|
+
{ items, pagination: formatters.parsePagination(html, { itemCount: items.length }) },
|
|
22
23
|
options.pretty,
|
|
23
24
|
),
|
|
24
25
|
)
|
package/src/commands/report.ts
CHANGED
|
@@ -79,9 +79,10 @@ async function listAction(options: ListOptions): Promise<void> {
|
|
|
79
79
|
...(options.search ? { searchWrd: options.search } : {}),
|
|
80
80
|
})
|
|
81
81
|
|
|
82
|
+
const items = formatters.parseReportList(html)
|
|
82
83
|
console.log(
|
|
83
84
|
formatOutput(
|
|
84
|
-
{ items: formatters.
|
|
85
|
+
{ items, pagination: formatters.parsePagination(html, { itemCount: items.length }) },
|
|
85
86
|
options.pretty,
|
|
86
87
|
),
|
|
87
88
|
)
|
|
@@ -114,9 +115,10 @@ async function approvalAction(options: ApprovalOptions): Promise<void> {
|
|
|
114
115
|
...(options.type ? { searchReport: options.type } : {}),
|
|
115
116
|
})
|
|
116
117
|
|
|
118
|
+
const items = formatters.parseApprovalList(html)
|
|
117
119
|
console.log(
|
|
118
120
|
formatOutput(
|
|
119
|
-
{ items: formatters.
|
|
121
|
+
{ items, pagination: formatters.parsePagination(html, { itemCount: items.length }) },
|
|
120
122
|
options.pretty,
|
|
121
123
|
),
|
|
122
124
|
)
|
package/src/commands/room.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { Command } from 'commander'
|
|
2
2
|
|
|
3
|
+
import { MENU_NO } from '../constants'
|
|
3
4
|
import * as formatters from '../formatters'
|
|
4
5
|
import { handleError } from '../shared/utils/error-handler'
|
|
5
6
|
import { formatOutput } from '../shared/utils/output'
|
|
6
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
buildRoomCancelPayload,
|
|
9
|
+
buildRoomReservationPayload,
|
|
10
|
+
buildRoomUpdatePayload,
|
|
11
|
+
resolveRoomId,
|
|
12
|
+
} from '../shared/utils/swmaestro'
|
|
7
13
|
import { getHttpOrExit } from './helpers'
|
|
8
14
|
|
|
9
15
|
type ListOptions = { date?: string; room?: string; reservations?: boolean; pretty?: boolean }
|
|
@@ -17,6 +23,26 @@ type ReserveOptions = {
|
|
|
17
23
|
notes?: string
|
|
18
24
|
pretty?: boolean
|
|
19
25
|
}
|
|
26
|
+
type GetOptions = { pretty?: boolean }
|
|
27
|
+
type UpdateOptions = {
|
|
28
|
+
title?: string
|
|
29
|
+
room?: string
|
|
30
|
+
date?: string
|
|
31
|
+
slots?: string
|
|
32
|
+
attendees?: string
|
|
33
|
+
notes?: string
|
|
34
|
+
pretty?: boolean
|
|
35
|
+
}
|
|
36
|
+
type CancelOptions = { pretty?: boolean }
|
|
37
|
+
type ReservationsOptions = {
|
|
38
|
+
status?: string
|
|
39
|
+
startDate?: string
|
|
40
|
+
endDate?: string
|
|
41
|
+
page?: string
|
|
42
|
+
pretty?: boolean
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const ROOM_UPDATE_SUCCESS_PATTERN = /정상적으로|수정하였습니다|수정되었습니다|저장되었습니다|취소되었습니다/
|
|
20
46
|
|
|
21
47
|
async function listAction(options: ListOptions): Promise<void> {
|
|
22
48
|
try {
|
|
@@ -59,6 +85,35 @@ async function listAction(options: ListOptions): Promise<void> {
|
|
|
59
85
|
}
|
|
60
86
|
}
|
|
61
87
|
|
|
88
|
+
async function reservationsAction(options: ReservationsOptions): Promise<void> {
|
|
89
|
+
try {
|
|
90
|
+
const status = options.status ?? 'confirmed'
|
|
91
|
+
if (status !== 'confirmed' && status !== 'cancelled' && status !== 'all') {
|
|
92
|
+
throw new Error(`Invalid --status value: ${status}. Use 'confirmed', 'cancelled', or 'all'.`)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const http = await getHttpOrExit()
|
|
96
|
+
const params: Record<string, string> = {
|
|
97
|
+
menuNo: MENU_NO.ROOM,
|
|
98
|
+
pageIndex: options.page ?? '1',
|
|
99
|
+
}
|
|
100
|
+
if (options.startDate) params.sdate = options.startDate
|
|
101
|
+
if (options.endDate) params.edate = options.endDate
|
|
102
|
+
if (status === 'confirmed') params.searchStat = 'RS001'
|
|
103
|
+
if (status === 'cancelled') params.searchStat = 'RS002'
|
|
104
|
+
|
|
105
|
+
const html = await http.get('/mypage/itemRent/list.do', params)
|
|
106
|
+
const items = formatters.parseRoomReservationList(html)
|
|
107
|
+
const result = {
|
|
108
|
+
items,
|
|
109
|
+
pagination: formatters.parsePagination(html, { itemCount: items.length }),
|
|
110
|
+
}
|
|
111
|
+
console.log(formatOutput(result, options.pretty))
|
|
112
|
+
} catch (error) {
|
|
113
|
+
handleError(error)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
62
117
|
async function availableAction(roomId: string, options: AvailableOptions): Promise<void> {
|
|
63
118
|
try {
|
|
64
119
|
const http = await getHttpOrExit()
|
|
@@ -96,6 +151,73 @@ async function reserveAction(options: ReserveOptions): Promise<void> {
|
|
|
96
151
|
}
|
|
97
152
|
}
|
|
98
153
|
|
|
154
|
+
async function fetchReservationDetail(http: Awaited<ReturnType<typeof getHttpOrExit>>, rentId: number) {
|
|
155
|
+
return formatters.parseRoomReservationDetail(
|
|
156
|
+
await http.get('/mypage/itemRent/view.do', { menuNo: MENU_NO.ROOM, rentId: String(rentId) }),
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async function postRoomMutation(
|
|
161
|
+
http: Awaited<ReturnType<typeof getHttpOrExit>>,
|
|
162
|
+
payload: Record<string, string>,
|
|
163
|
+
): Promise<void> {
|
|
164
|
+
try {
|
|
165
|
+
await http.post('/mypage/itemRent/update.do', payload)
|
|
166
|
+
} catch (error) {
|
|
167
|
+
if (error instanceof Error && ROOM_UPDATE_SUCCESS_PATTERN.test(error.message)) {
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
throw error
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function getAction(rentIdArg: string, options: GetOptions): Promise<void> {
|
|
175
|
+
try {
|
|
176
|
+
const http = await getHttpOrExit()
|
|
177
|
+
const rentId = Number.parseInt(rentIdArg, 10)
|
|
178
|
+
const detail = await fetchReservationDetail(http, rentId)
|
|
179
|
+
console.log(formatOutput(detail, options.pretty))
|
|
180
|
+
} catch (error) {
|
|
181
|
+
handleError(error)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async function updateAction(rentIdArg: string, options: UpdateOptions): Promise<void> {
|
|
186
|
+
try {
|
|
187
|
+
const http = await getHttpOrExit()
|
|
188
|
+
const rentId = Number.parseInt(rentIdArg, 10)
|
|
189
|
+
const existing = await fetchReservationDetail(http, rentId)
|
|
190
|
+
const slots = options.slots
|
|
191
|
+
?.split(',')
|
|
192
|
+
.map((slot) => slot.trim())
|
|
193
|
+
.filter(Boolean)
|
|
194
|
+
const payload = buildRoomUpdatePayload(existing, {
|
|
195
|
+
title: options.title,
|
|
196
|
+
roomId: options.room ? resolveRoomId(options.room) : undefined,
|
|
197
|
+
date: options.date,
|
|
198
|
+
slots: slots?.length ? slots : undefined,
|
|
199
|
+
attendees: options.attendees ? Number.parseInt(options.attendees, 10) : undefined,
|
|
200
|
+
notes: options.notes,
|
|
201
|
+
})
|
|
202
|
+
await postRoomMutation(http, payload)
|
|
203
|
+
console.log(formatOutput({ ok: true, rentId }, options.pretty))
|
|
204
|
+
} catch (error) {
|
|
205
|
+
handleError(error)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async function cancelAction(rentIdArg: string, options: CancelOptions): Promise<void> {
|
|
210
|
+
try {
|
|
211
|
+
const http = await getHttpOrExit()
|
|
212
|
+
const rentId = Number.parseInt(rentIdArg, 10)
|
|
213
|
+
const existing = await fetchReservationDetail(http, rentId)
|
|
214
|
+
await postRoomMutation(http, buildRoomCancelPayload(existing))
|
|
215
|
+
console.log(formatOutput({ ok: true, rentId }, options.pretty))
|
|
216
|
+
} catch (error) {
|
|
217
|
+
handleError(error)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
99
221
|
export const roomCommand = new Command('room')
|
|
100
222
|
.description('Manage room reservations')
|
|
101
223
|
.addCommand(
|
|
@@ -127,3 +249,40 @@ export const roomCommand = new Command('room')
|
|
|
127
249
|
.option('--pretty', 'Pretty print JSON output')
|
|
128
250
|
.action(reserveAction),
|
|
129
251
|
)
|
|
252
|
+
.addCommand(
|
|
253
|
+
new Command('get')
|
|
254
|
+
.description('Show a single reservation by rentId')
|
|
255
|
+
.argument('<rentId>', 'Reservation ID returned from view.do')
|
|
256
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
257
|
+
.action(getAction),
|
|
258
|
+
)
|
|
259
|
+
.addCommand(
|
|
260
|
+
new Command('update')
|
|
261
|
+
.description('Update an existing reservation (any subset of fields)')
|
|
262
|
+
.argument('<rentId>', 'Reservation ID returned from view.do')
|
|
263
|
+
.option('--title <title>', 'New title')
|
|
264
|
+
.option('--room <room>', 'New room ID or short name')
|
|
265
|
+
.option('--date <date>', 'New reservation date (YYYY-MM-DD)')
|
|
266
|
+
.option('--slots <slots>', 'New comma-separated HH:MM values')
|
|
267
|
+
.option('--attendees <count>', 'New number of attendees')
|
|
268
|
+
.option('--notes <notes>', 'New notes')
|
|
269
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
270
|
+
.action(updateAction),
|
|
271
|
+
)
|
|
272
|
+
.addCommand(
|
|
273
|
+
new Command('cancel')
|
|
274
|
+
.description('Cancel an existing reservation')
|
|
275
|
+
.argument('<rentId>', 'Reservation ID returned from view.do')
|
|
276
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
277
|
+
.action(cancelAction),
|
|
278
|
+
)
|
|
279
|
+
.addCommand(
|
|
280
|
+
new Command('reservations')
|
|
281
|
+
.description("List the user's room reservations")
|
|
282
|
+
.option('--status <status>', "Filter by status: 'confirmed' (default), 'cancelled', or 'all'")
|
|
283
|
+
.option('--start-date <date>', 'Earliest reservation date (YYYY-MM-DD)')
|
|
284
|
+
.option('--end-date <date>', 'Latest reservation date (YYYY-MM-DD)')
|
|
285
|
+
.option('--page <page>', 'Page number', '1')
|
|
286
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
287
|
+
.action(reservationsAction),
|
|
288
|
+
)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
|
|
3
|
+
import { MENU_NO } from '../constants'
|
|
4
|
+
import * as formatters from '../formatters'
|
|
5
|
+
import { handleError } from '../shared/utils/error-handler'
|
|
6
|
+
import { formatOutput } from '../shared/utils/output'
|
|
7
|
+
import { getHttpOrExit } from './helpers'
|
|
8
|
+
|
|
9
|
+
type ListOptions = { page?: string; pretty?: boolean }
|
|
10
|
+
|
|
11
|
+
async function listAction(options: ListOptions): Promise<void> {
|
|
12
|
+
try {
|
|
13
|
+
const http = await getHttpOrExit()
|
|
14
|
+
const html = await http.get('/mypage/schedule/list.do', {
|
|
15
|
+
menuNo: MENU_NO.SCHEDULE,
|
|
16
|
+
...(options.page ? { pageIndex: options.page } : {}),
|
|
17
|
+
})
|
|
18
|
+
console.log(formatOutput(formatters.parseScheduleList(html), options.pretty))
|
|
19
|
+
} catch (error) {
|
|
20
|
+
handleError(error)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const scheduleCommand = new Command('schedule')
|
|
25
|
+
.description('Browse monthly schedules')
|
|
26
|
+
.addCommand(
|
|
27
|
+
new Command('list')
|
|
28
|
+
.description('List monthly schedules')
|
|
29
|
+
.option('--page <n>', 'Page number')
|
|
30
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
31
|
+
.action(listAction),
|
|
32
|
+
)
|
package/src/commands/team.ts
CHANGED
|
@@ -1,25 +1,93 @@
|
|
|
1
1
|
import { Command } from 'commander'
|
|
2
2
|
|
|
3
|
-
import { MENU_NO } from '../constants'
|
|
4
3
|
import * as formatters from '../formatters'
|
|
5
4
|
import { handleError } from '../shared/utils/error-handler'
|
|
6
5
|
import { formatOutput } from '../shared/utils/output'
|
|
6
|
+
import { buildTeamActionPayload } from '../shared/utils/team-action-params'
|
|
7
|
+
import { buildTeamListParams, parseTeamSearchQuery } from '../shared/utils/team-params'
|
|
7
8
|
import { getHttpOrExit } from './helpers'
|
|
8
9
|
|
|
9
|
-
type
|
|
10
|
+
type ListOptions = { search?: string; pretty?: boolean }
|
|
11
|
+
type ActionOptions = { pretty?: boolean }
|
|
10
12
|
|
|
11
|
-
async function
|
|
13
|
+
async function listAction(options: ListOptions): Promise<void> {
|
|
12
14
|
try {
|
|
13
15
|
const http = await getHttpOrExit()
|
|
14
|
-
const
|
|
16
|
+
const search = options.search ? parseTeamSearchQuery(options.search) : undefined
|
|
17
|
+
const user = search?.me ? ((await http.checkLogin()) ?? undefined) : undefined
|
|
18
|
+
const html = await http.get('/mypage/myTeam/team.do', buildTeamListParams({ search, user }))
|
|
15
19
|
console.log(formatOutput(formatters.parseTeamInfo(html), options.pretty))
|
|
16
20
|
} catch (error) {
|
|
17
21
|
handleError(error)
|
|
18
22
|
}
|
|
19
23
|
}
|
|
20
24
|
|
|
25
|
+
async function joinAction(teamId: string, options: ActionOptions): Promise<void> {
|
|
26
|
+
await runTeamAction({
|
|
27
|
+
teamId,
|
|
28
|
+
path: '/mypage/myTeam/updateUserTeamIn.json',
|
|
29
|
+
failureMessage: '팀 참여에 실패했습니다.',
|
|
30
|
+
pretty: options.pretty,
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function leaveAction(teamId: string, options: ActionOptions): Promise<void> {
|
|
35
|
+
await runTeamAction({
|
|
36
|
+
teamId,
|
|
37
|
+
path: '/mypage/myTeam/updateUserTeamOut.json',
|
|
38
|
+
failureMessage: '팀 탈퇴에 실패했습니다.',
|
|
39
|
+
pretty: options.pretty,
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function runTeamAction(params: {
|
|
44
|
+
teamId: string
|
|
45
|
+
path: string
|
|
46
|
+
failureMessage: string
|
|
47
|
+
pretty?: boolean
|
|
48
|
+
}): Promise<void> {
|
|
49
|
+
try {
|
|
50
|
+
const http = await getHttpOrExit()
|
|
51
|
+
const user = await http.checkLogin()
|
|
52
|
+
if (!user) throw new Error('Not logged in. Run: opensoma auth login or opensoma auth extract')
|
|
53
|
+
if (!user.userNo) throw new Error('현재 사용자의 userNo를 확인할 수 없습니다.')
|
|
54
|
+
|
|
55
|
+
const response = await http.postJson<{ resultCode?: string }>(
|
|
56
|
+
params.path,
|
|
57
|
+
buildTeamActionPayload(params.teamId, user),
|
|
58
|
+
)
|
|
59
|
+
if (response.resultCode !== 'success') {
|
|
60
|
+
throw new Error(params.failureMessage)
|
|
61
|
+
}
|
|
62
|
+
console.log(formatOutput({ ok: true }, params.pretty))
|
|
63
|
+
} catch (error) {
|
|
64
|
+
handleError(error)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
21
68
|
export const teamCommand = new Command('team')
|
|
22
69
|
.description('Show team information')
|
|
23
70
|
.addCommand(
|
|
24
|
-
new Command('
|
|
71
|
+
new Command('list')
|
|
72
|
+
.description('List teams')
|
|
73
|
+
.option(
|
|
74
|
+
'--search <query>',
|
|
75
|
+
'Search (e.g. "keyword", "team:오픈소마", "mentor:@me", "member:@me", "project:Previzion")',
|
|
76
|
+
)
|
|
77
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
78
|
+
.action(listAction),
|
|
79
|
+
)
|
|
80
|
+
.addCommand(
|
|
81
|
+
new Command('join')
|
|
82
|
+
.description('Join a team')
|
|
83
|
+
.argument('<teamId>')
|
|
84
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
85
|
+
.action(joinAction),
|
|
86
|
+
)
|
|
87
|
+
.addCommand(
|
|
88
|
+
new Command('leave')
|
|
89
|
+
.description('Leave a team')
|
|
90
|
+
.argument('<teamId>')
|
|
91
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
92
|
+
.action(leaveAction),
|
|
25
93
|
)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
import { resolveTozIdentity, resolveTozPin } from './toz'
|
|
4
|
+
|
|
5
|
+
describe('resolveTozIdentity', () => {
|
|
6
|
+
it('uses flag values before stored identity', async () => {
|
|
7
|
+
const identity = await resolveTozIdentity('Mentor One', '010-1111-2222', {
|
|
8
|
+
promptPhone: async () => '010-3333-4444',
|
|
9
|
+
store: {
|
|
10
|
+
getTozIdentity: async () => ({ name: 'Mentor Two', phone: '010-5555-6666' }),
|
|
11
|
+
},
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
expect(identity).toEqual({ name: 'Mentor One', phone: '010-1111-2222' })
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('prompts for phone when name is available but phone is missing', async () => {
|
|
18
|
+
const identity = await resolveTozIdentity('Mentor One', undefined, {
|
|
19
|
+
promptPhone: async () => '010-3333-4444',
|
|
20
|
+
store: {
|
|
21
|
+
getTozIdentity: async () => null,
|
|
22
|
+
},
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
expect(identity).toEqual({ name: 'Mentor One', phone: '010-3333-4444' })
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('keeps non-interactive callers failing when phone is missing', async () => {
|
|
29
|
+
await expect(
|
|
30
|
+
resolveTozIdentity('Mentor One', undefined, {
|
|
31
|
+
store: {
|
|
32
|
+
getTozIdentity: async () => null,
|
|
33
|
+
},
|
|
34
|
+
}),
|
|
35
|
+
).rejects.toThrow(/Toz identity not set/)
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
describe('resolveTozPin', () => {
|
|
40
|
+
it('uses the flag value before prompting', async () => {
|
|
41
|
+
const pin = await resolveTozPin('123456', async () => '654321')
|
|
42
|
+
|
|
43
|
+
expect(pin).toBe('123456')
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('prompts when the flag value is missing', async () => {
|
|
47
|
+
const pin = await resolveTozPin(undefined, async () => '654321')
|
|
48
|
+
|
|
49
|
+
expect(pin).toBe('654321')
|
|
50
|
+
})
|
|
51
|
+
})
|