@d4y/agent-runtime-nuxt 0.1.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.
- package/README.md +314 -0
- package/dist/module.d.mts +69 -0
- package/dist/module.json +12 -0
- package/dist/module.mjs +60 -0
- package/dist/runtime/components/AgentRuntimeArtifactPreview.d.vue.ts +14 -0
- package/dist/runtime/components/AgentRuntimeArtifactPreview.vue +112 -0
- package/dist/runtime/components/AgentRuntimeArtifactPreview.vue.d.ts +14 -0
- package/dist/runtime/composables/useAgentRuntime.d.ts +130 -0
- package/dist/runtime/composables/useAgentRuntime.js +306 -0
- package/dist/runtime/composables/useAgentRuntimeMarkdown.d.ts +15 -0
- package/dist/runtime/composables/useAgentRuntimeMarkdown.js +109 -0
- package/dist/runtime/server/api/app.get.d.ts +6 -0
- package/dist/runtime/server/api/app.get.js +26 -0
- package/dist/runtime/server/api/conversations/[id]/abort.post.d.ts +6 -0
- package/dist/runtime/server/api/conversations/[id]/abort.post.js +18 -0
- package/dist/runtime/server/api/conversations/[id]/env.post.d.ts +10 -0
- package/dist/runtime/server/api/conversations/[id]/env.post.js +22 -0
- package/dist/runtime/server/api/conversations/[id]/files/raw/[...path].get.d.ts +11 -0
- package/dist/runtime/server/api/conversations/[id]/files/raw/[...path].get.js +31 -0
- package/dist/runtime/server/api/conversations/[id]/files.get.d.ts +14 -0
- package/dist/runtime/server/api/conversations/[id]/files.get.js +16 -0
- package/dist/runtime/server/api/conversations/[id]/history.get.d.ts +2 -0
- package/dist/runtime/server/api/conversations/[id]/history.get.js +16 -0
- package/dist/runtime/server/api/conversations/[id]/messages.post.d.ts +7 -0
- package/dist/runtime/server/api/conversations/[id]/messages.post.js +20 -0
- package/dist/runtime/server/api/conversations/[id]/stream.get.d.ts +9 -0
- package/dist/runtime/server/api/conversations/[id]/stream.get.js +28 -0
- package/dist/runtime/server/api/conversations/[id].delete.d.ts +2 -0
- package/dist/runtime/server/api/conversations/[id].delete.js +18 -0
- package/dist/runtime/server/api/conversations.post.d.ts +7 -0
- package/dist/runtime/server/api/conversations.post.js +17 -0
- package/dist/runtime/server/utils/agent-runtime.d.ts +12 -0
- package/dist/runtime/server/utils/agent-runtime.js +12 -0
- package/dist/runtime/utils/files.d.ts +16 -0
- package/dist/runtime/utils/files.js +42 -0
- package/dist/types.d.mts +9 -0
- package/package.json +67 -0
- package/src/frontend.ts +16 -0
- package/src/module.ts +155 -0
- package/src/nitro-globals.d.ts +8 -0
- package/src/runtime/components/AgentRuntimeArtifactPreview.vue +192 -0
- package/src/runtime/composables/useAgentRuntime.ts +527 -0
- package/src/runtime/composables/useAgentRuntimeMarkdown.ts +145 -0
- package/src/runtime/server/api/app.get.ts +50 -0
- package/src/runtime/server/api/conversations/[id]/abort.post.ts +26 -0
- package/src/runtime/server/api/conversations/[id]/env.post.ts +34 -0
- package/src/runtime/server/api/conversations/[id]/files/raw/[...path].get.ts +48 -0
- package/src/runtime/server/api/conversations/[id]/files.get.ts +33 -0
- package/src/runtime/server/api/conversations/[id]/history.get.ts +20 -0
- package/src/runtime/server/api/conversations/[id]/messages.post.ts +29 -0
- package/src/runtime/server/api/conversations/[id]/stream.get.ts +41 -0
- package/src/runtime/server/api/conversations/[id].delete.ts +22 -0
- package/src/runtime/server/api/conversations.post.ts +26 -0
- package/src/runtime/server/utils/agent-runtime.ts +33 -0
- package/src/runtime/utils/files.ts +78 -0
- package/src/shared.ts +46 -0
- package/src/vue-shim.d.ts +6 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { agentRuntime, agentRuntimeHeaders } from '../utils/agent-runtime'
|
|
2
|
+
|
|
3
|
+
interface AppEnvField {
|
|
4
|
+
required?: boolean
|
|
5
|
+
secret?: boolean
|
|
6
|
+
description?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface AppSummary {
|
|
10
|
+
appId: string
|
|
11
|
+
name: string
|
|
12
|
+
envSchema: Record<string, AppEnvField>
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface AppListResponse {
|
|
16
|
+
apps: AppSummary[]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* GET `${apiPrefix}/app` — returns the manifest of the configured app
|
|
21
|
+
* (so the client can render its env-schema form generically).
|
|
22
|
+
*/
|
|
23
|
+
export default defineEventHandler(async () => {
|
|
24
|
+
const cfg = agentRuntime()
|
|
25
|
+
const response = await fetch(`${cfg.baseUrl}/v1/apps`, {
|
|
26
|
+
headers: agentRuntimeHeaders(cfg)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
throw createError({
|
|
31
|
+
statusCode: response.status,
|
|
32
|
+
statusMessage: await response.text(),
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const res = await response.json() as AppListResponse
|
|
37
|
+
|
|
38
|
+
const app = res.apps.find(a => a.appId === cfg.appId)
|
|
39
|
+
if (!app) {
|
|
40
|
+
throw createError({
|
|
41
|
+
statusCode: 502,
|
|
42
|
+
statusMessage: `agent-runtime has no app with id "${cfg.appId}"`
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
appId: app.appId,
|
|
47
|
+
name: app.name,
|
|
48
|
+
envSchema: app.envSchema ?? {}
|
|
49
|
+
}
|
|
50
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { agentRuntime, agentRuntimeHeaders } from '../../../utils/agent-runtime'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* POST `${apiPrefix}/conversations/:id/abort` — cancels the in-flight
|
|
5
|
+
* agent run for this conversation. Returns 204 on success.
|
|
6
|
+
*/
|
|
7
|
+
export default defineEventHandler(async (event) => {
|
|
8
|
+
const cfg = agentRuntime()
|
|
9
|
+
const id = getRouterParam(event, 'id')
|
|
10
|
+
if (!id) throw createError({ statusCode: 400, statusMessage: 'missing conversation id' })
|
|
11
|
+
|
|
12
|
+
const response = await fetch(`${cfg.baseUrl}/v1/conversations/${id}/abort`, {
|
|
13
|
+
method: 'POST',
|
|
14
|
+
headers: agentRuntimeHeaders(cfg)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
if (!response.ok) {
|
|
18
|
+
throw createError({
|
|
19
|
+
statusCode: response.status,
|
|
20
|
+
statusMessage: await response.text(),
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
setResponseStatus(event, 204)
|
|
25
|
+
return null
|
|
26
|
+
})
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { agentRuntime, agentRuntimeHeaders } from '../../../utils/agent-runtime'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* POST `${apiPrefix}/conversations/:id/env` — patch the workspace env of an
|
|
5
|
+
* existing conversation. `merge: true` (the default) merges with the current
|
|
6
|
+
* env; `merge: false` replaces it entirely.
|
|
7
|
+
*
|
|
8
|
+
* Triggers any `bootstrap[]` step on the harness side whose `rerunOn` keys
|
|
9
|
+
* intersect the changed set, so this is also how you re-import auth.
|
|
10
|
+
*/
|
|
11
|
+
export default defineEventHandler(async (event) => {
|
|
12
|
+
const cfg = agentRuntime()
|
|
13
|
+
const id = getRouterParam(event, 'id')
|
|
14
|
+
if (!id) throw createError({ statusCode: 400, statusMessage: 'missing conversation id' })
|
|
15
|
+
const body = (await readBody<{ env?: Record<string, string>, merge?: boolean }>(event).catch(() => ({}))) as { env?: Record<string, string>, merge?: boolean }
|
|
16
|
+
if (!body.env || typeof body.env !== 'object') {
|
|
17
|
+
throw createError({ statusCode: 400, statusMessage: 'missing env' })
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const response = await fetch(`${cfg.baseUrl}/v1/conversations/${id}/env`, {
|
|
21
|
+
method: 'POST',
|
|
22
|
+
headers: agentRuntimeHeaders(cfg),
|
|
23
|
+
body: JSON.stringify({ env: body.env, merge: body.merge ?? true })
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
throw createError({
|
|
28
|
+
statusCode: response.status,
|
|
29
|
+
statusMessage: await response.text(),
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return await response.json()
|
|
34
|
+
})
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Readable } from 'node:stream'
|
|
2
|
+
import { agentRuntime } from '../../../../../utils/agent-runtime'
|
|
3
|
+
|
|
4
|
+
const PASS_THROUGH_HEADERS = ['content-type', 'content-length', 'content-disposition', 'cache-control']
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* GET `${apiPrefix}/conversations/:id/files/raw/<path>` — streams a single
|
|
8
|
+
* workspace file straight back to the browser.
|
|
9
|
+
*
|
|
10
|
+
* We intentionally read the path tail off the *raw* request URL rather than
|
|
11
|
+
* `getRouterParam` so the bytes (already URL-encoded by the browser) are
|
|
12
|
+
* forwarded verbatim — re-encoding after Nitro's decoding step is fragile
|
|
13
|
+
* for filenames containing spaces or unicode.
|
|
14
|
+
*/
|
|
15
|
+
export default defineEventHandler(async (event) => {
|
|
16
|
+
const cfg = agentRuntime()
|
|
17
|
+
const id = getRouterParam(event, 'id')
|
|
18
|
+
if (!id) throw createError({ statusCode: 400, statusMessage: 'missing conversation id' })
|
|
19
|
+
|
|
20
|
+
const apiPrefix = (useRuntimeConfig().public.agentRuntime?.apiPrefix ?? '/api/agent-runtime').replace(/\/+$/, '')
|
|
21
|
+
const marker = `${apiPrefix}/conversations/${id}/files/raw/`
|
|
22
|
+
const idx = event.path.indexOf(marker)
|
|
23
|
+
const tail = idx >= 0 ? event.path.slice(idx + marker.length) : ''
|
|
24
|
+
if (!tail) throw createError({ statusCode: 400, statusMessage: 'missing file path' })
|
|
25
|
+
|
|
26
|
+
const upstreamUrl = `${cfg.baseUrl}/v1/conversations/${id}/files/raw/${tail}`
|
|
27
|
+
const controller = new AbortController()
|
|
28
|
+
event.node.req.on('close', () => controller.abort())
|
|
29
|
+
|
|
30
|
+
const upstream = await fetch(upstreamUrl, {
|
|
31
|
+
headers: { 'X-Agent-Runtime-App-Key': cfg.appKey },
|
|
32
|
+
signal: controller.signal
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
if (!upstream.ok || !upstream.body) {
|
|
36
|
+
throw createError({
|
|
37
|
+
statusCode: upstream.status || 502,
|
|
38
|
+
statusMessage: `agent-runtime file fetch failed: ${upstream.status} ${upstream.statusText}`
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
for (const name of PASS_THROUGH_HEADERS) {
|
|
43
|
+
const v = upstream.headers.get(name)
|
|
44
|
+
if (v) setHeader(event, name, v)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return sendStream(event, Readable.fromWeb(upstream.body as never))
|
|
48
|
+
})
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { agentRuntime } from '../../../utils/agent-runtime'
|
|
2
|
+
|
|
3
|
+
/** Single file entry as returned by agent-runtime. */
|
|
4
|
+
export interface FileEntry {
|
|
5
|
+
name: string
|
|
6
|
+
relPath: string
|
|
7
|
+
size: number
|
|
8
|
+
mtime: string
|
|
9
|
+
mimeType: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* GET `${apiPrefix}/conversations/:id/files` — lists the workspace files
|
|
14
|
+
* the agent has produced (or read in) so the UI can show a side panel.
|
|
15
|
+
*/
|
|
16
|
+
export default defineEventHandler(async (event): Promise<{ items: FileEntry[] }> => {
|
|
17
|
+
const cfg = agentRuntime()
|
|
18
|
+
const id = getRouterParam(event, 'id')
|
|
19
|
+
if (!id) throw createError({ statusCode: 400, statusMessage: 'missing conversation id' })
|
|
20
|
+
|
|
21
|
+
const response = await fetch(`${cfg.baseUrl}/v1/conversations/${id}/files`, {
|
|
22
|
+
headers: { 'X-Agent-Runtime-App-Key': cfg.appKey }
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
throw createError({
|
|
27
|
+
statusCode: response.status,
|
|
28
|
+
statusMessage: await response.text(),
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return await response.json() as { items: FileEntry[] }
|
|
33
|
+
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { agentRuntime } from '../../../utils/agent-runtime'
|
|
2
|
+
|
|
3
|
+
export default defineEventHandler(async (event) => {
|
|
4
|
+
const cfg = agentRuntime()
|
|
5
|
+
const id = getRouterParam(event, 'id')
|
|
6
|
+
if (!id) throw createError({ statusCode: 400, statusMessage: 'missing conversation id' })
|
|
7
|
+
|
|
8
|
+
const response = await fetch(`${cfg.baseUrl}/v1/conversations/${id}/history`, {
|
|
9
|
+
headers: { 'X-Agent-Runtime-App-Key': cfg.appKey },
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
if (!response.ok) {
|
|
13
|
+
throw createError({
|
|
14
|
+
statusCode: response.status,
|
|
15
|
+
statusMessage: await response.text(),
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return await response.json()
|
|
20
|
+
})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { agentRuntime, agentRuntimeHeaders } from '../../../utils/agent-runtime'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* POST `${apiPrefix}/conversations/:id/messages` — append a user turn.
|
|
5
|
+
* The optional `context` field is forwarded verbatim to agent-runtime, where
|
|
6
|
+
* the agent picks it up as per-turn dynamic context.
|
|
7
|
+
*/
|
|
8
|
+
export default defineEventHandler(async (event) => {
|
|
9
|
+
const cfg = agentRuntime()
|
|
10
|
+
const id = getRouterParam(event, 'id')
|
|
11
|
+
if (!id) throw createError({ statusCode: 400, statusMessage: 'missing conversation id' })
|
|
12
|
+
const body = await readBody<{ content: string, context?: unknown, language?: string }>(event)
|
|
13
|
+
if (!body?.content) throw createError({ statusCode: 400, statusMessage: 'missing content' })
|
|
14
|
+
|
|
15
|
+
const response = await fetch(`${cfg.baseUrl}/v1/conversations/${id}/messages`, {
|
|
16
|
+
method: 'POST',
|
|
17
|
+
headers: agentRuntimeHeaders(cfg),
|
|
18
|
+
body: JSON.stringify({ content: body.content, context: body.context, language: body.language })
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
throw createError({
|
|
23
|
+
statusCode: response.status,
|
|
24
|
+
statusMessage: await response.text(),
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return await response.json()
|
|
29
|
+
})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Readable } from 'node:stream'
|
|
2
|
+
import { agentRuntime } from '../../../utils/agent-runtime'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* GET `${apiPrefix}/conversations/:id/stream` — pipes the upstream
|
|
6
|
+
* Server-Sent Events stream from agent-runtime straight back to the browser.
|
|
7
|
+
*
|
|
8
|
+
* Closing the client connection aborts the upstream fetch so we don't leave
|
|
9
|
+
* orphaned conversations hanging.
|
|
10
|
+
*/
|
|
11
|
+
export default defineEventHandler(async (event) => {
|
|
12
|
+
const cfg = agentRuntime()
|
|
13
|
+
const id = getRouterParam(event, 'id')
|
|
14
|
+
if (!id) throw createError({ statusCode: 400, statusMessage: 'missing conversation id' })
|
|
15
|
+
|
|
16
|
+
const controller = new AbortController()
|
|
17
|
+
event.node.req.on('close', () => controller.abort())
|
|
18
|
+
|
|
19
|
+
const upstream = await fetch(`${cfg.baseUrl}/v1/conversations/${id}/stream`, {
|
|
20
|
+
method: 'GET',
|
|
21
|
+
headers: {
|
|
22
|
+
'X-Agent-Runtime-App-Key': cfg.appKey,
|
|
23
|
+
accept: 'text/event-stream'
|
|
24
|
+
},
|
|
25
|
+
signal: controller.signal
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
if (!upstream.ok || !upstream.body) {
|
|
29
|
+
throw createError({
|
|
30
|
+
statusCode: upstream.status || 502,
|
|
31
|
+
statusMessage: `agent-runtime stream failed: ${upstream.status} ${upstream.statusText}`
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
setHeader(event, 'content-type', 'text/event-stream')
|
|
36
|
+
setHeader(event, 'cache-control', 'no-cache, no-transform')
|
|
37
|
+
setHeader(event, 'connection', 'keep-alive')
|
|
38
|
+
setHeader(event, 'x-accel-buffering', 'no')
|
|
39
|
+
|
|
40
|
+
return sendStream(event, Readable.fromWeb(upstream.body as never))
|
|
41
|
+
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { agentRuntime } from '../../utils/agent-runtime'
|
|
2
|
+
|
|
3
|
+
export default defineEventHandler(async (event) => {
|
|
4
|
+
const cfg = agentRuntime()
|
|
5
|
+
const id = getRouterParam(event, 'id')
|
|
6
|
+
if (!id) throw createError({ statusCode: 400, statusMessage: 'missing conversation id' })
|
|
7
|
+
|
|
8
|
+
const response = await fetch(`${cfg.baseUrl}/v1/conversations/${id}`, {
|
|
9
|
+
method: 'DELETE',
|
|
10
|
+
headers: { 'X-Agent-Runtime-App-Key': cfg.appKey },
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
if (!response.ok) {
|
|
14
|
+
throw createError({
|
|
15
|
+
statusCode: response.status,
|
|
16
|
+
statusMessage: await response.text(),
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
setResponseStatus(event, 204)
|
|
21
|
+
return null
|
|
22
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { agentRuntime, agentRuntimeHeaders } from '../utils/agent-runtime'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* POST `${apiPrefix}/conversations` — opens a new conversation against the
|
|
5
|
+
* configured app, optionally seeded with an `env` map (which the harness
|
|
6
|
+
* uses to populate the workspace environment) and a model override.
|
|
7
|
+
*/
|
|
8
|
+
export default defineEventHandler(async (event) => {
|
|
9
|
+
const cfg = agentRuntime()
|
|
10
|
+
const body = (await readBody<{ env?: Record<string, string>, model?: unknown, language?: string }>(event).catch(() => ({}))) as { env?: Record<string, string>, model?: unknown, language?: string }
|
|
11
|
+
|
|
12
|
+
const response = await fetch(`${cfg.baseUrl}/v1/conversations`, {
|
|
13
|
+
method: 'POST',
|
|
14
|
+
headers: agentRuntimeHeaders(cfg),
|
|
15
|
+
body: JSON.stringify({ appId: cfg.appId, env: body.env ?? {}, model: body.model, language: body.language })
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
if (!response.ok) {
|
|
19
|
+
throw createError({
|
|
20
|
+
statusCode: response.status,
|
|
21
|
+
statusMessage: await response.text(),
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return await response.json()
|
|
26
|
+
})
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createAgentRuntimeRequestHeaders, resolveAgentRuntimeConfig, type AgentRuntimeResolvedConfig } from '../../../shared'
|
|
2
|
+
|
|
3
|
+
// h3 / Nitro helpers (`useRuntimeConfig`, `createError`, `defineEventHandler`,
|
|
4
|
+
// `getRouterParam`, `setHeader`, `sendStream`, `readBody`, `setResponseStatus`,
|
|
5
|
+
// `$fetch`) are auto-injected into module-provided server handlers by Nitro
|
|
6
|
+
// at build time, so we don't import them here.
|
|
7
|
+
|
|
8
|
+
/** Resolved server-side configuration for talking to agent-runtime. */
|
|
9
|
+
export type AgentRuntimeRuntime = AgentRuntimeResolvedConfig
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Read the agent-runtime section of `runtimeConfig`, validating that the app key
|
|
13
|
+
* has been provided. Throws a typed Nitro 500 otherwise so the proxy routes
|
|
14
|
+
* surface a clear error instead of silently forwarding an unauthenticated
|
|
15
|
+
* request.
|
|
16
|
+
*/
|
|
17
|
+
export const agentRuntime = (): AgentRuntimeRuntime => {
|
|
18
|
+
try {
|
|
19
|
+
return resolveAgentRuntimeConfig(useRuntimeConfig().agentRuntime)
|
|
20
|
+
} catch (error) {
|
|
21
|
+
throw createError({
|
|
22
|
+
statusCode: 500,
|
|
23
|
+
statusMessage:
|
|
24
|
+
error instanceof Error
|
|
25
|
+
? `${error.message}. Set it via env, runtimeConfig.agentRuntime.appKey, or the module options.`
|
|
26
|
+
: '[agent-runtime] AGENT_RUNTIME_APP_KEY is not configured. Set it via env, runtimeConfig.agentRuntime.appKey, or the module options.',
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Build the standard JSON request headers, including the app key. */
|
|
32
|
+
export const agentRuntimeHeaders = (cfg: AgentRuntimeRuntime, extra: Record<string, string> = {}): Record<string, string> =>
|
|
33
|
+
createAgentRuntimeRequestHeaders(cfg, extra)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export interface AgentRuntimeFileLike {
|
|
2
|
+
name?: string | null
|
|
3
|
+
relPath?: string | null
|
|
4
|
+
mimeType?: string | null
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type AgentRuntimeFilePreviewKind = 'image' | 'pdf' | 'text' | 'download'
|
|
8
|
+
|
|
9
|
+
const IMAGE_EXT_RE = /\.(?:png|jpe?g|gif|webp|svg|avif|bmp)(?:[?#].*)?$/i
|
|
10
|
+
const PDF_EXT_RE = /\.pdf(?:[?#].*)?$/i
|
|
11
|
+
const TEXT_EXT_RE = /\.(?:txt|md|json|csv|tsv|xml|ya?ml|log|html?|css|js|mjs|cjs|ts|tsx|jsx|vue|py|rb|sh|sql)(?:[?#].*)?$/i
|
|
12
|
+
|
|
13
|
+
export const toAgentRuntimeWorkspaceRelativePath = (uri: string): string | null => {
|
|
14
|
+
if (!uri) return null
|
|
15
|
+
if (uri.startsWith('sandbox:')) return uri.slice('sandbox:'.length).replace(/^\/+/, '')
|
|
16
|
+
if (uri.startsWith('/workspace/')) return uri.slice('/workspace/'.length)
|
|
17
|
+
if (uri === '/workspace') return ''
|
|
18
|
+
if (uri.startsWith('workspace/')) return uri.slice('workspace/'.length)
|
|
19
|
+
return null
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const resolveAgentRuntimeWorkspaceUri = (
|
|
23
|
+
uri: string,
|
|
24
|
+
resolveWorkspacePath?: (relPath: string) => string | null | undefined,
|
|
25
|
+
): string | null => {
|
|
26
|
+
const relPath = toAgentRuntimeWorkspaceRelativePath(uri)
|
|
27
|
+
if (relPath === null) return null
|
|
28
|
+
const resolved = resolveWorkspacePath?.(relPath)
|
|
29
|
+
return resolved || null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const isAgentRuntimeImageMimeType = (mimeType: string | null | undefined): boolean =>
|
|
33
|
+
typeof mimeType === 'string' && /^image\//i.test(mimeType)
|
|
34
|
+
|
|
35
|
+
export const isAgentRuntimePdfMimeType = (mimeType: string | null | undefined): boolean =>
|
|
36
|
+
typeof mimeType === 'string' && mimeType.toLowerCase() === 'application/pdf'
|
|
37
|
+
|
|
38
|
+
export const isAgentRuntimeTextMimeType = (mimeType: string | null | undefined): boolean => {
|
|
39
|
+
if (typeof mimeType !== 'string' || !mimeType) {
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const normalized = mimeType.toLowerCase()
|
|
44
|
+
return normalized.startsWith('text/')
|
|
45
|
+
|| normalized === 'application/json'
|
|
46
|
+
|| normalized === 'application/ld+json'
|
|
47
|
+
|| normalized === 'application/xml'
|
|
48
|
+
|| normalized === 'application/yaml'
|
|
49
|
+
|| normalized === 'application/x-yaml'
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const isAgentRuntimeImagePath = (value: string | null | undefined): boolean =>
|
|
53
|
+
typeof value === 'string' && IMAGE_EXT_RE.test(value)
|
|
54
|
+
|
|
55
|
+
export const isAgentRuntimePdfPath = (value: string | null | undefined): boolean =>
|
|
56
|
+
typeof value === 'string' && PDF_EXT_RE.test(value)
|
|
57
|
+
|
|
58
|
+
export const isAgentRuntimeTextPath = (value: string | null | undefined): boolean =>
|
|
59
|
+
typeof value === 'string' && TEXT_EXT_RE.test(value)
|
|
60
|
+
|
|
61
|
+
export const getAgentRuntimeFilePreviewKind = (file: AgentRuntimeFileLike): AgentRuntimeFilePreviewKind => {
|
|
62
|
+
if (isAgentRuntimeImageMimeType(file.mimeType) || isAgentRuntimeImagePath(file.name) || isAgentRuntimeImagePath(file.relPath)) {
|
|
63
|
+
return 'image'
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (isAgentRuntimePdfMimeType(file.mimeType) || isAgentRuntimePdfPath(file.name) || isAgentRuntimePdfPath(file.relPath)) {
|
|
67
|
+
return 'pdf'
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (isAgentRuntimeTextMimeType(file.mimeType) || isAgentRuntimeTextPath(file.name) || isAgentRuntimeTextPath(file.relPath)) {
|
|
71
|
+
return 'text'
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return 'download'
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const canPreviewAgentRuntimeFileInline = (file: AgentRuntimeFileLike): boolean =>
|
|
78
|
+
getAgentRuntimeFilePreviewKind(file) !== 'download'
|
package/src/shared.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
|
|
3
|
+
export interface AgentRuntimeConfigInput {
|
|
4
|
+
baseUrl?: string | null
|
|
5
|
+
appKey?: string | null
|
|
6
|
+
appId?: string | null
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface AgentRuntimeResolvedConfig {
|
|
10
|
+
baseUrl: string
|
|
11
|
+
appKey: string
|
|
12
|
+
appId: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const normalizeAgentRuntimeBaseUrl = (value: string | null | undefined) =>
|
|
16
|
+
String(value || '').trim().replace(/\/+$/, '')
|
|
17
|
+
|
|
18
|
+
export const createScopeFingerprint = (parts: Array<string | null | undefined>) => {
|
|
19
|
+
const normalized = parts.map((part) => (typeof part === 'string' && part.trim().length > 0 ? part.trim() : 'none')).join('|')
|
|
20
|
+
return createHash('sha256').update(normalized).digest('hex').slice(0, 12)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const resolveAgentRuntimeConfig = (
|
|
24
|
+
config: AgentRuntimeConfigInput | null | undefined,
|
|
25
|
+
options?: { defaultAppId?: string },
|
|
26
|
+
): AgentRuntimeResolvedConfig => {
|
|
27
|
+
const appKey = String(config?.appKey || '').trim()
|
|
28
|
+
if (!appKey) {
|
|
29
|
+
throw new Error('[agent-runtime] AGENT_RUNTIME_APP_KEY is not configured')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
baseUrl: normalizeAgentRuntimeBaseUrl(config?.baseUrl),
|
|
34
|
+
appKey,
|
|
35
|
+
appId: String(config?.appId || options?.defaultAppId || 'omnisearch'),
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const createAgentRuntimeRequestHeaders = (
|
|
40
|
+
config: Pick<AgentRuntimeResolvedConfig, 'appKey'>,
|
|
41
|
+
extra: Record<string, string> = {},
|
|
42
|
+
): Record<string, string> => ({
|
|
43
|
+
'X-Agent-Runtime-App-Key': config.appKey,
|
|
44
|
+
'content-type': 'application/json',
|
|
45
|
+
...extra,
|
|
46
|
+
})
|