opensoma 0.2.0 → 0.3.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.
Files changed (87) hide show
  1. package/dist/package.json +2 -2
  2. package/dist/src/cli.d.ts.map +1 -1
  3. package/dist/src/cli.js +2 -1
  4. package/dist/src/cli.js.map +1 -1
  5. package/dist/src/client.d.ts +26 -1
  6. package/dist/src/client.d.ts.map +1 -1
  7. package/dist/src/client.js +141 -3
  8. package/dist/src/client.js.map +1 -1
  9. package/dist/src/commands/auth.d.ts +12 -0
  10. package/dist/src/commands/auth.d.ts.map +1 -1
  11. package/dist/src/commands/auth.js +151 -23
  12. package/dist/src/commands/auth.js.map +1 -1
  13. package/dist/src/commands/helpers.d.ts +12 -0
  14. package/dist/src/commands/helpers.d.ts.map +1 -1
  15. package/dist/src/commands/helpers.js +55 -9
  16. package/dist/src/commands/helpers.js.map +1 -1
  17. package/dist/src/commands/index.d.ts +1 -0
  18. package/dist/src/commands/index.d.ts.map +1 -1
  19. package/dist/src/commands/index.js +1 -0
  20. package/dist/src/commands/index.js.map +1 -1
  21. package/dist/src/commands/mentoring.d.ts.map +1 -1
  22. package/dist/src/commands/mentoring.js +37 -1
  23. package/dist/src/commands/mentoring.js.map +1 -1
  24. package/dist/src/commands/report.d.ts +27 -0
  25. package/dist/src/commands/report.d.ts.map +1 -0
  26. package/dist/src/commands/report.js +224 -0
  27. package/dist/src/commands/report.js.map +1 -0
  28. package/dist/src/constants.d.ts +2 -0
  29. package/dist/src/constants.d.ts.map +1 -1
  30. package/dist/src/constants.js +2 -0
  31. package/dist/src/constants.js.map +1 -1
  32. package/dist/src/credential-manager.d.ts +7 -0
  33. package/dist/src/credential-manager.d.ts.map +1 -1
  34. package/dist/src/credential-manager.js +76 -2
  35. package/dist/src/credential-manager.js.map +1 -1
  36. package/dist/src/formatters.d.ts +4 -1
  37. package/dist/src/formatters.d.ts.map +1 -1
  38. package/dist/src/formatters.js +91 -1
  39. package/dist/src/formatters.js.map +1 -1
  40. package/dist/src/http.d.ts +3 -0
  41. package/dist/src/http.d.ts.map +1 -1
  42. package/dist/src/http.js +109 -4
  43. package/dist/src/http.js.map +1 -1
  44. package/dist/src/session-recovery.d.ts +12 -0
  45. package/dist/src/session-recovery.d.ts.map +1 -0
  46. package/dist/src/session-recovery.js +34 -0
  47. package/dist/src/session-recovery.js.map +1 -0
  48. package/dist/src/shared/utils/report-params.d.ts +11 -0
  49. package/dist/src/shared/utils/report-params.d.ts.map +1 -0
  50. package/dist/src/shared/utils/report-params.js +27 -0
  51. package/dist/src/shared/utils/report-params.js.map +1 -0
  52. package/dist/src/shared/utils/swmaestro.d.ts +24 -0
  53. package/dist/src/shared/utils/swmaestro.d.ts.map +1 -1
  54. package/dist/src/shared/utils/swmaestro.js +50 -2
  55. package/dist/src/shared/utils/swmaestro.js.map +1 -1
  56. package/dist/src/token-extractor.d.ts.map +1 -1
  57. package/dist/src/token-extractor.js +38 -8
  58. package/dist/src/token-extractor.js.map +1 -1
  59. package/dist/src/types.d.ts +121 -0
  60. package/dist/src/types.d.ts.map +1 -1
  61. package/dist/src/types.js +72 -0
  62. package/dist/src/types.js.map +1 -1
  63. package/package.json +2 -2
  64. package/src/cli.ts +2 -0
  65. package/src/client.test.ts +95 -6
  66. package/src/client.ts +172 -4
  67. package/src/commands/auth.test.ts +152 -1
  68. package/src/commands/auth.ts +174 -28
  69. package/src/commands/helpers.test.ts +216 -0
  70. package/src/commands/helpers.ts +77 -12
  71. package/src/commands/index.ts +1 -0
  72. package/src/commands/mentoring.ts +55 -0
  73. package/src/commands/report.test.ts +49 -0
  74. package/src/commands/report.ts +322 -0
  75. package/src/constants.ts +2 -0
  76. package/src/credential-manager.test.ts +41 -1
  77. package/src/credential-manager.ts +103 -2
  78. package/src/formatters.test.ts +287 -0
  79. package/src/formatters.ts +105 -0
  80. package/src/http.test.ts +190 -4
  81. package/src/http.ts +132 -4
  82. package/src/session-recovery.ts +56 -0
  83. package/src/shared/utils/report-params.ts +41 -0
  84. package/src/shared/utils/swmaestro.ts +77 -5
  85. package/src/token-extractor.ts +59 -20
  86. package/src/types.test.ts +97 -0
  87. package/src/types.ts +84 -0
