@ouraihub/aether-worker 0.1.0 → 0.1.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouraihub/aether-worker",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "license": "LGPL-3.0-only",
5
5
  "repository": {
6
6
  "type": "git",
@@ -19,8 +19,8 @@
19
19
  "./*": "./src/*.ts"
20
20
  },
21
21
  "dependencies": {
22
- "@ouraihub/aether-contracts": "0.1.0",
23
- "@ouraihub/aether-core": "0.1.0"
22
+ "@ouraihub/aether-contracts": "0.1.1",
23
+ "@ouraihub/aether-core": "0.1.1"
24
24
  },
25
25
  "devDependencies": {},
26
26
  "files": [
@@ -35,6 +35,7 @@
35
35
  },
36
36
  "scripts": {
37
37
  "typecheck": "tsc --noEmit",
38
- "test": "tsx --test \"src/**/*.test.ts\""
38
+ "test": "tsx --test \"src/**/*.test.ts\"",
39
+ "scenarios": "tsx scenarios/run.ts"
39
40
  }
40
41
  }
package/src/app.ts CHANGED
@@ -47,7 +47,11 @@ const statuses: Record<KitErrorCode, number> = {
47
47
  function failureResponse(error: unknown): Response {
48
48
  let safe: KitError = safeError(error)
49
49
  if (safe.code === 'internal')
50
- safe = { code: 'internal', message: 'Internal request failure', details: { errorId: crypto.randomUUID() } }
50
+ safe = {
51
+ code: 'internal',
52
+ message: 'Internal request failure',
53
+ details: { errorId: crypto.randomUUID() },
54
+ }
51
55
  return Response.json(safe, { status: statuses[safe.code] })
52
56
  }
53
57
  function loopback(url: URL): boolean {
@@ -97,17 +101,25 @@ function secured(response: Response, origin: string | null, allowed: string): Re
97
101
  headers.set('Referrer-Policy', 'no-referrer')
98
102
  headers.set(
99
103
  'Cache-Control',
100
- headers.get('content-type')?.startsWith('text/event-stream') ? 'no-cache, no-transform' : 'no-store',
104
+ headers.get('content-type')?.startsWith('text/event-stream')
105
+ ? 'no-cache, no-transform'
106
+ : 'no-store',
101
107
  )
102
108
  if (origin === allowed) {
103
109
  headers.set('Access-Control-Allow-Origin', allowed)
104
110
  headers.set('Access-Control-Allow-Credentials', 'true')
105
111
  headers.append('Vary', 'Origin')
106
112
  }
107
- return new Response(response.body, { status: response.status, statusText: response.statusText, headers })
113
+ return new Response(response.body, {
114
+ status: response.status,
115
+ statusText: response.statusText,
116
+ headers,
117
+ })
108
118
  }
109
119
  /** Product entrypoints compose this handler; this package never supplies product login or business tools. */
110
- export function createAetherWorkerApp(options: WorkerAppOptions): { fetch(request: Request): Promise<Response> } {
120
+ export function createAetherWorkerApp(options: WorkerAppOptions): {
121
+ fetch(request: Request): Promise<Response>
122
+ } {
111
123
  const publicOrigin = new URL(options.origin)
112
124
  if (
113
125
  !options.applicationId ||
@@ -127,9 +139,13 @@ export function createAetherWorkerApp(options: WorkerAppOptions): { fetch(reques
127
139
  origin = raw.headers.get('origin')
128
140
  let response: Response
129
141
  try {
130
- if (url.protocol !== 'https:' && !(options.allowLoopbackHttp && url.protocol === 'http:' && loopback(url)))
142
+ if (
143
+ url.protocol !== 'https:' &&
144
+ !(options.allowLoopbackHttp && url.protocol === 'http:' && loopback(url))
145
+ )
131
146
  throw new KitFailure('forbidden', 'Secure transport required')
132
- if (origin !== null && origin !== options.origin) throw new KitFailure('forbidden', 'Origin rejected')
147
+ if (origin !== null && origin !== options.origin)
148
+ throw new KitFailure('forbidden', 'Origin rejected')
133
149
  if (raw.method === 'OPTIONS') {
134
150
  if (origin !== options.origin) throw new KitFailure('forbidden', 'Origin rejected')
135
151
  return secured(
@@ -147,12 +163,15 @@ export function createAetherWorkerApp(options: WorkerAppOptions): { fetch(reques
147
163
  }
148
164
  const mutating = !['GET', 'HEAD'].includes(raw.method)
149
165
  const publicRoute =
150
- url.pathname === '/mcp' || url.pathname.startsWith('/oauth/') || url.pathname.startsWith('/.well-known/')
166
+ url.pathname === '/mcp' ||
167
+ url.pathname.startsWith('/oauth/') ||
168
+ url.pathname.startsWith('/.well-known/')
151
169
  if (
152
170
  mutating &&
153
171
  raw.body !== null &&
154
172
  !publicRoute &&
155
- raw.headers.get('content-type')?.split(';')[0]?.trim().toLowerCase() !== 'application/json'
173
+ raw.headers.get('content-type')?.split(';')[0]?.trim().toLowerCase() !==
174
+ 'application/json'
156
175
  )
157
176
  throw badRequest('contentType', 'json_required')
158
177
  const request = await boundedRequest(raw, maximum)
@@ -162,7 +181,8 @@ export function createAetherWorkerApp(options: WorkerAppOptions): { fetch(reques
162
181
  publicResponse = await handler.handlePublic(request)
163
182
  if (publicResponse) break
164
183
  }
165
- if (!publicResponse) throw new KitFailure('not_found', 'Unknown route', { entity: 'route' })
184
+ if (!publicResponse)
185
+ throw new KitFailure('not_found', 'Unknown route', { entity: 'route' })
166
186
  response = publicResponse
167
187
  } else {
168
188
  const actor = await options.identity.resolve(request)
@@ -176,7 +196,12 @@ export function createAetherWorkerApp(options: WorkerAppOptions): { fetch(reques
176
196
  )
177
197
  throw new KitFailure('forbidden', 'Request authorization rejected')
178
198
  await options.authorizeRoute(actor, request)
179
- const context: WorkerContext = { actor: immutableActor(actor), requestId, now, signal: request.signal }
199
+ const context: WorkerContext = {
200
+ actor: immutableActor(actor),
201
+ requestId,
202
+ now,
203
+ signal: request.signal,
204
+ }
180
205
  if (url.pathname === '/api/v1/runs' && request.method === 'POST') {
181
206
  const input: unknown = await request.json()
182
207
  if (!isContract('RunRequest', input)) throw badRequest('run', 'invalid')
@@ -190,25 +215,33 @@ export function createAetherWorkerApp(options: WorkerAppOptions): { fetch(reques
190
215
  })
191
216
  const value: unknown = await accepted.json()
192
217
  if (accepted.status !== 202) {
193
- if (isContract('KitError', value)) throw new KitFailure(value.code, value.message, value.details)
218
+ if (isContract('KitError', value))
219
+ throw new KitFailure(value.code, value.message, value.details)
194
220
  throw new KitFailure('internal', 'Invalid coordinator reply')
195
221
  }
196
- if (!isContract('RunAccepted', value)) throw new KitFailure('internal', 'Invalid coordinator acceptance')
222
+ if (!isContract('RunAccepted', value))
223
+ throw new KitFailure('internal', 'Invalid coordinator acceptance')
197
224
  response = Response.json(
198
225
  { accepted: value, events: `/api/v1/runs/${encodeURIComponent(runId)}/events` },
199
226
  { status: 202 },
200
227
  )
201
228
  } else {
202
- const run = /^\/api\/v1\/runs\/([^/]+)(?:\/(events|cancel|approval))?$/u.exec(url.pathname)
229
+ const run = /^\/api\/v1\/runs\/([^/]+)(?:\/(events|cancel|approval))?$/u.exec(
230
+ url.pathname,
231
+ )
203
232
  if (run) {
204
233
  const runId = decodeURIComponent(run[1] ?? ''),
205
234
  action = run[2] ?? 'snapshot'
206
235
  if (
207
236
  !/^[A-Za-z0-9_-]{1,128}$/u.test(runId) ||
208
- (['events', 'snapshot'].includes(action) ? request.method !== 'GET' : request.method !== 'POST')
237
+ (['events', 'snapshot'].includes(action)
238
+ ? request.method !== 'GET'
239
+ : request.method !== 'POST')
209
240
  )
210
241
  throw badRequest('run', 'invalid')
211
- const stub = options.runs.get(options.runs.idFromName(`${options.applicationId}:${runId}`)),
242
+ const stub = options.runs.get(
243
+ options.runs.idFromName(`${options.applicationId}:${runId}`),
244
+ ),
212
245
  headers = new Headers({ 'x-aether-actor': actorHeader(context.actor) })
213
246
  if (request.headers.has('Last-Event-ID'))
214
247
  headers.set('Last-Event-ID', request.headers.get('Last-Event-ID') ?? '')
@@ -244,7 +277,10 @@ export function createAetherWorkerApp(options: WorkerAppOptions): { fetch(reques
244
277
  stub
245
278
  .fetch('https://aether.internal/cancel', {
246
279
  method: 'POST',
247
- headers: { 'x-aether-actor': actorHeader(context.actor), 'Content-Type': 'application/json' },
280
+ headers: {
281
+ 'x-aether-actor': actorHeader(context.actor),
282
+ 'Content-Type': 'application/json',
283
+ },
248
284
  body: '{}',
249
285
  })
250
286
  .then(async (reply) => {
package/src/context.ts CHANGED
@@ -29,14 +29,20 @@ export function isActor(value: unknown): value is Actor {
29
29
  (value.kind === 'user' || value.kind === 'service' || value.kind === 'mcp_client') &&
30
30
  'tenantId' in value &&
31
31
  (value.tenantId === null ||
32
- (typeof value.tenantId === 'string' && value.tenantId.length > 0 && value.tenantId.length <= 256)) &&
32
+ (typeof value.tenantId === 'string' &&
33
+ value.tenantId.length > 0 &&
34
+ value.tenantId.length <= 256)) &&
33
35
  'sessionId' in value &&
34
36
  (value.sessionId === null ||
35
- (typeof value.sessionId === 'string' && value.sessionId.length > 0 && value.sessionId.length <= 256)) &&
37
+ (typeof value.sessionId === 'string' &&
38
+ value.sessionId.length > 0 &&
39
+ value.sessionId.length <= 256)) &&
36
40
  'scopes' in value &&
37
41
  Array.isArray(value.scopes) &&
38
42
  value.scopes.length <= 256 &&
39
- value.scopes.every((scope: unknown) => typeof scope === 'string' && scope.length > 0 && scope.length <= 256)
43
+ value.scopes.every(
44
+ (scope: unknown) => typeof scope === 'string' && scope.length > 0 && scope.length <= 256,
45
+ )
40
46
  )
41
47
  }
42
48
  export function immutableActor(actor: Actor): Actor {
@@ -21,7 +21,9 @@ export type Action =
21
21
  | { kind: 'retry'; sourceId: string }
22
22
  | { kind: 'translation'; sourceId: string; targetLanguage: string }
23
23
  export type SelectionGuard =
24
- { kind: 'explicit' } | { kind: 'selected'; turnId: string } | { kind: 'latest'; turnId: string | null }
24
+ | { kind: 'explicit' }
25
+ | { kind: 'selected'; turnId: string }
26
+ | { kind: 'latest'; turnId: string | null }
25
27
  export interface ReservationInput {
26
28
  input: string
27
29
  images: ImagePart[]
@@ -46,12 +48,16 @@ export function authoritative(
46
48
  guard: SelectionGuard,
47
49
  document: ConversationDocument | null,
48
50
  ): ReservationInput {
49
- if ((request.conversationId ?? null) !== (document?.summary.id ?? null)) throw notFound('conversation')
51
+ if ((request.conversationId ?? null) !== (document?.summary.id ?? null))
52
+ throw notFound('conversation')
50
53
  if (document) {
51
54
  idle(document)
52
55
  if (guard.kind === 'selected' && document.summary.selectedTurnId !== guard.turnId)
53
56
  throw conflict('preparation_changed')
54
- if (guard.kind === 'latest' && (document.summary.selectedTurnId !== null || latest(document) !== guard.turnId))
57
+ if (
58
+ guard.kind === 'latest' &&
59
+ (document.summary.selectedTurnId !== null || latest(document) !== guard.turnId)
60
+ )
55
61
  throw conflict('preparation_changed')
56
62
  }
57
63
  let input = request.input,
@@ -101,7 +107,11 @@ async function digest(value: string): Promise<string> {
101
107
  const bytes = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(value))
102
108
  return Array.from(new Uint8Array(bytes), (byte) => byte.toString(16).padStart(2, '0')).join('')
103
109
  }
104
- async function imageBasis(turnId: string, kind: string, images: readonly StoredImage[]): Promise<unknown[]> {
110
+ async function imageBasis(
111
+ turnId: string,
112
+ kind: string,
113
+ images: readonly StoredImage[],
114
+ ): Promise<unknown[]> {
105
115
  return Promise.all(
106
116
  images.map(async (image, index) => ({
107
117
  turnId,
@@ -151,7 +161,11 @@ export async function fingerprint(
151
161
  })),
152
162
  )
153
163
  const currentImages = await Promise.all(
154
- input.images.map(async (image, index) => ({ index, mime: image.mime, data: await digest(image.data) })),
164
+ input.images.map(async (image, index) => ({
165
+ index,
166
+ mime: image.mime,
167
+ data: await digest(image.data),
168
+ })),
155
169
  )
156
170
  return digest(
157
171
  canonicalJson({
@@ -185,13 +199,22 @@ export async function resolveAction(
185
199
  checkText('input', request.input, 100_000)
186
200
  validateImages(request.images ?? [])
187
201
  const sourced = Boolean(request.retryTurnId || request.translateTurnId || request.regenerate)
188
- if (Number(Boolean(request.retryTurnId)) + Number(Boolean(request.translateTurnId)) + Number(request.regenerate) > 1)
202
+ if (
203
+ Number(Boolean(request.retryTurnId)) +
204
+ Number(Boolean(request.translateTurnId)) +
205
+ Number(request.regenerate) >
206
+ 1
207
+ )
189
208
  throw badRequest('action', 'multiple')
190
209
  if (sourced && request.replyToId) throw badRequest('replyToId', 'action_conflict')
191
210
  if (sourced && (request.images?.length ?? 0) > 0) throw badRequest('images', 'action_conflict')
192
- if (Boolean(request.translateTurnId) !== (request.targetLanguage !== null && request.targetLanguage !== undefined))
211
+ if (
212
+ Boolean(request.translateTurnId) !==
213
+ (request.targetLanguage !== null && request.targetLanguage !== undefined)
214
+ )
193
215
  throw badRequest('targetLanguage', 'translation_required')
194
- if (!request.conversationId && (sourced || request.replyToId)) throw badRequest('conversationId', 'required')
216
+ if (!request.conversationId && (sourced || request.replyToId))
217
+ throw badRequest('conversationId', 'required')
195
218
  if ((request.retryTurnId || request.translateTurnId) && request.webSearch)
196
219
  throw badRequest('webSearch', 'action_conflict')
197
220
  let guard: SelectionGuard = { kind: 'explicit' },