tensorgrid-ui 1.1.1 → 1.1.3

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/index.js CHANGED
@@ -189,17 +189,35 @@ export function apply(ctx) {
189
189
 
190
190
  // ── Вход по подписке ────────────────────────────────────────────────
191
191
  //
192
- // Стойка входа монтируется отдельной строкой профиля, и её может не
193
- // быть. Берём мягко: без неё маршрут честно отвечает, что вход
194
- // недоступен, вместо того чтобы уронить весь ряд.
192
+ // Стойка входа монтируется отдельной строкой профиля, и её может не быть
193
+ // вовсе.
194
+ //
195
+ // Прочитать её через ctx.get мало: возвращённая служба остаётся под
196
+ // охраной контекста, который её не объявил, и первое же обращение к
197
+ // методу отвечает «cannot get property "list" without inject». Объявить
198
+ // стойку в inject всего ряда тоже нельзя — тогда без неё не
199
+ // смонтируется ни палитра, ни перевод.
200
+ //
201
+ // Поэтому службы забираются ВЛОЖЕННЫМ рядом и запоминаются: он ждёт их
202
+ // появления, не задерживая остальное, и ссылки остаются пригодными,
203
+ // потому что связаны с контекстом, который их объявил. Маршрут при этом
204
+ // регистрируется всегда — профиль без стойки получает внятный ответ, а
205
+ // не молчание.
206
+ let authorization = null
207
+ let credentials = null
208
+ web.inject(['authorization', 'credentials'], (bound) => {
209
+ authorization = bound.authorization
210
+ credentials = bound.credentials
211
+ bound.effect(() => () => { authorization = null; credentials = null })
212
+ })
213
+
195
214
  web.effect(() =>
196
215
  web.webServer.register({
197
216
  kind: 'exact',
198
217
  path: '/api/tensorgrid.auth',
199
218
  async handler(req, res) {
200
219
  try {
201
- const authorization = web.get('authorization')
202
- if (authorization === undefined) {
220
+ if (authorization === null) {
203
221
  reply(res, 200, { available: false, problem: 'стойка входа не смонтирована' })
204
222
  return
205
223
  }
@@ -207,7 +225,7 @@ export function apply(ctx) {
207
225
  const bridge = await loadModule('./auth-bridge.js')
208
226
 
209
227
  if (req.method === 'GET') {
210
- reply(res, 200, { available: true, ...(await bridge.listProviders(web)) })
228
+ reply(res, 200, { available: true, ...(await bridge.listProviders(authorization, credentials)) })
211
229
  return
212
230
  }
213
231
 
@@ -230,9 +248,9 @@ export function apply(ctx) {
230
248
  }
231
249
 
232
250
  let result
233
- if (body.action === 'begin') result = bridge.begin(web, String(body.key), body.method)
251
+ if (body.action === 'begin') result = bridge.begin(authorization, String(body.key), body.method)
234
252
  else if (body.action === 'answer') result = bridge.answer(body.value)
235
- else if (body.action === 'cancel') result = bridge.cancel(web, body.key)
253
+ else if (body.action === 'cancel') result = bridge.cancel(authorization, body.key)
236
254
  else if (body.action === 'poll') result = bridge.poll()
237
255
  else { reply(res, 400, { problem: `неизвестное действие: ${String(body.action)}` }); return }
238
256
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tensorgrid-ui",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "TENSOR GRID — фирменный интерфейс поверх DeepSeek Harness: палитра, живой ambient-слой, айдентика и русский язык-пакет",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",