@itphx/synth-core 0.3.0 → 0.4.4

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 (187) hide show
  1. package/dist/auth/local.d.ts +14 -0
  2. package/dist/auth/local.d.ts.map +1 -1
  3. package/dist/auth/local.js +63 -11
  4. package/dist/auth/local.js.map +1 -1
  5. package/dist/auth/local.test.js +59 -0
  6. package/dist/auth/local.test.js.map +1 -1
  7. package/dist/events/bus.d.ts +7 -0
  8. package/dist/events/bus.d.ts.map +1 -1
  9. package/dist/events/bus.js +26 -3
  10. package/dist/events/bus.js.map +1 -1
  11. package/dist/events/bus.test.js +46 -0
  12. package/dist/events/bus.test.js.map +1 -1
  13. package/dist/index.d.ts +3 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +2 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/main.js +247 -8
  18. package/dist/main.js.map +1 -1
  19. package/dist/providers/index.d.ts +1 -0
  20. package/dist/providers/index.d.ts.map +1 -1
  21. package/dist/providers/index.js +4 -0
  22. package/dist/providers/index.js.map +1 -1
  23. package/dist/providers/remote.d.ts +60 -0
  24. package/dist/providers/remote.d.ts.map +1 -0
  25. package/dist/providers/remote.js +394 -0
  26. package/dist/providers/remote.js.map +1 -0
  27. package/dist/providers/remote.test.d.ts +2 -0
  28. package/dist/providers/remote.test.d.ts.map +1 -0
  29. package/dist/providers/remote.test.js +258 -0
  30. package/dist/providers/remote.test.js.map +1 -0
  31. package/dist/remote/aggregate.d.ts +43 -0
  32. package/dist/remote/aggregate.d.ts.map +1 -0
  33. package/dist/remote/aggregate.js +84 -0
  34. package/dist/remote/aggregate.js.map +1 -0
  35. package/dist/remote/aggregate.test.d.ts +2 -0
  36. package/dist/remote/aggregate.test.d.ts.map +1 -0
  37. package/dist/remote/aggregate.test.js +120 -0
  38. package/dist/remote/aggregate.test.js.map +1 -0
  39. package/dist/remote/client.d.ts +142 -0
  40. package/dist/remote/client.d.ts.map +1 -0
  41. package/dist/remote/client.js +290 -0
  42. package/dist/remote/client.js.map +1 -0
  43. package/dist/remote/client.test.d.ts +2 -0
  44. package/dist/remote/client.test.d.ts.map +1 -0
  45. package/dist/remote/client.test.js +176 -0
  46. package/dist/remote/client.test.js.map +1 -0
  47. package/dist/remote/connection.d.ts +66 -0
  48. package/dist/remote/connection.d.ts.map +1 -0
  49. package/dist/remote/connection.js +298 -0
  50. package/dist/remote/connection.js.map +1 -0
  51. package/dist/remote/connection.test.d.ts +2 -0
  52. package/dist/remote/connection.test.d.ts.map +1 -0
  53. package/dist/remote/connection.test.js +323 -0
  54. package/dist/remote/connection.test.js.map +1 -0
  55. package/dist/remote/credentials.d.ts +13 -0
  56. package/dist/remote/credentials.d.ts.map +1 -0
  57. package/dist/remote/credentials.js +81 -0
  58. package/dist/remote/credentials.js.map +1 -0
  59. package/dist/remote/credentials.test.d.ts +2 -0
  60. package/dist/remote/credentials.test.d.ts.map +1 -0
  61. package/dist/remote/credentials.test.js +69 -0
  62. package/dist/remote/credentials.test.js.map +1 -0
  63. package/dist/remote/device-flow.d.ts +72 -0
  64. package/dist/remote/device-flow.d.ts.map +1 -0
  65. package/dist/remote/device-flow.js +229 -0
  66. package/dist/remote/device-flow.js.map +1 -0
  67. package/dist/remote/device-flow.test.d.ts +2 -0
  68. package/dist/remote/device-flow.test.d.ts.map +1 -0
  69. package/dist/remote/device-flow.test.js +103 -0
  70. package/dist/remote/device-flow.test.js.map +1 -0
  71. package/dist/remote/event-bridge.d.ts +46 -0
  72. package/dist/remote/event-bridge.d.ts.map +1 -0
  73. package/dist/remote/event-bridge.js +218 -0
  74. package/dist/remote/event-bridge.js.map +1 -0
  75. package/dist/remote/event-bridge.test.d.ts +2 -0
  76. package/dist/remote/event-bridge.test.d.ts.map +1 -0
  77. package/dist/remote/event-bridge.test.js +240 -0
  78. package/dist/remote/event-bridge.test.js.map +1 -0
  79. package/dist/remote/index.d.ts +19 -0
  80. package/dist/remote/index.d.ts.map +1 -0
  81. package/dist/remote/index.js +11 -0
  82. package/dist/remote/index.js.map +1 -0
  83. package/dist/remote/session.d.ts +72 -0
  84. package/dist/remote/session.d.ts.map +1 -0
  85. package/dist/remote/session.js +248 -0
  86. package/dist/remote/session.js.map +1 -0
  87. package/dist/remote/session.test.d.ts +2 -0
  88. package/dist/remote/session.test.d.ts.map +1 -0
  89. package/dist/remote/session.test.js +482 -0
  90. package/dist/remote/session.test.js.map +1 -0
  91. package/dist/remote/transfer.d.ts +75 -0
  92. package/dist/remote/transfer.d.ts.map +1 -0
  93. package/dist/remote/transfer.js +491 -0
  94. package/dist/remote/transfer.js.map +1 -0
  95. package/dist/remote/transfer.test.d.ts +2 -0
  96. package/dist/remote/transfer.test.d.ts.map +1 -0
  97. package/dist/remote/transfer.test.js +416 -0
  98. package/dist/remote/transfer.test.js.map +1 -0
  99. package/dist/remote/types.d.ts +28 -0
  100. package/dist/remote/types.d.ts.map +1 -0
  101. package/dist/remote/types.js +10 -0
  102. package/dist/remote/types.js.map +1 -0
  103. package/dist/server/app.d.ts +62 -1
  104. package/dist/server/app.d.ts.map +1 -1
  105. package/dist/server/app.js +364 -20
  106. package/dist/server/app.js.map +1 -1
  107. package/dist/server/app.proxy.test.d.ts +2 -0
  108. package/dist/server/app.proxy.test.d.ts.map +1 -0
  109. package/dist/server/app.proxy.test.js +193 -0
  110. package/dist/server/app.proxy.test.js.map +1 -0
  111. package/dist/server/app.remote.test.d.ts +2 -0
  112. package/dist/server/app.remote.test.d.ts.map +1 -0
  113. package/dist/server/app.remote.test.js +674 -0
  114. package/dist/server/app.remote.test.js.map +1 -0
  115. package/dist/server/proxy.d.ts +14 -0
  116. package/dist/server/proxy.d.ts.map +1 -0
  117. package/dist/server/proxy.js +93 -0
  118. package/dist/server/proxy.js.map +1 -0
  119. package/dist/server/routes/auth.d.ts +26 -2
  120. package/dist/server/routes/auth.d.ts.map +1 -1
  121. package/dist/server/routes/auth.js +269 -36
  122. package/dist/server/routes/auth.js.map +1 -1
  123. package/dist/server/routes/auth.test.d.ts +2 -0
  124. package/dist/server/routes/auth.test.d.ts.map +1 -0
  125. package/dist/server/routes/auth.test.js +466 -0
  126. package/dist/server/routes/auth.test.js.map +1 -0
  127. package/dist/server/routes/fs.d.ts +2 -1
  128. package/dist/server/routes/fs.d.ts.map +1 -1
  129. package/dist/server/routes/fs.js +159 -5
  130. package/dist/server/routes/fs.js.map +1 -1
  131. package/dist/server/routes/hybrid.d.ts +32 -0
  132. package/dist/server/routes/hybrid.d.ts.map +1 -0
  133. package/dist/server/routes/hybrid.js +21 -0
  134. package/dist/server/routes/hybrid.js.map +1 -0
  135. package/dist/server/routes/hybrid.test.d.ts +2 -0
  136. package/dist/server/routes/hybrid.test.d.ts.map +1 -0
  137. package/dist/server/routes/hybrid.test.js +454 -0
  138. package/dist/server/routes/hybrid.test.js.map +1 -0
  139. package/dist/server/routes/messages.d.ts +2 -1
  140. package/dist/server/routes/messages.d.ts.map +1 -1
  141. package/dist/server/routes/messages.js +104 -1
  142. package/dist/server/routes/messages.js.map +1 -1
  143. package/dist/server/routes/models.cache.test.d.ts +2 -0
  144. package/dist/server/routes/models.cache.test.d.ts.map +1 -0
  145. package/dist/server/routes/models.cache.test.js +59 -0
  146. package/dist/server/routes/models.cache.test.js.map +1 -0
  147. package/dist/server/routes/models.d.ts +9 -1
  148. package/dist/server/routes/models.d.ts.map +1 -1
  149. package/dist/server/routes/models.js +35 -2
  150. package/dist/server/routes/models.js.map +1 -1
  151. package/dist/server/routes/models.remote.test.d.ts +2 -0
  152. package/dist/server/routes/models.remote.test.d.ts.map +1 -0
  153. package/dist/server/routes/models.remote.test.js +101 -0
  154. package/dist/server/routes/models.remote.test.js.map +1 -0
  155. package/dist/server/routes/project-transfer.d.ts +16 -0
  156. package/dist/server/routes/project-transfer.d.ts.map +1 -0
  157. package/dist/server/routes/project-transfer.js +141 -0
  158. package/dist/server/routes/project-transfer.js.map +1 -0
  159. package/dist/server/routes/project-transfer.test.d.ts +2 -0
  160. package/dist/server/routes/project-transfer.test.d.ts.map +1 -0
  161. package/dist/server/routes/project-transfer.test.js +199 -0
  162. package/dist/server/routes/project-transfer.test.js.map +1 -0
  163. package/dist/server/routes/projects.d.ts +2 -1
  164. package/dist/server/routes/projects.d.ts.map +1 -1
  165. package/dist/server/routes/projects.js +101 -5
  166. package/dist/server/routes/projects.js.map +1 -1
  167. package/dist/server/routes/providers.d.ts +1 -1
  168. package/dist/server/routes/providers.d.ts.map +1 -1
  169. package/dist/server/routes/providers.js +3 -1
  170. package/dist/server/routes/providers.js.map +1 -1
  171. package/dist/server/routes/providers.test.d.ts +2 -0
  172. package/dist/server/routes/providers.test.d.ts.map +1 -0
  173. package/dist/server/routes/providers.test.js +58 -0
  174. package/dist/server/routes/providers.test.js.map +1 -0
  175. package/dist/server/routes/queue.d.ts +2 -1
  176. package/dist/server/routes/queue.d.ts.map +1 -1
  177. package/dist/server/routes/queue.js +89 -1
  178. package/dist/server/routes/queue.js.map +1 -1
  179. package/dist/server/routes/sessions.d.ts +8 -1
  180. package/dist/server/routes/sessions.d.ts.map +1 -1
  181. package/dist/server/routes/sessions.js +202 -9
  182. package/dist/server/routes/sessions.js.map +1 -1
  183. package/dist/server/serialize.d.ts +2 -0
  184. package/dist/server/serialize.d.ts.map +1 -1
  185. package/dist/server/serialize.js +2 -0
  186. package/dist/server/serialize.js.map +1 -1
  187. package/package.json +6 -2