@@ -22,9 +22,9 @@ type BrowserConfig = {
22
22
  }
23
23
 
24
24
  type CookieRow = {
25
- encrypted_value: Uint8Array | Buffer | null
25
+ encrypted_value: ArrayBuffer | Uint8Array | Buffer | null
26
26
  last_access_utc?: number | bigint | null
27
- value: string | null
27
+ value: ArrayBuffer | Uint8Array | Buffer | string | null
28
28
  }
29
29
 
30
30
  export interface ExtractedSessionCandidate {
@@ -92,6 +92,20 @@ function queryCookieDb(dbPath: string): CookieRow | undefined {
92
92
  }
93
93
 
94
94
  try {
95
+ const Database = require('better-sqlite3') as new (
96
+ path: string,
97
+ options?: { readonly?: boolean },
98
+ ) => {
99
+ close: () => void
100
+ prepare: (query: string) => { get: () => CookieRow | undefined }
101
+ }
102
+ const db = new Database(dbPath, { readonly: true })
103
+ try {
104
+ return db.prepare(COOKIE_QUERY).get() ?? undefined
105
+ } finally {
106
+ db.close()
107
+ }
108
+ } catch {
95
109
  const { DatabaseSync } = require('node:sqlite') as {
96
110
  DatabaseSync: new (
97
111
  path: string,
@@ -107,20 +121,6 @@ function queryCookieDb(dbPath: string): CookieRow | undefined {
107
121
  } finally {
108
122
  db.close()
109
123
  }
110
- } catch {
111
- const Database = require('better-sqlite3') as new (
112
- path: string,
113
- options?: { readonly?: boolean },
114
- ) => {
115
- close: () => void
116
- prepare: (query: string) => { get: () => CookieRow | undefined }
117
- }
118
- const db = new Database(dbPath, { readonly: true })
119
- try {
120
- return db.prepare(COOKIE_QUERY).get() ?? undefined
121
- } finally {
122
- db.close()
123
- }
124
124
  }
125
125
  }
126
126
 
@@ -156,14 +156,14 @@ export class TokenExtractor {
156
156
  const tempDatabasePath = join(tempDirectory, 'Cookies')
157
157
 
158
158
  try {
159
- copyFileSync(databasePath, tempDatabasePath)
159
+ copySqliteDatabase(databasePath, tempDatabasePath)
160
160
 
161
161
  const row = queryCookieDb(tempDatabasePath)
162
162
  if (!row) {
163
163
  continue
164
164
  }
165
165
 
166
- const plaintextValue = typeof row.value === 'string' ? row.value.trim() : ''
166
+ const plaintextValue = normalizeCookieText(row.value)
167
167
  if (plaintextValue) {
168
168
  this.addCandidate(candidates, {
169
169
  browser: browser.name,
@@ -174,11 +174,12 @@ export class TokenExtractor {
174
174
  continue
175
175
  }
176
176
 
177
- if (!row.encrypted_value || row.encrypted_value.length === 0) {
177
+ const encryptedValue = normalizeCookieBytes(row.encrypted_value)
178
+ if (!encryptedValue || encryptedValue.length === 0) {
178
179
  continue
179
180
  }
180
181
 
181
- const decryptedValue = await this.decryptCookie(Buffer.from(row.encrypted_value), browser.name)
182
+ const decryptedValue = await this.decryptCookie(encryptedValue, browser.name)
182
183
  if (decryptedValue) {
183
184
  this.addCandidate(candidates, {
184
185
  browser: browser.name,
@@ -299,3 +300,41 @@ export class TokenExtractor {
299
300
  return typeof lastAccessUtc === 'number' ? lastAccessUtc : 0
300
301
  }
301
302
  }
303
+
304
+ function copySqliteDatabase(sourcePath: string, targetPath: string): void {
305
+ copyFileSync(sourcePath, targetPath)
306
+
307
+ for (const suffix of ['-wal', '-shm']) {
308
+ const sidecarSourcePath = `${sourcePath}${suffix}`
309
+ if (!existsSync(sidecarSourcePath)) {
310
+ continue
311
+ }
312
+
313
+ copyFileSync(sidecarSourcePath, `${targetPath}${suffix}`)
314
+ }
315
+ }
316
+
317
+ function normalizeCookieBytes(value: ArrayBuffer | Uint8Array | Buffer | null): Buffer | null {
318
+ if (!value) {
319
+ return null
320
+ }
321
+
322
+ if (Buffer.isBuffer(value)) {
323
+ return value
324
+ }
325
+
326
+ if (value instanceof Uint8Array) {
327
+ return Buffer.from(value)
328
+ }
329
+
330
+ return Buffer.from(value)
331
+ }
332
+
333
+ function normalizeCookieText(value: ArrayBuffer | Uint8Array | Buffer | string | null): string {
334
+ if (typeof value === 'string') {
335
+ return value.trim()
336
+ }
337
+
338
+ const bytes = normalizeCookieBytes(value)
339
+ return bytes ? bytes.toString('utf8').trim() : ''
340
+ }
package/src/types.test.ts CHANGED
@@ -2,6 +2,7 @@ import { describe, expect, test } from 'bun:test'
2
2
 
3
3
  import {
4
4
  ApplicationHistoryItemSchema,
5
+ ApprovalListItemSchema,
5
6
  CredentialsSchema,
6
7
  DashboardSchema,
7
8
  EventListItemSchema,
@@ -11,6 +12,9 @@ import {
11
12
  NoticeDetailSchema,
12
13
  NoticeListItemSchema,
13
14
  PaginationSchema,
15
+ ReportCreateOptionsSchema,
16
+ ReportDetailSchema,
17
+ ReportListItemSchema,
14
18
  RoomCardSchema,
15
19
  TeamInfoSchema,
16
20
  } from './types'
@@ -48,6 +52,7 @@ describe('schemas', () => {
48
52
  createdAt: '2026-04-01',
49
53
  content: '<p>내용</p>',
50
54
  venue: '온라인(Webex)',
55
+ applicants: [],
51
56
  }),
52
57
  ).toBeDefined()
53
58
 
@@ -153,6 +158,82 @@ describe('schemas', () => {
153
158
  loggedInAt: '2026-04-09T00:00:00.000Z',
154
159
  }),
155
160
  ).toBeDefined()
161
+
162
+ expect(
163
+ ReportListItemSchema.parse({
164
+ id: 1,
165
+ category: '멘토링',
166
+ title: 'AI 멘토링 보고서',
167
+ progressDate: '2026-04-10',
168
+ status: '승인완료',
169
+ author: '전수열',
170
+ createdAt: '2026-04-11',
171
+ acceptedTime: '2시간',
172
+ payAmount: '100000',
173
+ }),
174
+ ).toBeDefined()
175
+
176
+ expect(
177
+ ReportDetailSchema.parse({
178
+ id: 1,
179
+ category: '멘토링',
180
+ title: 'AI 멘토링 보고서',
181
+ progressDate: '2026-04-10',
182
+ status: '승인완료',
183
+ author: '전수열',
184
+ createdAt: '2026-04-11',
185
+ acceptedTime: '2시간',
186
+ payAmount: '100000',
187
+ content: '멘토링 진행 내용',
188
+ subject: 'AI 기술 멘토링',
189
+ menteeRegion: 'S',
190
+ reportType: 'MRC010',
191
+ teamNames: '오픈소마',
192
+ venue: '스페이스 A1',
193
+ attendanceCount: 4,
194
+ attendanceNames: '김개발, 박코딩, 이알고, 최리즘',
195
+ progressStartTime: '14:00',
196
+ progressEndTime: '16:00',
197
+ exceptStartTime: '',
198
+ exceptEndTime: '',
199
+ exceptReason: '',
200
+ mentorOpinion: '우수',
201
+ nonAttendanceNames: '',
202
+ etc: '',
203
+ files: [],
204
+ }),
205
+ ).toBeDefined()
206
+
207
+ expect(
208
+ ApprovalListItemSchema.parse({
209
+ id: 1,
210
+ category: '멘토링',
211
+ title: 'AI 멘토링 보고서',
212
+ progressDate: '2026-04-10',
213
+ status: '승인완료',
214
+ author: '전수열',
215
+ createdAt: '2026-04-11',
216
+ acceptedTime: '2시간',
217
+ travelExpense: '50000',
218
+ mentoringAllowance: '100000',
219
+ }),
220
+ ).toBeDefined()
221
+
222
+ expect(
223
+ ReportCreateOptionsSchema.parse({
224
+ menteeRegion: 'S',
225
+ reportType: 'MRC010',
226
+ progressDate: '2026-04-10',
227
+ venue: '스페이스 A1',
228
+ attendanceCount: 4,
229
+ attendanceNames: '김개발, 박코딩, 이알고, 최리즘',
230
+ progressStartTime: '14:00',
231
+ progressEndTime: '16:00',
232
+ subject: 'AI 멘토링 진행 보고',
233
+ content:
234
+ '이번 멘토링에서는 AI 기술의 기초 개념과 실제 적용 사례에 대해 다루었습니다. 참가자들은 머신러닝의 기본 원리와 딥러닝의 구조에 대해 학습하고, 실습을 통해 모델을 구현해보았습니다.',
235
+ }),
236
+ ).toBeDefined()
156
237
  })
157
238
 
158
239
  test('reject invalid values', () => {
@@ -170,5 +251,21 @@ describe('schemas', () => {
170
251
  expect(() => ApplicationHistoryItemSchema.parse({ id: 1, status: '신청완료' })).toThrow()
171
252
  expect(() => PaginationSchema.parse({ total: '23', currentPage: 2, totalPages: 3 })).toThrow()
172
253
  expect(() => CredentialsSchema.parse({ sessionCookie: 'cookie' })).toThrow()
254
+ expect(() => ReportListItemSchema.parse({ id: 1, category: '멘토링' })).toThrow()
255
+ expect(() => ReportDetailSchema.parse({ id: 1, category: '멘토링', title: '보고서' })).toThrow()
256
+ expect(() =>
257
+ ReportCreateOptionsSchema.parse({
258
+ menteeRegion: 'S',
259
+ reportType: 'MRC010',
260
+ progressDate: '2026-04-10',
261
+ venue: '스페이스 A1',
262
+ attendanceCount: 4,
263
+ attendanceNames: '김개발',
264
+ progressStartTime: '14:00',
265
+ progressEndTime: '16:00',
266
+ subject: '짧음',
267
+ content: '짧은 내용',
268
+ }),
269
+ ).toThrow()
173
270
  })
174
271
  })
package/src/types.ts CHANGED
@@ -34,9 +34,18 @@ export const MentoringListItemSchema = z.object({
34
34
  })
35
35
  export type MentoringListItem = z.infer<typeof MentoringListItemSchema>
36
36
 
37
+ export const MentoringApplicantSchema = z.object({
38
+ name: z.string(),
39
+ appliedAt: z.string(),
40
+ cancelledAt: z.string(),
41
+ status: z.string(),
42
+ })
43
+ export type MentoringApplicant = z.infer<typeof MentoringApplicantSchema>
44
+
37
45
  export const MentoringDetailSchema = MentoringListItemSchema.extend({
38
46
  content: z.string(),
39
47
  venue: z.string(),
48
+ applicants: z.array(MentoringApplicantSchema),
40
49
  })
41
50
  export type MentoringDetail = z.infer<typeof MentoringDetailSchema>
42
51
 
@@ -135,6 +144,81 @@ export const CredentialsSchema = z.object({
135
144
  cookies: z.string().optional(),
136
145
  csrfToken: z.string(),
137
146
  username: z.string().optional(),
147
+ password: z.string().optional(),
138
148
  loggedInAt: z.string().optional(),
139
149
  })
140
150
  export type Credentials = z.infer<typeof CredentialsSchema>
151
+
152
+ export const ReportListItemSchema = z.object({
153
+ id: z.number(),
154
+ category: z.string(),
155
+ title: z.string(),
156
+ progressDate: z.string(),
157
+ status: z.string(),
158
+ author: z.string(),
159
+ createdAt: z.string(),
160
+ acceptedTime: z.string(),
161
+ payAmount: z.string(),
162
+ })
163
+ export type ReportListItem = z.infer<typeof ReportListItemSchema>
164
+
165
+ export const ReportDetailSchema = ReportListItemSchema.extend({
166
+ content: z.string(),
167
+ subject: z.string(),
168
+ menteeRegion: z.string(),
169
+ reportType: z.string(),
170
+ teamNames: z.string(),
171
+ venue: z.string(),
172
+ attendanceCount: z.number(),
173
+ attendanceNames: z.string(),
174
+ progressStartTime: z.string(),
175
+ progressEndTime: z.string(),
176
+ exceptStartTime: z.string(),
177
+ exceptEndTime: z.string(),
178
+ exceptReason: z.string(),
179
+ mentorOpinion: z.string(),
180
+ nonAttendanceNames: z.string(),
181
+ etc: z.string(),
182
+ files: z.array(z.string()),
183
+ })
184
+ export type ReportDetail = z.infer<typeof ReportDetailSchema>
185
+
186
+ export const ApprovalListItemSchema = z.object({
187
+ id: z.number(),
188
+ category: z.string(),
189
+ title: z.string(),
190
+ progressDate: z.string(),
191
+ status: z.string(),
192
+ author: z.string(),
193
+ createdAt: z.string(),
194
+ acceptedTime: z.string(),
195
+ travelExpense: z.string(),
196
+ mentoringAllowance: z.string(),
197
+ })
198
+ export type ApprovalListItem = z.infer<typeof ApprovalListItemSchema>
199
+
200
+ export const ReportCreateOptionsSchema = z.object({
201
+ menteeRegion: z.enum(['S', 'B']),
202
+ reportType: z.enum(['MRC010', 'MRC020']),
203
+ progressDate: z.string(),
204
+ teamNames: z.string().optional(),
205
+ venue: z.string(),
206
+ attendanceCount: z.number(),
207
+ attendanceNames: z.string(),
208
+ progressStartTime: z.string(),
209
+ progressEndTime: z.string(),
210
+ exceptStartTime: z.string().optional(),
211
+ exceptEndTime: z.string().optional(),
212
+ exceptReason: z.string().optional(),
213
+ subject: z.string().min(10),
214
+ content: z.string().min(100),
215
+ mentorOpinion: z.string().optional(),
216
+ nonAttendanceNames: z.string().optional(),
217
+ etc: z.string().optional(),
218
+ })
219
+ export type ReportCreateOptions = z.infer<typeof ReportCreateOptionsSchema>
220
+
221
+ export const ReportUpdateOptionsSchema = ReportCreateOptionsSchema.partial().extend({
222
+ id: z.number(),
223
+ })
224
+ export type ReportUpdateOptions = z.infer<typeof ReportUpdateOptionsSchema>