tensorgrid-ui 1.1.0 → 1.1.2

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.
@@ -12,6 +12,13 @@
12
12
  *
13
13
  * Импортируются только встроенные модули Node — строка композиции грузится
14
14
  * раньше, чем о пакете что-либо известно.
15
+ *
16
+ * Функции принимают САМИ СЛУЖБЫ, а не контекст. В Cordis чтение
17
+ * `ctx.authorization` как свойства требует объявить службу в `inject`, а наш
18
+ * ряд объявляет только `webServer`: стойка входа необязательна и может
19
+ * отсутствовать вовсе. Читать её полагается через `ctx.get(...)`, и дальше
20
+ * передавать результат — иначе внутри снова окажется запрещённое обращение,
21
+ * и Cordis ответит «cannot get property "authorization" without inject».
15
22
  */
16
23
 
17
24
  /** Текущая попытка. null, пока никто не входил. */
@@ -72,9 +79,8 @@ function view() {
72
79
  * попыток: пользователь мог войти в прошлый раз, в другом профиле или
73
80
  * вообще через терминал.
74
81
  */
75
- export async function listProviders(ctx) {
76
- const entries = ctx.authorization.list()
77
- const credentials = ctx.get('credentials')
82
+ export async function listProviders(authorization, credentials) {
83
+ const entries = authorization.list()
78
84
 
79
85
  let configured = new Set()
80
86
  if (credentials !== undefined) {
@@ -99,7 +105,7 @@ export async function listProviders(ctx) {
99
105
  }
100
106
 
101
107
  /** Начинает вход. Предыдущая завершённая попытка забывается. */
102
- export function begin(ctx, key, method) {
108
+ export function begin(authorization, key, method) {
103
109
  if (attempt !== null && attempt.outcome === null && attempt.problem === null) {
104
110
  return { problem: 'одна попытка уже идёт', attempt: view() }
105
111
  }
@@ -122,7 +128,7 @@ export function begin(ctx, key, method) {
122
128
  }
123
129
  if (typeof method === 'string' && method !== '') request.method = method
124
130
 
125
- ctx.authorization.begin(request).then(
131
+ authorization.begin(request).then(
126
132
  (outcome) => {
127
133
  current.outcome = String(outcome.status)
128
134
  current.prompt = null
@@ -155,10 +161,10 @@ export function answer(value) {
155
161
  * ключ занятым, и осиротевшая попытка иначе блокирует повторный вход до
156
162
  * перезапуска.
157
163
  */
158
- export function cancel(ctx, key) {
164
+ export function cancel(authorization, key) {
159
165
  const target = key ?? attempt?.key
160
166
  if (typeof target !== 'string') return { problem: 'нечего отменять', attempt: view() }
161
- ctx.authorization.cancel(target)
167
+ authorization.cancel(target)
162
168
  if (attempt !== null && attempt.key === target) {
163
169
  attempt.problem = attempt.problem ?? 'отменено'
164
170
  attempt.prompt = null
package/lib/client.js CHANGED
@@ -188,6 +188,9 @@ window.__ModuleLoader__.load({
188
188
  'auth.done': 'Signed in.',
189
189
  'auth.cancelled': 'Sign-in cancelled.',
190
190
  'auth.unavailable': 'Sign-in is unavailable: the authorization row is not mounted in this profile.',
191
+ 'auth.needsRestart': 'Restart the application: the browser already has the new version, the server does not yet.',
192
+ 'auth.badResponse': 'The server answered {status} instead of data.',
193
+ 'auth.empty': 'No provider offers subscription sign-in.',
191
194
  'update.title': 'Updates',
192
195
  'update.check': 'Check',
193
196
  'update.apply': 'Update',
@@ -230,6 +233,9 @@ window.__ModuleLoader__.load({
230
233
  'auth.done': '已登录。',
231
234
  'auth.cancelled': '登录已取消。',
232
235
  'auth.unavailable': '无法登录:此配置文件未挂载授权行。',
236
+ 'auth.needsRestart': '请重启应用:浏览器已是新版本,服务端还不是。',
237
+ 'auth.badResponse': '服务端返回 {status},而不是数据。',
238
+ 'auth.empty': '没有服务商提供订阅登录。',
233
239
  'update.title': '更新',
234
240
  'update.check': '检查',
235
241
  'update.apply': '更新',
@@ -1569,6 +1575,9 @@ window.__ModuleLoader__.load({
1569
1575
  'auth.done': 'Вход выполнен.',
1570
1576
  'auth.cancelled': 'Вход отменён.',
1571
1577
  'auth.unavailable': 'Вход недоступен: в профиле не смонтирована строка авторизации.',
1578
+ 'auth.needsRestart': 'Перезапустите приложение: у браузера уже новая версия, у сервера ещё нет.',
1579
+ 'auth.badResponse': 'Сервер ответил {status} вместо данных.',
1580
+ 'auth.empty': 'Ни один провайдер не предлагает вход по подписке.',
1572
1581
  'update.title': 'Обновления',
1573
1582
  'update.check': 'Проверить',
1574
1583
  'update.apply': 'Обновить',
@@ -1945,8 +1954,23 @@ window.__ModuleLoader__.load({
1945
1954
  headers: { 'content-type': 'application/json' },
1946
1955
  body: JSON.stringify(body),
1947
1956
  })
1957
+
1958
+ // Маршрут поднимает host-половина, а она читается только при старте
1959
+ // приложения. После обновления браузер получает новый код раньше
1960
+ // Host-а, запрос уходит в общий заслон и возвращает 401 страницей, а
1961
+ // не JSON. Без этой проверки строка молча показывала пустоту.
1962
+ const type = response.headers.get('content-type') || ''
1963
+ if (!type.includes('application/json')) {
1964
+ return {
1965
+ available: true,
1966
+ providers: [],
1967
+ problem: response.status === 401 || response.status === 404
1968
+ ? t('auth.needsRestart')
1969
+ : t('auth.badResponse').replace('{status}', String(response.status)),
1970
+ }
1971
+ }
1948
1972
  return response.json()
1949
- }, [])
1973
+ }, [t])
1950
1974
 
1951
1975
  const refresh = React.useCallback(async (body) => {
1952
1976
  setBusy(true)
@@ -2069,6 +2093,12 @@ window.__ModuleLoader__.load({
2069
2093
  jsx('div', { className: 'dsx-setting__hint', children: t('auth.hint') }),
2070
2094
  ],
2071
2095
  }),
2096
+ state !== null && state.problem && (attempt === null || !attempt.problem)
2097
+ ? jsx('div', { className: 'dsx-setting__hint dsx-setting__hint--warn', children: state.problem })
2098
+ : null,
2099
+ state !== null && !state.problem && subscription.length === 0
2100
+ ? jsx('div', { className: 'dsx-setting__hint', children: t('auth.empty') })
2101
+ : null,
2072
2102
  jsx('div', {
2073
2103
  className: 'dsx-auth__list',
2074
2104
  children: subscription.map((provider) =>
package/lib/index.js CHANGED
@@ -205,9 +205,12 @@ export function apply(ctx) {
205
205
  }
206
206
 
207
207
  const bridge = await loadModule('./auth-bridge.js')
208
+ // Хранилище учётных данных читается тем же способом: как
209
+ // свойство оно потребовало бы объявления в inject.
210
+ const credentials = web.get('credentials')
208
211
 
209
212
  if (req.method === 'GET') {
210
- reply(res, 200, { available: true, ...(await bridge.listProviders(web)) })
213
+ reply(res, 200, { available: true, ...(await bridge.listProviders(authorization, credentials)) })
211
214
  return
212
215
  }
213
216
 
@@ -230,9 +233,9 @@ export function apply(ctx) {
230
233
  }
231
234
 
232
235
  let result
233
- if (body.action === 'begin') result = bridge.begin(web, String(body.key), body.method)
236
+ if (body.action === 'begin') result = bridge.begin(authorization, String(body.key), body.method)
234
237
  else if (body.action === 'answer') result = bridge.answer(body.value)
235
- else if (body.action === 'cancel') result = bridge.cancel(web, body.key)
238
+ else if (body.action === 'cancel') result = bridge.cancel(authorization, body.key)
236
239
  else if (body.action === 'poll') result = bridge.poll()
237
240
  else { reply(res, 400, { problem: `неизвестное действие: ${String(body.action)}` }); return }
238
241
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tensorgrid-ui",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "TENSOR GRID — фирменный интерфейс поверх DeepSeek Harness: палитра, живой ambient-слой, айдентика и русский язык-пакет",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",