@@ -0,0 +1,229 @@
1
+ // OIDC Device Flow (RFC 8628) клиент для гибридного режима.
2
+ // Пользователь авторизуется на внешнем устройстве: мы получаем user_code + verification_uri,
3
+ // показываем их в CLI, после подтверждения — обмениваем device_code на токен.
4
+ import { RemoteError } from './types.js';
5
+ /** Таймаут HTTP-запросов к серверу авторизации. */
6
+ const AUTH_HTTP_TIMEOUT_MS = 15_000;
7
+ /** Общий таймаут ожидания подтверждения пользователем. */
8
+ const DEFAULT_POLL_TIMEOUT_MS = 5 * 60 * 1000;
9
+ /** Дополнительная задержка при slow_down (RFC 8628 §3.5). */
10
+ const SLOW_DOWN_EXTRA_MS = 5_000;
11
+ function str(value) {
12
+ return typeof value === 'string' ? value : undefined;
13
+ }
14
+ function num(value) {
15
+ if (typeof value === 'number')
16
+ return value;
17
+ if (typeof value === 'string' && value !== '')
18
+ return Number(value);
19
+ return undefined;
20
+ }
21
+ async function getJson(url) {
22
+ let res;
23
+ try {
24
+ res = await fetch(url, { signal: AbortSignal.timeout(AUTH_HTTP_TIMEOUT_MS) });
25
+ }
26
+ catch (err) {
27
+ const reason = err instanceof Error ? err.message : String(err);
28
+ throw new RemoteError(`Сервер авторизации недоступен: ${reason}`, 'AUTH');
29
+ }
30
+ if (!res.ok) {
31
+ throw new RemoteError(`Не удалось получить конфигурацию OIDC-провайдера (HTTP ${res.status})`, 'AUTH');
32
+ }
33
+ return (await res.json());
34
+ }
35
+ export async function postForm(url, params, opts = {}) {
36
+ let res;
37
+ try {
38
+ res = await fetch(url, {
39
+ method: 'POST',
40
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
41
+ body: params,
42
+ signal: AbortSignal.timeout(AUTH_HTTP_TIMEOUT_MS),
43
+ });
44
+ }
45
+ catch (err) {
46
+ const reason = err instanceof Error ? err.message : String(err);
47
+ throw new RemoteError(`Сервер авторизации недоступен: ${reason}`, opts.networkErrorCode ?? 'AUTH');
48
+ }
49
+ let body = {};
50
+ try {
51
+ body = (await res.json());
52
+ }
53
+ catch {
54
+ // пустое/не-JSON тело — оставляем {}, ошибку сформируем по статусу
55
+ }
56
+ if (!res.ok && !body.error) {
57
+ throw new RemoteError(`Сервер авторизации вернул ошибку (HTTP ${res.status})`, 'AUTH');
58
+ }
59
+ return body;
60
+ }
61
+ /**
62
+ * Получить конфигурацию OIDC-провайдера: GET {issuer}/.well-known/openid-configuration.
63
+ * Возвращает только endpoint-ы, нужные для device flow.
64
+ */
65
+ export async function discover(issuer) {
66
+ const base = issuer.replace(/\/$/, '');
67
+ const data = await getJson(`${base}/.well-known/openid-configuration`);
68
+ const tokenEndpoint = str(data.token_endpoint);
69
+ const deviceEndpoint = str(data.device_authorization_endpoint);
70
+ if (!tokenEndpoint || !deviceEndpoint) {
71
+ throw new RemoteError('OIDC-провайдер не поддерживает device flow (нет token_endpoint или device_authorization_endpoint)', 'AUTH');
72
+ }
73
+ return {
74
+ token_endpoint: tokenEndpoint,
75
+ device_authorization_endpoint: deviceEndpoint,
76
+ issuer: base,
77
+ };
78
+ }
79
+ /**
80
+ * Начать device flow: запросить device_code/user_code у сервера авторизации.
81
+ * Возвращает сессию, по которой нужно показать пользователю user_code и verification_uri.
82
+ */
83
+ export async function startDeviceFlow(issuer, clientId) {
84
+ const disc = await discover(issuer);
85
+ const params = new URLSearchParams({
86
+ client_id: clientId,
87
+ scope: 'openid profile',
88
+ });
89
+ const body = await postForm(disc.device_authorization_endpoint, params);
90
+ const authError = str(body.error);
91
+ if (authError) {
92
+ // RFC 8628 §3.1: сервер может вернуть ошибку (например, unauthorized_client
93
+ // для confidential-клиента). Показываем её вместо generic-сообщения.
94
+ const detail = str(body.error_description) ?? authError;
95
+ throw new RemoteError(`Сервер авторизации не выдал device-код: ${authError} — ${detail} (проверьте client_id и тип клиента)`, 'AUTH');
96
+ }
97
+ const deviceCode = str(body.device_code);
98
+ const userCode = str(body.user_code);
99
+ const verificationUri = str(body.verification_uri);
100
+ if (!deviceCode || !userCode || !verificationUri) {
101
+ throw new RemoteError('Сервер авторизации вернул некорректный ответ (нет device_code/user_code/verification_uri)', 'AUTH');
102
+ }
103
+ return {
104
+ deviceCode,
105
+ userCode,
106
+ verificationUri,
107
+ verificationUriComplete: str(body.verification_uri_complete),
108
+ expiresIn: num(body.expires_in) ?? 0,
109
+ interval: num(body.interval) ?? 5,
110
+ tokenEndpoint: disc.token_endpoint,
111
+ clientId,
112
+ };
113
+ }
114
+ /**
115
+ * Опрашивать token_endpoint, пока пользователь не подтвердит авторизацию.
116
+ * - authorization_pending → ждём interval и продолжаем;
117
+ * - slow_down → добавляем 5 секунд к ожиданию;
118
+ * - expired_token / access_denied → RemoteError('AUTH') с понятным сообщением.
119
+ * onPoll вызывается перед каждой попыткой (начиная с 1).
120
+ */
121
+ export async function pollDeviceToken(session, onPoll, timeoutMs = DEFAULT_POLL_TIMEOUT_MS) {
122
+ const deadline = Date.now() + timeoutMs;
123
+ let attempt = 0;
124
+ for (;;) {
125
+ attempt += 1;
126
+ if (Date.now() > deadline) {
127
+ throw new RemoteError('Истёк таймаут ожидания авторизации. Запустите авторизацию заново.', 'AUTH');
128
+ }
129
+ if (attempt > 1) {
130
+ await sleep(session.interval * 1000);
131
+ }
132
+ onPoll?.(attempt);
133
+ const params = new URLSearchParams({
134
+ grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
135
+ device_code: session.deviceCode,
136
+ client_id: session.clientId,
137
+ });
138
+ const body = await postForm(session.tokenEndpoint, params);
139
+ const accessToken = str(body.access_token);
140
+ if (accessToken) {
141
+ return {
142
+ accessToken,
143
+ refreshToken: str(body.refresh_token),
144
+ expiresIn: num(body.expires_in),
145
+ };
146
+ }
147
+ const err = str(body.error);
148
+ switch (err) {
149
+ case 'authorization_pending':
150
+ continue;
151
+ case 'slow_down':
152
+ await sleep(SLOW_DOWN_EXTRA_MS);
153
+ continue;
154
+ case 'expired_token':
155
+ throw new RemoteError('Срок действия кода устройства истёк. Запустите авторизацию заново.', 'AUTH');
156
+ case 'access_denied':
157
+ throw new RemoteError('Доступ отклонён пользователем.', 'AUTH');
158
+ default:
159
+ throw new RemoteError(`Сервер авторизации вернул ошибку: ${err ?? 'неизвестная'}`, 'AUTH');
160
+ }
161
+ }
162
+ }
163
+ /**
164
+ * Обновить access token по refresh token (grant_type=refresh_token, RFC 6749 §6).
165
+ * Ошибки (в т.ч. invalid_grant) → RemoteError('AUTH') с понятным сообщением.
166
+ */
167
+ export async function refreshAccessToken(issuer, clientId, refreshToken) {
168
+ const disc = await discover(issuer);
169
+ const params = new URLSearchParams({
170
+ grant_type: 'refresh_token',
171
+ refresh_token: refreshToken,
172
+ client_id: clientId,
173
+ });
174
+ const body = await postForm(disc.token_endpoint, params);
175
+ const accessToken = str(body.access_token);
176
+ if (!accessToken) {
177
+ const err = str(body.error);
178
+ const detail = err === 'invalid_grant'
179
+ ? 'refresh token недействителен или истёк'
180
+ : (err ?? 'неизвестная ошибка');
181
+ throw new RemoteError(`Не удалось обновить токен: ${detail}`, 'AUTH');
182
+ }
183
+ return {
184
+ accessToken,
185
+ refreshToken: str(body.refresh_token),
186
+ expiresIn: num(body.expires_in),
187
+ };
188
+ }
189
+ /**
190
+ * Вход по логину/паролю (ROPC, grant_type=password, RFC 6749 §4.3).
191
+ * Используется в гибридном режиме для «единой личности»: локальный core
192
+ * логинится как серверный пользователь Keycloak (public-клиент с
193
+ * directAccessGrantsEnabled). Токен-эндпоинт — стандартный путь Keycloak
194
+ * {issuer}/protocol/openid-connect/token.
195
+ * Ошибки аутентификации (invalid_grant/invalid_client) → RemoteError('AUTH')
196
+ * с понятным русским сообщением; сеть недоступна → RemoteError('OFFLINE').
197
+ */
198
+ export async function passwordGrant(issuer, clientId, username, password) {
199
+ const base = issuer.replace(/\/+$/, '');
200
+ const params = new URLSearchParams({
201
+ grant_type: 'password',
202
+ client_id: clientId,
203
+ username,
204
+ password,
205
+ });
206
+ // Сеть недоступна → OFFLINE (а не AUTH, как у postForm по умолчанию):
207
+ // ROPC-логин из runtime-эндпоинта должен различать «неверные учётные данные»
208
+ // и «сервер авторизации недоступен».
209
+ const body = await postForm(`${base}/protocol/openid-connect/token`, params, {
210
+ networkErrorCode: 'OFFLINE',
211
+ });
212
+ const accessToken = str(body.access_token);
213
+ if (!accessToken) {
214
+ const err = str(body.error);
215
+ const detail = err === 'invalid_grant' || err === 'invalid_client'
216
+ ? 'Неверный логин или пароль'
217
+ : (err ?? 'неизвестная ошибка');
218
+ throw new RemoteError(`Не удалось войти: ${detail}`, 'AUTH');
219
+ }
220
+ return {
221
+ accessToken,
222
+ refreshToken: str(body.refresh_token),
223
+ expiresIn: num(body.expires_in),
224
+ };
225
+ }
226
+ function sleep(ms) {
227
+ return new Promise((resolve) => setTimeout(resolve, ms));
228
+ }
229
+ //# sourceMappingURL=device-flow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-flow.js","sourceRoot":"","sources":["../../src/remote/device-flow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=device-flow.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-flow.test.d.ts","sourceRoot":"","sources":["../../src/remote/device-flow.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,103 @@
1
+ import { describe, it, expect, afterEach, vi } from 'vitest';
2
+ import { refreshAccessToken, startDeviceFlow } from './device-flow.js';
3
+ import { RemoteError } from './types.js';
4
+ function jsonResponse(data, status = 200) {
5
+ return new Response(JSON.stringify(data), {
6
+ status,
7
+ headers: { 'Content-Type': 'application/json' },
8
+ });
9
+ }
10
+ const DISCOVERY = {
11
+ issuer: 'https://issuer.test',
12
+ token_endpoint: 'https://issuer.test/token',
13
+ device_authorization_endpoint: 'https://issuer.test/device',
14
+ };
15
+ afterEach(() => {
16
+ vi.unstubAllGlobals();
17
+ });
18
+ describe('startDeviceFlow', () => {
19
+ it('unauthorized_client на device endpoint → RemoteError(AUTH) с текстом ошибки сервера', async () => {
20
+ const fetchMock = vi.fn((url, _init) => {
21
+ const u = String(url);
22
+ if (u.includes('.well-known/openid-configuration')) {
23
+ return Promise.resolve(jsonResponse(DISCOVERY));
24
+ }
25
+ if (u.includes('/device')) {
26
+ return Promise.resolve(jsonResponse({
27
+ error: 'unauthorized_client',
28
+ error_description: 'Invalid client or Invalid client credentials',
29
+ }, 400));
30
+ }
31
+ return Promise.resolve(jsonResponse({ error: 'not_found' }, 404));
32
+ });
33
+ vi.stubGlobal('fetch', fetchMock);
34
+ const err = await startDeviceFlow('https://issuer.test', 'bad-client').catch((e) => e);
35
+ expect(err).toBeInstanceOf(RemoteError);
36
+ expect(err.code).toBe('AUTH');
37
+ expect(err.message).toContain('unauthorized_client');
38
+ expect(err.message).not.toContain('нет device_code');
39
+ });
40
+ });
41
+ describe('refreshAccessToken', () => {
42
+ it('успешный refresh: возвращает новый access token и шлёт refresh-грант на token_endpoint', async () => {
43
+ const fetchMock = vi.fn((url, _init) => {
44
+ const u = String(url);
45
+ if (u.includes('.well-known/openid-configuration')) {
46
+ return Promise.resolve(jsonResponse(DISCOVERY));
47
+ }
48
+ if (u.includes('/token')) {
49
+ return Promise.resolve(jsonResponse({
50
+ access_token: 'at-new',
51
+ refresh_token: 'rt-new',
52
+ expires_in: 3600,
53
+ }));
54
+ }
55
+ return Promise.resolve(jsonResponse({ error: 'not_found' }, 404));
56
+ });
57
+ vi.stubGlobal('fetch', fetchMock);
58
+ const result = await refreshAccessToken('https://issuer.test', 'client-1', 'rt-old');
59
+ expect(result).toEqual({ accessToken: 'at-new', refreshToken: 'rt-new', expiresIn: 3600 });
60
+ const tokenCall = fetchMock.mock.calls.find(([u]) => String(u).includes('/token'));
61
+ expect(tokenCall).toBeDefined();
62
+ const [, init] = tokenCall;
63
+ expect(init.method).toBe('POST');
64
+ const body = init.body;
65
+ expect(body).toBeInstanceOf(URLSearchParams);
66
+ const params = body;
67
+ expect(params.get('grant_type')).toBe('refresh_token');
68
+ expect(params.get('refresh_token')).toBe('rt-old');
69
+ expect(params.get('client_id')).toBe('client-1');
70
+ });
71
+ it('refresh без нового refresh_token: возвращает только access token', async () => {
72
+ const fetchMock = vi.fn((url) => {
73
+ const u = String(url);
74
+ if (u.includes('.well-known/openid-configuration')) {
75
+ return Promise.resolve(jsonResponse(DISCOVERY));
76
+ }
77
+ return Promise.resolve(jsonResponse({ access_token: 'at-new', expires_in: 300 }));
78
+ });
79
+ vi.stubGlobal('fetch', fetchMock);
80
+ const result = await refreshAccessToken('https://issuer.test', 'client-1', 'rt-old');
81
+ expect(result).toEqual({ accessToken: 'at-new', refreshToken: undefined, expiresIn: 300 });
82
+ });
83
+ it('invalid_grant → RemoteError(AUTH)', async () => {
84
+ const fetchMock = vi.fn((url, _init) => {
85
+ const u = String(url);
86
+ if (u.includes('.well-known/openid-configuration')) {
87
+ return Promise.resolve(jsonResponse(DISCOVERY));
88
+ }
89
+ return Promise.resolve(jsonResponse({ error: 'invalid_grant' }, 400));
90
+ });
91
+ vi.stubGlobal('fetch', fetchMock);
92
+ const err = await refreshAccessToken('https://issuer.test', 'client-1', 'rt-dead').catch((e) => e);
93
+ expect(err).toBeInstanceOf(RemoteError);
94
+ expect(err.code).toBe('AUTH');
95
+ });
96
+ it('сервер авторизации недоступен → RemoteError(AUTH)', async () => {
97
+ vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new TypeError('Failed to fetch')));
98
+ const err = await refreshAccessToken('https://issuer.test', 'client-1', 'rt-old').catch((e) => e);
99
+ expect(err).toBeInstanceOf(RemoteError);
100
+ expect(err.code).toBe('AUTH');
101
+ });
102
+ });
103
+ //# sourceMappingURL=device-flow.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-flow.test.js","sourceRoot":"","sources":["../../src/remote/device-flow.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import type { BusEvent } from '@itphx/synth-shared';
2
+ import type { RemoteBackendClient } from './client.js';
3
+ import type { EventBus } from '../events/bus.js';
4
+ export interface RemoteEventBridgeOptions {
5
+ /** Клиент к удалённому backend: для запроса GET /api/v2/events. */
6
+ client: RemoteBackendClient;
7
+ /** Поставщик токена (обычно RemoteSession.getToken) — автообновление в рантайме. */
8
+ getToken: () => Promise<string | null>;
9
+ /** Локальный EventBus, куда публикуем переписанные события. */
10
+ eventBus: EventBus;
11
+ /** URL удалённого backend (для логов/диагностики). */
12
+ backendUrl: string;
13
+ /** Базовая задержка реконнекта (по умолчанию 2000 мс). */
14
+ reconnectDelayMs?: number;
15
+ /** Максимальная задержка реконнекта (по умолчанию 60000 мс). */
16
+ maxReconnectDelayMs?: number;
17
+ /** Селектор: какие события транслировать (по умолчанию все). */
18
+ shouldForward?: (event: BusEvent) => boolean;
19
+ }
20
+ export declare class RemoteEventBridge {
21
+ onStatusChange?: (connected: boolean) => void;
22
+ private readonly _client;
23
+ private readonly _getToken;
24
+ private readonly _eventBus;
25
+ private readonly _backendUrl;
26
+ private readonly _reconnectDelayMs;
27
+ private readonly _maxReconnectDelayMs;
28
+ private readonly _shouldForward;
29
+ private _controller;
30
+ private _reconnectTimer;
31
+ private _attempt;
32
+ private _stopped;
33
+ private _connected;
34
+ constructor(options: RemoteEventBridgeOptions);
35
+ get connected(): boolean;
36
+ start(): void;
37
+ stop(): void;
38
+ private _setConnected;
39
+ private _connect;
40
+ private _scheduleReconnect;
41
+ private _processBlock;
42
+ private _forward;
43
+ /** Переписать вложенные ссылки на сущности backend в `remote:<...>` (best-effort, только строки). */
44
+ private _rewriteData;
45
+ }
46
+ //# sourceMappingURL=event-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-bridge.d.ts","sourceRoot":"","sources":["../../src/remote/event-bridge.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,QAAQ,EAAa,MAAM,qBAAqB,CAAC;AAE/D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEvD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,MAAM,WAAW,wBAAwB;IACvC,mEAAmE;IACnE,MAAM,EAAE,mBAAmB,CAAC;IAC5B,oFAAoF;IACpF,QAAQ,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,+DAA+D;IAC/D,QAAQ,EAAE,QAAQ,CAAC;IACnB,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gEAAgE;IAChE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,gEAAgE;IAChE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,OAAO,CAAC;CAC9C;AAQD,qBAAa,iBAAiB;IAC5B,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;IAE9C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA+B;IACzD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAS;IAC9C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA+B;IAE9D,OAAO,CAAC,WAAW,CAAgC;IACnD,OAAO,CAAC,eAAe,CAA8C;IACrE,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAAS;gBAEf,OAAO,EAAE,wBAAwB;IAU7C,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,KAAK,IAAI,IAAI;IAOb,IAAI,IAAI,IAAI;IAWZ,OAAO,CAAC,aAAa;YAMP,QAAQ;IA6EtB,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,aAAa;IAqBrB,OAAO,CAAC,QAAQ;IAyBhB,qGAAqG;IACrG,OAAO,CAAC,YAAY;CAoBrB"}
@@ -0,0 +1,218 @@
1
+ // RemoteEventBridge — мост событий удалённого backend в локальный EventBus (гибрид, Часть 7).
2
+ // Подписывается на SSE GET /api/v2/events удалённого backend, переписывает id
3
+ // в `remote:<...>` и публикует в локальный EventBus → локальный /api/v2/events (SSE/WS) → UI.
4
+ //
5
+ // Фильтр контента по умолчанию: форвардим ВСЕ события. UI рендерит контент только из
6
+ // событий `stream.chunk`, приходящих через transport (WS/SSE /api/v2/events); тело
7
+ // prompt-стрима UI не читает (enqueueMessage отбрасывает response.body), а событий
8
+ // `message.stream`/`stream.content` в кодовой базе нет. Значит в гибриде мост — ЕДИНСТВЕННЫЙ
9
+ // путь контента до UI, исключать stream.chunk нельзя (иначе двойного вывода не будет,
10
+ // а контента не будет вовсе).
11
+ import { nowISO } from '@itphx/synth-shared';
12
+ import { toRemoteRef } from './aggregate.js';
13
+ const DEFAULT_RECONNECT_DELAY_MS = 2_000;
14
+ const DEFAULT_MAX_RECONNECT_DELAY_MS = 60_000;
15
+ /** Ключи data, являющиеся ссылками на сущности удалённого backend → переписываем в remote:<...>. */
16
+ const REWRITE_KEYS = ['session_id', 'project_id', 'parent_id', 'child_session_id', 'parent_session_id', 'id'];
17
+ export class RemoteEventBridge {
18
+ onStatusChange;
19
+ _client;
20
+ _getToken;
21
+ _eventBus;
22
+ _backendUrl;
23
+ _reconnectDelayMs;
24
+ _maxReconnectDelayMs;
25
+ _shouldForward;
26
+ _controller = null;
27
+ _reconnectTimer = null;
28
+ _attempt = 0;
29
+ _stopped = false;
30
+ _connected = false;
31
+ constructor(options) {
32
+ this._client = options.client;
33
+ this._getToken = options.getToken;
34
+ this._eventBus = options.eventBus;
35
+ this._backendUrl = options.backendUrl;
36
+ this._reconnectDelayMs = options.reconnectDelayMs ?? DEFAULT_RECONNECT_DELAY_MS;
37
+ this._maxReconnectDelayMs = options.maxReconnectDelayMs ?? DEFAULT_MAX_RECONNECT_DELAY_MS;
38
+ this._shouldForward = options.shouldForward ?? (() => true);
39
+ }
40
+ get connected() {
41
+ return this._connected;
42
+ }
43
+ start() {
44
+ this._stopped = false;
45
+ // Уже подключены/подключаемся или реконнект запланирован — не плодим дубли.
46
+ if (this._controller || this._reconnectTimer)
47
+ return;
48
+ void this._connect();
49
+ }
50
+ stop() {
51
+ this._stopped = true;
52
+ this._controller?.abort();
53
+ this._controller = null;
54
+ if (this._reconnectTimer) {
55
+ clearTimeout(this._reconnectTimer);
56
+ this._reconnectTimer = null;
57
+ }
58
+ this._setConnected(false);
59
+ }
60
+ _setConnected(value) {
61
+ if (this._connected === value)
62
+ return;
63
+ this._connected = value;
64
+ this.onStatusChange?.(value);
65
+ }
66
+ async _connect() {
67
+ if (this._stopped)
68
+ return;
69
+ const controller = new AbortController();
70
+ this._controller = controller;
71
+ let res;
72
+ try {
73
+ res = await this._client.request('GET', '/api/v2/events', undefined, {
74
+ raw: true,
75
+ timeoutMs: 0,
76
+ signal: controller.signal,
77
+ });
78
+ }
79
+ catch {
80
+ // Сеть недоступна / 401 / 5xx — откат и реконнект с backoff.
81
+ this._controller = null;
82
+ if (!this._stopped && !controller.signal.aborted) {
83
+ this._setConnected(false);
84
+ this._scheduleReconnect();
85
+ }
86
+ return;
87
+ }
88
+ if (!res.ok || !res.body) {
89
+ this._controller = null;
90
+ if (!this._stopped && !controller.signal.aborted) {
91
+ this._setConnected(false);
92
+ this._scheduleReconnect();
93
+ }
94
+ return;
95
+ }
96
+ // Стрим открыт.
97
+ this._setConnected(true);
98
+ console.debug(`[RemoteEventBridge] подключено к событиям ${this._backendUrl}`);
99
+ try {
100
+ const reader = res.body.getReader();
101
+ const decoder = new TextDecoder();
102
+ let buffer = '';
103
+ while (!controller.signal.aborted) {
104
+ const { done, value } = await reader.read();
105
+ if (done)
106
+ break; // backend закрыл стрим → реконнект
107
+ buffer += decoder.decode(value, { stream: true });
108
+ // SSE-блоки разделяются пустой строкой; keepalive-комментарии (строки с ':')
109
+ // отсекаются в _processBlock.
110
+ const blocks = buffer.split('\n\n');
111
+ buffer = blocks.pop() ?? '';
112
+ for (const block of blocks) {
113
+ this._processBlock(block);
114
+ }
115
+ }
116
+ // Остаточный буфер без завершающего \n\n (обрыв посреди события) — обрабатываем.
117
+ // Флаш декодера: многобайтовый символ, разорванный на границе чанка, мог
118
+ // остаться в декодере, а не в буфере.
119
+ buffer += decoder.decode();
120
+ if (buffer.trim()) {
121
+ this._processBlock(buffer);
122
+ }
123
+ if (!controller.signal.aborted) {
124
+ try {
125
+ await reader.cancel();
126
+ }
127
+ catch { /* ignore */ }
128
+ }
129
+ }
130
+ catch {
131
+ // Ошибка чтения (обрыв сети) → реконнект.
132
+ }
133
+ finally {
134
+ this._controller = null;
135
+ if (!this._stopped && !controller.signal.aborted) {
136
+ this._setConnected(false);
137
+ this._scheduleReconnect();
138
+ }
139
+ }
140
+ }
141
+ _scheduleReconnect() {
142
+ if (this._stopped || this._reconnectTimer)
143
+ return;
144
+ const delay = Math.min(this._reconnectDelayMs * 2 ** this._attempt, this._maxReconnectDelayMs);
145
+ this._attempt += 1;
146
+ console.debug(`[RemoteEventBridge] реконнект через ${delay} мс (попытка ${this._attempt})`);
147
+ this._reconnectTimer = setTimeout(() => {
148
+ this._reconnectTimer = null;
149
+ void this._connect();
150
+ }, delay);
151
+ }
152
+ _processBlock(block) {
153
+ const dataLines = [];
154
+ for (const line of block.split('\n')) {
155
+ if (line.startsWith('data: ')) {
156
+ dataLines.push(line.slice('data: '.length));
157
+ }
158
+ // `event:` и `:keepalive` игнорируем — тип события внутри JSON (data).
159
+ }
160
+ if (dataLines.length === 0)
161
+ return;
162
+ const raw = dataLines.join('\n');
163
+ let parsed;
164
+ try {
165
+ parsed = JSON.parse(raw);
166
+ }
167
+ catch {
168
+ console.debug('[RemoteEventBridge] невалидный JSON в SSE-событии, пропускаю');
169
+ return;
170
+ }
171
+ this._forward(parsed);
172
+ }
173
+ _forward(raw) {
174
+ if (typeof raw.sessionId !== 'string')
175
+ return;
176
+ const type = raw.type;
177
+ if (typeof type !== 'string')
178
+ return;
179
+ // Получили валидное событие — стрим живой: backoff начинается заново
180
+ // (иначе стрим, открывающийся и сразу падающий без событий, давал бы
181
+ // бесконечные быстрые реконнекты на базовой задержке).
182
+ this._attempt = 0;
183
+ const data = (typeof raw.data === 'object' && raw.data !== null ? raw.data : {});
184
+ const rewrittenSessionId = toRemoteRef(raw.sessionId);
185
+ const rewrittenData = this._rewriteData(data);
186
+ const event = {
187
+ type: type,
188
+ sessionId: rewrittenSessionId,
189
+ data: rewrittenData,
190
+ timestamp: typeof raw.timestamp === 'string' ? raw.timestamp : nowISO(),
191
+ };
192
+ if (!this._shouldForward(event))
193
+ return;
194
+ this._eventBus.publish(rewrittenSessionId, event.type, event.data);
195
+ }
196
+ /** Переписать вложенные ссылки на сущности backend в `remote:<...>` (best-effort, только строки). */
197
+ _rewriteData(data) {
198
+ const out = { ...data };
199
+ for (const key of REWRITE_KEYS) {
200
+ const value = out[key];
201
+ if (typeof value === 'string') {
202
+ out[key] = toRemoteRef(value);
203
+ }
204
+ }
205
+ if (Array.isArray(out.references)) {
206
+ out.references = out.references.map((ref) => {
207
+ if (ref && typeof ref === 'object' && typeof ref.session_id === 'string') {
208
+ const r = { ...ref };
209
+ r.session_id = toRemoteRef(r.session_id);
210
+ return r;
211
+ }
212
+ return ref;
213
+ });
214
+ }
215
+ return out;
216
+ }
217
+ }
218
+ //# sourceMappingURL=event-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-bridge.js","sourceRoot":"","sources":["../../src/remote/event-bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=event-bridge.test.d.ts.map