aiag-cli 1.8.0 → 2.2.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 (121) hide show
  1. package/README.md +125 -89
  2. package/dist/api/client.d.ts +171 -0
  3. package/dist/api/client.d.ts.map +1 -0
  4. package/dist/api/client.js +518 -0
  5. package/dist/api/client.js.map +1 -0
  6. package/dist/api/endpoints.d.ts +112 -0
  7. package/dist/api/endpoints.d.ts.map +1 -0
  8. package/dist/api/endpoints.js +150 -0
  9. package/dist/api/endpoints.js.map +1 -0
  10. package/dist/api/types.d.ts +395 -0
  11. package/dist/api/types.d.ts.map +1 -0
  12. package/dist/api/types.js +7 -0
  13. package/dist/api/types.js.map +1 -0
  14. package/dist/auth/credentials.d.ts +73 -0
  15. package/dist/auth/credentials.d.ts.map +1 -0
  16. package/dist/auth/credentials.js +150 -0
  17. package/dist/auth/credentials.js.map +1 -0
  18. package/dist/auth/device.d.ts +58 -0
  19. package/dist/auth/device.d.ts.map +1 -0
  20. package/dist/auth/device.js +235 -0
  21. package/dist/auth/device.js.map +1 -0
  22. package/dist/auth/token.d.ts +55 -0
  23. package/dist/auth/token.d.ts.map +1 -0
  24. package/dist/auth/token.js +153 -0
  25. package/dist/auth/token.js.map +1 -0
  26. package/dist/cli.js +51 -1
  27. package/dist/cli.js.map +1 -1
  28. package/dist/commands/auto.d.ts.map +1 -1
  29. package/dist/commands/auto.js +321 -236
  30. package/dist/commands/auto.js.map +1 -1
  31. package/dist/commands/complete.d.ts +1 -0
  32. package/dist/commands/complete.d.ts.map +1 -1
  33. package/dist/commands/complete.js +30 -0
  34. package/dist/commands/complete.js.map +1 -1
  35. package/dist/commands/connect.d.ts +25 -0
  36. package/dist/commands/connect.d.ts.map +1 -0
  37. package/dist/commands/connect.js +356 -0
  38. package/dist/commands/connect.js.map +1 -0
  39. package/dist/commands/login.d.ts +19 -0
  40. package/dist/commands/login.d.ts.map +1 -0
  41. package/dist/commands/login.js +119 -0
  42. package/dist/commands/login.js.map +1 -0
  43. package/dist/commands/logout.d.ts +15 -0
  44. package/dist/commands/logout.d.ts.map +1 -0
  45. package/dist/commands/logout.js +50 -0
  46. package/dist/commands/logout.js.map +1 -0
  47. package/dist/commands/next.d.ts +1 -0
  48. package/dist/commands/next.d.ts.map +1 -1
  49. package/dist/commands/next.js +41 -2
  50. package/dist/commands/next.js.map +1 -1
  51. package/dist/commands/project.d.ts +13 -0
  52. package/dist/commands/project.d.ts.map +1 -0
  53. package/dist/commands/project.js +92 -0
  54. package/dist/commands/project.js.map +1 -0
  55. package/dist/commands/session.d.ts +10 -2
  56. package/dist/commands/session.d.ts.map +1 -1
  57. package/dist/commands/session.js +80 -3
  58. package/dist/commands/session.js.map +1 -1
  59. package/dist/commands/status.d.ts +1 -0
  60. package/dist/commands/status.d.ts.map +1 -1
  61. package/dist/commands/status.js +51 -0
  62. package/dist/commands/status.js.map +1 -1
  63. package/dist/commands/sync.d.ts +33 -0
  64. package/dist/commands/sync.d.ts.map +1 -0
  65. package/dist/commands/sync.js +555 -0
  66. package/dist/commands/sync.js.map +1 -0
  67. package/dist/commands/work.d.ts +1 -0
  68. package/dist/commands/work.d.ts.map +1 -1
  69. package/dist/commands/work.js +22 -1
  70. package/dist/commands/work.js.map +1 -1
  71. package/dist/config/global.d.ts +54 -0
  72. package/dist/config/global.d.ts.map +1 -0
  73. package/dist/config/global.js +110 -0
  74. package/dist/config/global.js.map +1 -0
  75. package/dist/prompts/coding.d.ts +31 -0
  76. package/dist/prompts/coding.d.ts.map +1 -0
  77. package/dist/prompts/coding.js +228 -0
  78. package/dist/prompts/coding.js.map +1 -0
  79. package/dist/prompts/index.d.ts +10 -0
  80. package/dist/prompts/index.d.ts.map +1 -0
  81. package/dist/prompts/index.js +10 -0
  82. package/dist/prompts/index.js.map +1 -0
  83. package/dist/prompts/initializer.d.ts +20 -0
  84. package/dist/prompts/initializer.d.ts.map +1 -0
  85. package/dist/prompts/initializer.js +147 -0
  86. package/dist/prompts/initializer.js.map +1 -0
  87. package/dist/sdk/client.d.ts +67 -0
  88. package/dist/sdk/client.d.ts.map +1 -0
  89. package/dist/sdk/client.js +196 -0
  90. package/dist/sdk/client.js.map +1 -0
  91. package/dist/sdk/index.d.ts +8 -0
  92. package/dist/sdk/index.d.ts.map +1 -0
  93. package/dist/sdk/index.js +8 -0
  94. package/dist/sdk/index.js.map +1 -0
  95. package/dist/sdk/security.d.ts +43 -0
  96. package/dist/sdk/security.d.ts.map +1 -0
  97. package/dist/sdk/security.js +214 -0
  98. package/dist/sdk/security.js.map +1 -0
  99. package/dist/types.d.ts +64 -0
  100. package/dist/types.d.ts.map +1 -1
  101. package/dist/utils/connection.d.ts +126 -0
  102. package/dist/utils/connection.d.ts.map +1 -0
  103. package/dist/utils/connection.js +226 -0
  104. package/dist/utils/connection.js.map +1 -0
  105. package/dist/utils/messages.d.ts +76 -0
  106. package/dist/utils/messages.d.ts.map +1 -1
  107. package/dist/utils/messages.js +87 -1
  108. package/dist/utils/messages.js.map +1 -1
  109. package/dist/utils/output.d.ts +10 -0
  110. package/dist/utils/output.d.ts.map +1 -1
  111. package/dist/utils/output.js +31 -0
  112. package/dist/utils/output.js.map +1 -1
  113. package/dist/utils/prompt.d.ts +10 -0
  114. package/dist/utils/prompt.d.ts.map +1 -0
  115. package/dist/utils/prompt.js +22 -0
  116. package/dist/utils/prompt.js.map +1 -0
  117. package/dist/utils/sseClient.d.ts +183 -0
  118. package/dist/utils/sseClient.d.ts.map +1 -0
  119. package/dist/utils/sseClient.js +391 -0
  120. package/dist/utils/sseClient.js.map +1 -0
  121. package/package.json +3 -2
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Credentials 관리 모듈
3
+ *
4
+ * ~/.aiag/credentials 파일 관리
5
+ *
6
+ * 저장 형식 (JSON):
7
+ * {
8
+ * "serverUrl": "https://aiag-adp.example.com",
9
+ * "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
10
+ * "refreshToken": "...",
11
+ * "expiresAt": "2025-01-01T00:00:00Z",
12
+ * "userId": "user_123",
13
+ * "email": "user@example.com"
14
+ * }
15
+ */
16
+ import { existsSync, readFileSync, writeFileSync, unlinkSync } from 'fs';
17
+ import { join } from 'path';
18
+ import { ensureGlobalConfigDir, getGlobalConfigDir } from '../config/global.js';
19
+ /**
20
+ * Credentials 파일 경로 반환
21
+ * @returns ~/.aiag/credentials
22
+ */
23
+ export function getCredentialsPath() {
24
+ return join(getGlobalConfigDir(), 'credentials');
25
+ }
26
+ /**
27
+ * 저장된 credentials 로드
28
+ * @returns Credentials 또는 null (없거나 유효하지 않은 경우)
29
+ */
30
+ export function loadCredentials() {
31
+ const credentialsPath = getCredentialsPath();
32
+ if (!existsSync(credentialsPath)) {
33
+ return null;
34
+ }
35
+ try {
36
+ const content = readFileSync(credentialsPath, 'utf-8');
37
+ const credentials = JSON.parse(content);
38
+ // 필수 필드 검증
39
+ if (!credentials.serverUrl ||
40
+ !credentials.accessToken ||
41
+ !credentials.expiresAt ||
42
+ !credentials.userId ||
43
+ !credentials.email) {
44
+ return null;
45
+ }
46
+ return credentials;
47
+ }
48
+ catch {
49
+ return null;
50
+ }
51
+ }
52
+ /**
53
+ * Credentials 저장
54
+ * @param credentials 저장할 인증 정보
55
+ */
56
+ export function saveCredentials(credentials) {
57
+ ensureGlobalConfigDir();
58
+ const credentialsPath = getCredentialsPath();
59
+ writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2), {
60
+ mode: 0o600, // 소유자만 읽기/쓰기
61
+ });
62
+ }
63
+ /**
64
+ * Credentials 삭제
65
+ */
66
+ export function clearCredentials() {
67
+ const credentialsPath = getCredentialsPath();
68
+ if (existsSync(credentialsPath)) {
69
+ unlinkSync(credentialsPath);
70
+ }
71
+ }
72
+ /**
73
+ * 로그인 상태 확인
74
+ * @returns 로그인 되어 있으면 true
75
+ */
76
+ export function isLoggedIn() {
77
+ const credentials = loadCredentials();
78
+ if (!credentials) {
79
+ return false;
80
+ }
81
+ // 토큰 만료 확인
82
+ return !isTokenExpired(credentials);
83
+ }
84
+ /**
85
+ * 토큰 만료 여부 확인
86
+ * @param credentials 인증 정보
87
+ * @returns 만료되었으면 true
88
+ */
89
+ export function isTokenExpired(credentials) {
90
+ const expiresAt = new Date(credentials.expiresAt);
91
+ const now = new Date();
92
+ // 5분 여유를 두고 만료 판정 (갱신 시간 확보)
93
+ const bufferMs = 5 * 60 * 1000;
94
+ return now.getTime() >= expiresAt.getTime() - bufferMs;
95
+ }
96
+ /**
97
+ * 토큰 만료까지 남은 시간 (초)
98
+ * @param credentials 인증 정보
99
+ * @returns 남은 시간 (초), 이미 만료되었으면 0
100
+ */
101
+ export function getTokenTimeRemaining(credentials) {
102
+ const expiresAt = new Date(credentials.expiresAt);
103
+ const now = new Date();
104
+ const remaining = Math.floor((expiresAt.getTime() - now.getTime()) / 1000);
105
+ return Math.max(0, remaining);
106
+ }
107
+ /**
108
+ * 현재 로그인된 사용자 이메일 반환
109
+ * @returns 이메일 또는 null
110
+ */
111
+ export function getCurrentUserEmail() {
112
+ const credentials = loadCredentials();
113
+ return credentials?.email ?? null;
114
+ }
115
+ /**
116
+ * 현재 연결된 서버 URL 반환
117
+ * @returns 서버 URL 또는 null
118
+ */
119
+ export function getCurrentServerUrl() {
120
+ const credentials = loadCredentials();
121
+ return credentials?.serverUrl ?? null;
122
+ }
123
+ /**
124
+ * Access Token 반환 (유효한 경우만)
125
+ * @returns 액세스 토큰 또는 null (만료 또는 없음)
126
+ */
127
+ export function getAccessToken() {
128
+ const credentials = loadCredentials();
129
+ if (!credentials || isTokenExpired(credentials)) {
130
+ return null;
131
+ }
132
+ return credentials.accessToken;
133
+ }
134
+ /**
135
+ * Credentials 업데이트 (부분 업데이트)
136
+ * 기존 credentials가 있는 경우에만 동작
137
+ */
138
+ export function updateCredentials(updates) {
139
+ const credentials = loadCredentials();
140
+ if (!credentials) {
141
+ return false;
142
+ }
143
+ const updated = {
144
+ ...credentials,
145
+ ...updates,
146
+ };
147
+ saveCredentials(updated);
148
+ return true;
149
+ }
150
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/auth/credentials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * 디바이스 인증 플로우 모듈
3
+ *
4
+ * OAuth 2.0 Device Authorization Grant (RFC 8628) 구현
5
+ *
6
+ * 플로우:
7
+ * 1. POST /api/auth/device/request → device_code, user_code 수신
8
+ * 2. 브라우저 열기 (인증 URL)
9
+ * 3. GET /api/auth/device/poll 폴링
10
+ * 4. 토큰 수신 → credentials 저장
11
+ */
12
+ import type { DeviceCodeResponse, DeviceTokenResponse, DevicePollStatus, Credentials } from '../types.js';
13
+ /**
14
+ * 디바이스 인증 코드 요청
15
+ *
16
+ * @param serverUrl aiag-adp 서버 URL
17
+ * @returns 디바이스 코드 응답
18
+ * @throws Error 서버 연결 실패 또는 응답 오류 시
19
+ */
20
+ export declare function requestDeviceCode(serverUrl: string): Promise<DeviceCodeResponse>;
21
+ /**
22
+ * 디바이스 인증 토큰 폴링
23
+ *
24
+ * @param serverUrl aiag-adp 서버 URL
25
+ * @param deviceCode 디바이스 코드
26
+ * @returns 폴링 결과 (상태 또는 토큰)
27
+ */
28
+ export declare function pollForToken(serverUrl: string, deviceCode: string): Promise<{
29
+ status: DevicePollStatus;
30
+ token?: DeviceTokenResponse;
31
+ }>;
32
+ /**
33
+ * 디바이스 인증 전체 플로우 실행
34
+ *
35
+ * @param serverUrl 서버 URL
36
+ * @param callbacks 콜백 함수들
37
+ * @returns 성공 시 Credentials
38
+ */
39
+ export declare function performDeviceAuth(serverUrl: string, callbacks: {
40
+ onCodeReceived: (code: DeviceCodeResponse) => void;
41
+ onPolling: () => void;
42
+ onSuccess: (email: string) => void;
43
+ onError: (error: string) => void;
44
+ }): Promise<Credentials | null>;
45
+ /**
46
+ * 브라우저 열기
47
+ * ESM 동적 import 사용 (open 패키지)
48
+ */
49
+ export declare function openBrowser(url: string): Promise<boolean>;
50
+ /**
51
+ * 서버 URL 유효성 검증
52
+ */
53
+ export declare function validateServerUrl(serverUrl: string): boolean;
54
+ /**
55
+ * 서버 연결 테스트
56
+ */
57
+ export declare function testServerConnection(serverUrl: string): Promise<boolean>;
58
+ //# sourceMappingURL=device.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../src/auth/device.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,WAAW,EACZ,MAAM,aAAa,CAAC;AAQrB;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA4BtF;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC;IAAE,MAAM,EAAE,gBAAgB,CAAC;IAAC,KAAK,CAAC,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAuDpE;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE;IACT,cAAc,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACnD,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC,GACA,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAoE7B;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CA0B/D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAO5D;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAW9E"}
@@ -0,0 +1,235 @@
1
+ /**
2
+ * 디바이스 인증 플로우 모듈
3
+ *
4
+ * OAuth 2.0 Device Authorization Grant (RFC 8628) 구현
5
+ *
6
+ * 플로우:
7
+ * 1. POST /api/auth/device/request → device_code, user_code 수신
8
+ * 2. 브라우저 열기 (인증 URL)
9
+ * 3. GET /api/auth/device/poll 폴링
10
+ * 4. 토큰 수신 → credentials 저장
11
+ */
12
+ import { saveCredentials } from './credentials.js';
13
+ import { setDefaultServer } from '../config/global.js';
14
+ // 디바이스 인증 관련 API 엔드포인트
15
+ const DEVICE_REQUEST_ENDPOINT = '/api/auth/device/request';
16
+ const DEVICE_POLL_ENDPOINT = '/api/auth/device/poll';
17
+ /**
18
+ * 디바이스 인증 코드 요청
19
+ *
20
+ * @param serverUrl aiag-adp 서버 URL
21
+ * @returns 디바이스 코드 응답
22
+ * @throws Error 서버 연결 실패 또는 응답 오류 시
23
+ */
24
+ export async function requestDeviceCode(serverUrl) {
25
+ const url = new URL(DEVICE_REQUEST_ENDPOINT, serverUrl).toString();
26
+ const response = await fetch(url, {
27
+ method: 'POST',
28
+ headers: {
29
+ 'Content-Type': 'application/json',
30
+ },
31
+ body: JSON.stringify({
32
+ clientId: 'aiag-cli',
33
+ scope: 'read write',
34
+ }),
35
+ });
36
+ if (!response.ok) {
37
+ const error = await response.text();
38
+ throw new Error(`디바이스 코드 요청 실패: ${response.status} ${error}`);
39
+ }
40
+ const data = await response.json();
41
+ return {
42
+ deviceCode: (data.device_code || data.deviceCode),
43
+ userCode: (data.user_code || data.userCode),
44
+ verificationUri: (data.verification_uri || data.verificationUri),
45
+ expiresIn: (data.expires_in || data.expiresIn || 300),
46
+ interval: (data.interval || 5),
47
+ };
48
+ }
49
+ /**
50
+ * 디바이스 인증 토큰 폴링
51
+ *
52
+ * @param serverUrl aiag-adp 서버 URL
53
+ * @param deviceCode 디바이스 코드
54
+ * @returns 폴링 결과 (상태 또는 토큰)
55
+ */
56
+ export async function pollForToken(serverUrl, deviceCode) {
57
+ const url = new URL(DEVICE_POLL_ENDPOINT, serverUrl).toString();
58
+ const response = await fetch(url, {
59
+ method: 'POST',
60
+ headers: {
61
+ 'Content-Type': 'application/json',
62
+ },
63
+ body: JSON.stringify({
64
+ deviceCode,
65
+ clientId: 'aiag-cli',
66
+ }),
67
+ });
68
+ if (response.status === 400) {
69
+ // 아직 인증 대기 중
70
+ const data = await response.json();
71
+ const error = (data.error || data.status);
72
+ switch (error) {
73
+ case 'authorization_pending':
74
+ case 'pending':
75
+ return { status: 'pending' };
76
+ case 'slow_down':
77
+ // 폴링 간격을 늘려야 함 (호출자가 처리)
78
+ return { status: 'pending' };
79
+ case 'expired_token':
80
+ case 'expired':
81
+ return { status: 'expired' };
82
+ case 'access_denied':
83
+ case 'denied':
84
+ return { status: 'denied' };
85
+ default:
86
+ return { status: 'pending' };
87
+ }
88
+ }
89
+ if (!response.ok) {
90
+ const error = await response.text();
91
+ throw new Error(`토큰 폴링 실패: ${response.status} ${error}`);
92
+ }
93
+ // 인증 성공
94
+ const data = await response.json();
95
+ return {
96
+ status: 'authorized',
97
+ token: {
98
+ accessToken: (data.access_token || data.accessToken),
99
+ refreshToken: (data.refresh_token || data.refreshToken),
100
+ expiresIn: (data.expires_in || data.expiresIn || 3600),
101
+ userId: (data.user_id || data.userId),
102
+ email: data.email,
103
+ },
104
+ };
105
+ }
106
+ /**
107
+ * 디바이스 인증 전체 플로우 실행
108
+ *
109
+ * @param serverUrl 서버 URL
110
+ * @param callbacks 콜백 함수들
111
+ * @returns 성공 시 Credentials
112
+ */
113
+ export async function performDeviceAuth(serverUrl, callbacks) {
114
+ try {
115
+ // 1. 디바이스 코드 요청
116
+ const codeResponse = await requestDeviceCode(serverUrl);
117
+ callbacks.onCodeReceived(codeResponse);
118
+ // 2. 폴링 시작
119
+ const startTime = Date.now();
120
+ const expiresAtMs = startTime + codeResponse.expiresIn * 1000;
121
+ let interval = codeResponse.interval * 1000;
122
+ while (Date.now() < expiresAtMs) {
123
+ callbacks.onPolling();
124
+ // 폴링 간격 대기
125
+ await sleep(interval);
126
+ // 폴링 요청
127
+ const result = await pollForToken(serverUrl, codeResponse.deviceCode);
128
+ switch (result.status) {
129
+ case 'authorized':
130
+ if (result.token) {
131
+ // 토큰 수신 성공
132
+ const expiresAt = new Date(Date.now() + result.token.expiresIn * 1000).toISOString();
133
+ const credentials = {
134
+ serverUrl,
135
+ accessToken: result.token.accessToken,
136
+ refreshToken: result.token.refreshToken,
137
+ expiresAt,
138
+ userId: result.token.userId,
139
+ email: result.token.email,
140
+ };
141
+ // Credentials 저장
142
+ saveCredentials(credentials);
143
+ // 기본 서버로 설정
144
+ setDefaultServer(serverUrl);
145
+ callbacks.onSuccess(result.token.email);
146
+ return credentials;
147
+ }
148
+ break;
149
+ case 'expired':
150
+ callbacks.onError('인증 코드가 만료되었습니다.');
151
+ return null;
152
+ case 'denied':
153
+ callbacks.onError('인증이 거부되었습니다.');
154
+ return null;
155
+ case 'pending':
156
+ // 계속 폴링
157
+ break;
158
+ }
159
+ }
160
+ // 타임아웃
161
+ callbacks.onError('인증 시간이 초과되었습니다.');
162
+ return null;
163
+ }
164
+ catch (error) {
165
+ const message = error instanceof Error ? error.message : '알 수 없는 오류';
166
+ callbacks.onError(message);
167
+ return null;
168
+ }
169
+ }
170
+ /**
171
+ * 브라우저 열기
172
+ * ESM 동적 import 사용 (open 패키지)
173
+ */
174
+ export async function openBrowser(url) {
175
+ try {
176
+ // Node.js 18+에서 사용 가능한 방법
177
+ const { exec } = await import('child_process');
178
+ const { platform } = await import('os');
179
+ const platformName = platform();
180
+ let command;
181
+ if (platformName === 'darwin') {
182
+ command = `open "${url}"`;
183
+ }
184
+ else if (platformName === 'win32') {
185
+ command = `start "" "${url}"`;
186
+ }
187
+ else {
188
+ // Linux 및 기타
189
+ command = `xdg-open "${url}"`;
190
+ }
191
+ return new Promise((resolve) => {
192
+ exec(command, (error) => {
193
+ resolve(!error);
194
+ });
195
+ });
196
+ }
197
+ catch {
198
+ return false;
199
+ }
200
+ }
201
+ /**
202
+ * 서버 URL 유효성 검증
203
+ */
204
+ export function validateServerUrl(serverUrl) {
205
+ try {
206
+ const url = new URL(serverUrl);
207
+ return url.protocol === 'https:' || url.protocol === 'http:';
208
+ }
209
+ catch {
210
+ return false;
211
+ }
212
+ }
213
+ /**
214
+ * 서버 연결 테스트
215
+ */
216
+ export async function testServerConnection(serverUrl) {
217
+ try {
218
+ const url = new URL('/api/health', serverUrl).toString();
219
+ const response = await fetch(url, {
220
+ method: 'GET',
221
+ signal: AbortSignal.timeout(5000), // 5초 타임아웃
222
+ });
223
+ return response.ok;
224
+ }
225
+ catch {
226
+ return false;
227
+ }
228
+ }
229
+ /**
230
+ * 유틸리티: sleep
231
+ */
232
+ function sleep(ms) {
233
+ return new Promise((resolve) => setTimeout(resolve, ms));
234
+ }
235
+ //# sourceMappingURL=device.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device.js","sourceRoot":"","sources":["../../src/auth/device.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * 토큰 관리 모듈
3
+ *
4
+ * - 토큰 갱신
5
+ * - 토큰 검증
6
+ * - 토큰 만료 처리
7
+ */
8
+ import type { Credentials } from '../types.js';
9
+ /**
10
+ * 토큰 갱신 시도
11
+ *
12
+ * @param credentials 현재 인증 정보
13
+ * @returns 갱신된 credentials 또는 null (갱신 실패)
14
+ */
15
+ export declare function refreshToken(credentials: Credentials): Promise<Credentials | null>;
16
+ /**
17
+ * 토큰 무효화 (로그아웃 시 서버에 알림)
18
+ *
19
+ * @param credentials 인증 정보
20
+ * @returns 성공 여부
21
+ */
22
+ export declare function revokeToken(credentials: Credentials): Promise<boolean>;
23
+ /**
24
+ * 유효한 액세스 토큰 얻기
25
+ * 만료된 경우 자동 갱신 시도
26
+ *
27
+ * @returns 유효한 액세스 토큰 또는 null
28
+ */
29
+ export declare function getValidAccessToken(): Promise<string | null>;
30
+ /**
31
+ * 토큰 상태 정보
32
+ */
33
+ export interface TokenStatus {
34
+ isValid: boolean;
35
+ isExpired: boolean;
36
+ hasRefreshToken: boolean;
37
+ timeRemaining: number;
38
+ email: string | null;
39
+ serverUrl: string | null;
40
+ }
41
+ /**
42
+ * 토큰 상태 확인
43
+ */
44
+ export declare function getTokenStatus(): TokenStatus;
45
+ /**
46
+ * 토큰 자동 갱신 여부 확인
47
+ * 만료 10분 전부터 갱신 권장
48
+ */
49
+ export declare function shouldRefreshToken(): boolean;
50
+ /**
51
+ * 토큰이 곧 만료되는지 확인 (경고용)
52
+ * 만료 30분 전
53
+ */
54
+ export declare function isTokenExpiringSoon(): boolean;
55
+ //# sourceMappingURL=token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/auth/token.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAY/C;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAyCxF;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAmB5E;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAmBlE;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAwB5C;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAU5C;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAU7C"}
@@ -0,0 +1,153 @@
1
+ /**
2
+ * 토큰 관리 모듈
3
+ *
4
+ * - 토큰 갱신
5
+ * - 토큰 검증
6
+ * - 토큰 만료 처리
7
+ */
8
+ import { loadCredentials, saveCredentials, isTokenExpired, getTokenTimeRemaining, } from './credentials.js';
9
+ // 토큰 갱신 엔드포인트
10
+ const TOKEN_REFRESH_ENDPOINT = '/api/auth/token/refresh';
11
+ const TOKEN_REVOKE_ENDPOINT = '/api/auth/token/revoke';
12
+ /**
13
+ * 토큰 갱신 시도
14
+ *
15
+ * @param credentials 현재 인증 정보
16
+ * @returns 갱신된 credentials 또는 null (갱신 실패)
17
+ */
18
+ export async function refreshToken(credentials) {
19
+ // refreshToken이 없으면 갱신 불가
20
+ if (!credentials.refreshToken) {
21
+ return null;
22
+ }
23
+ try {
24
+ const url = new URL(TOKEN_REFRESH_ENDPOINT, credentials.serverUrl).toString();
25
+ const response = await fetch(url, {
26
+ method: 'POST',
27
+ headers: {
28
+ 'Content-Type': 'application/json',
29
+ },
30
+ body: JSON.stringify({
31
+ refreshToken: credentials.refreshToken,
32
+ }),
33
+ });
34
+ if (!response.ok) {
35
+ return null;
36
+ }
37
+ const data = await response.json();
38
+ // 새 토큰으로 credentials 업데이트
39
+ const expiresIn = (data.expires_in || data.expiresIn || 3600);
40
+ const expiresAt = new Date(Date.now() + expiresIn * 1000).toISOString();
41
+ const updated = {
42
+ ...credentials,
43
+ accessToken: (data.access_token || data.accessToken),
44
+ refreshToken: (data.refresh_token || data.refreshToken || credentials.refreshToken),
45
+ expiresAt,
46
+ };
47
+ saveCredentials(updated);
48
+ return updated;
49
+ }
50
+ catch {
51
+ return null;
52
+ }
53
+ }
54
+ /**
55
+ * 토큰 무효화 (로그아웃 시 서버에 알림)
56
+ *
57
+ * @param credentials 인증 정보
58
+ * @returns 성공 여부
59
+ */
60
+ export async function revokeToken(credentials) {
61
+ try {
62
+ const url = new URL(TOKEN_REVOKE_ENDPOINT, credentials.serverUrl).toString();
63
+ const response = await fetch(url, {
64
+ method: 'POST',
65
+ headers: {
66
+ 'Content-Type': 'application/json',
67
+ Authorization: `Bearer ${credentials.accessToken}`,
68
+ },
69
+ body: JSON.stringify({
70
+ token: credentials.accessToken,
71
+ }),
72
+ });
73
+ return response.ok;
74
+ }
75
+ catch {
76
+ return false;
77
+ }
78
+ }
79
+ /**
80
+ * 유효한 액세스 토큰 얻기
81
+ * 만료된 경우 자동 갱신 시도
82
+ *
83
+ * @returns 유효한 액세스 토큰 또는 null
84
+ */
85
+ export async function getValidAccessToken() {
86
+ const credentials = loadCredentials();
87
+ if (!credentials) {
88
+ return null;
89
+ }
90
+ // 토큰이 유효한 경우
91
+ if (!isTokenExpired(credentials)) {
92
+ return credentials.accessToken;
93
+ }
94
+ // 토큰 갱신 시도
95
+ const refreshed = await refreshToken(credentials);
96
+ if (refreshed) {
97
+ return refreshed.accessToken;
98
+ }
99
+ // 갱신 실패
100
+ return null;
101
+ }
102
+ /**
103
+ * 토큰 상태 확인
104
+ */
105
+ export function getTokenStatus() {
106
+ const credentials = loadCredentials();
107
+ if (!credentials) {
108
+ return {
109
+ isValid: false,
110
+ isExpired: true,
111
+ hasRefreshToken: false,
112
+ timeRemaining: 0,
113
+ email: null,
114
+ serverUrl: null,
115
+ };
116
+ }
117
+ const expired = isTokenExpired(credentials);
118
+ return {
119
+ isValid: !expired,
120
+ isExpired: expired,
121
+ hasRefreshToken: !!credentials.refreshToken,
122
+ timeRemaining: getTokenTimeRemaining(credentials),
123
+ email: credentials.email,
124
+ serverUrl: credentials.serverUrl,
125
+ };
126
+ }
127
+ /**
128
+ * 토큰 자동 갱신 여부 확인
129
+ * 만료 10분 전부터 갱신 권장
130
+ */
131
+ export function shouldRefreshToken() {
132
+ const credentials = loadCredentials();
133
+ if (!credentials || !credentials.refreshToken) {
134
+ return false;
135
+ }
136
+ const remaining = getTokenTimeRemaining(credentials);
137
+ const thresholdSeconds = 10 * 60; // 10분
138
+ return remaining > 0 && remaining < thresholdSeconds;
139
+ }
140
+ /**
141
+ * 토큰이 곧 만료되는지 확인 (경고용)
142
+ * 만료 30분 전
143
+ */
144
+ export function isTokenExpiringSoon() {
145
+ const credentials = loadCredentials();
146
+ if (!credentials) {
147
+ return false;
148
+ }
149
+ const remaining = getTokenTimeRemaining(credentials);
150
+ const thresholdSeconds = 30 * 60; // 30분
151
+ return remaining > 0 && remaining < thresholdSeconds;
152
+ }
153
+ //# sourceMappingURL=token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/auth/token.ts"],"names":[],"mappings":"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"}