feat(AI): nomad.md for custom instructions
This commit is contained in:
parent
6a4f02dd46
commit
6cd10df6c1
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { NomadMdService } from '#services/nomad_md_service'
|
||||||
|
import { updateNomadMdSchema } from '#validators/nomad_md'
|
||||||
|
import { inject } from '@adonisjs/core'
|
||||||
|
import type { HttpContext } from '@adonisjs/core/http'
|
||||||
|
|
||||||
|
@inject()
|
||||||
|
export default class NomadMdController {
|
||||||
|
constructor(private nomadMdService: NomadMdService) {}
|
||||||
|
|
||||||
|
async show({ response }: HttpContext) {
|
||||||
|
const content = await this.nomadMdService.read()
|
||||||
|
return response.status(200).json({ content })
|
||||||
|
}
|
||||||
|
|
||||||
|
async update({ request, response }: HttpContext) {
|
||||||
|
const { content } = await request.validateUsing(updateNomadMdSchema)
|
||||||
|
// Empty request strings arrive as undefined (AdonisJS null-coerces them);
|
||||||
|
// treat that as an explicit "clear the file".
|
||||||
|
await this.nomadMdService.write(content ?? '')
|
||||||
|
return response.status(200).json({ success: true, message: 'NOMAD.md saved successfully' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { ChatService } from '#services/chat_service'
|
import { ChatService } from '#services/chat_service'
|
||||||
import { DockerService } from '#services/docker_service'
|
import { DockerService } from '#services/docker_service'
|
||||||
|
import { NomadMdService } from '#services/nomad_md_service'
|
||||||
import { OllamaService } from '#services/ollama_service'
|
import { OllamaService } from '#services/ollama_service'
|
||||||
import { RagService } from '#services/rag_service'
|
import { RagService } from '#services/rag_service'
|
||||||
import Service from '#models/service'
|
import Service from '#models/service'
|
||||||
|
|
@ -20,7 +21,8 @@ export default class OllamaController {
|
||||||
private chatService: ChatService,
|
private chatService: ChatService,
|
||||||
private dockerService: DockerService,
|
private dockerService: DockerService,
|
||||||
private ollamaService: OllamaService,
|
private ollamaService: OllamaService,
|
||||||
private ragService: RagService
|
private ragService: RagService,
|
||||||
|
private nomadMdService: NomadMdService
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
async availableModels({ request }: HttpContext) {
|
async availableModels({ request }: HttpContext) {
|
||||||
|
|
@ -71,6 +73,16 @@ export default class OllamaController {
|
||||||
reqData.messages.unshift(systemPrompt)
|
reqData.messages.unshift(systemPrompt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inject the user-managed NOMAD.md as its own leading system message so the
|
||||||
|
// user's persistent instructions take precedence, while the default
|
||||||
|
// formatting prompt and any RAG context below remain intact. A missing or
|
||||||
|
// blank file yields null and changes nothing.
|
||||||
|
const nomadPrompt = await this.nomadMdService.getSystemPrompt()
|
||||||
|
if (nomadPrompt) {
|
||||||
|
logger.debug('[OllamaController] Injecting NOMAD.md system prompt')
|
||||||
|
reqData.messages.unshift({ role: 'system' as const, content: nomadPrompt })
|
||||||
|
}
|
||||||
|
|
||||||
// Query rewriting for better RAG retrieval with manageable context
|
// Query rewriting for better RAG retrieval with manageable context
|
||||||
// Will return user's latest message if no rewriting is needed
|
// Will return user's latest message if no rewriting is needed
|
||||||
const rewrittenQuery = await this.rewriteQueryWithContext(reqData.messages, reqData.model)
|
const rewrittenQuery = await this.rewriteQueryWithContext(reqData.messages, reqData.model)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { rename, writeFile } from 'node:fs/promises'
|
||||||
|
import { dirname } from 'node:path'
|
||||||
|
import { randomUUID } from 'node:crypto'
|
||||||
|
import app from '@adonisjs/core/services/app'
|
||||||
|
import { ensureDirectoryExists, getFile } from '../utils/fs.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages the user-editable `NOMAD.md` file that is injected as a system prompt
|
||||||
|
* during chat completions. The file lives in the admin storage directory
|
||||||
|
* (`/app/storage/NOMAD.md` in the container, `/opt/project-nomad/storage/NOMAD.md`
|
||||||
|
* on the host), so an end user can also edit it directly on disk. The file is
|
||||||
|
* created lazily on the first save — a missing or empty file simply means no
|
||||||
|
* custom prompt is injected.
|
||||||
|
*/
|
||||||
|
export class NomadMdService {
|
||||||
|
static STORAGE_PATH = 'storage/NOMAD.md'
|
||||||
|
|
||||||
|
private get filePath(): string {
|
||||||
|
return app.makePath(NomadMdService.STORAGE_PATH)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the raw file contents. Returns an empty string when the file does not
|
||||||
|
* exist yet (the editor renders a template client-side in that case).
|
||||||
|
*/
|
||||||
|
async read(): Promise<string> {
|
||||||
|
const content = await getFile(this.filePath, 'string')
|
||||||
|
return content ?? ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persist the file. Written atomically (tmp file + rename) so a concurrent
|
||||||
|
* on-disk edit never observes a half-written file.
|
||||||
|
*/
|
||||||
|
async write(content: string): Promise<void> {
|
||||||
|
const filePath = this.filePath
|
||||||
|
await ensureDirectoryExists(dirname(filePath))
|
||||||
|
const tmpPath = `${filePath}.tmp.${randomUUID()}`
|
||||||
|
await writeFile(tmpPath, content, 'utf-8')
|
||||||
|
await rename(tmpPath, filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The trimmed contents suitable for injection as a system message, or `null`
|
||||||
|
* when the file is missing or blank (so chat behaviour is unchanged).
|
||||||
|
*/
|
||||||
|
async getSystemPrompt(): Promise<string | null> {
|
||||||
|
const content = (await this.read()).trim()
|
||||||
|
return content.length > 0 ? content : null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import vine from '@vinejs/vine'
|
||||||
|
|
||||||
|
// Allow an empty/absent value so the user can clear their NOMAD.md — AdonisJS
|
||||||
|
// converts empty request strings to null, so `optional()` (coerced to '' in the
|
||||||
|
// controller) is what lets a "clear" through. The cap keeps a single system
|
||||||
|
// prompt from growing unbounded.
|
||||||
|
export const updateNomadMdSchema = vine.compile(
|
||||||
|
vine.object({
|
||||||
|
content: vine.string().maxLength(100_000).optional(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
import { useEffect, useRef } from 'react'
|
||||||
|
import { Compartment, EditorState } from '@codemirror/state'
|
||||||
|
import { EditorView } from '@codemirror/view'
|
||||||
|
import { markdown } from '@codemirror/lang-markdown'
|
||||||
|
import { oneDark } from '@codemirror/theme-one-dark'
|
||||||
|
import { basicSetup } from 'codemirror'
|
||||||
|
import { useTheme } from '~/hooks/useTheme'
|
||||||
|
|
||||||
|
interface MarkdownEditorProps {
|
||||||
|
/** Initial document contents. Read once on mount; later edits are reported via onChange. */
|
||||||
|
initialValue: string
|
||||||
|
onChange: (value: string) => void
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A thin React wrapper that mounts a CodeMirror 6 editor tuned for Markdown.
|
||||||
|
* The editor is uncontrolled after mount (its state lives in CodeMirror); the
|
||||||
|
* current value is surfaced to the parent through onChange. The one-dark theme
|
||||||
|
* is applied via a Compartment so light/dark toggles reconfigure in place
|
||||||
|
* without rebuilding the document.
|
||||||
|
*/
|
||||||
|
export default function MarkdownEditor({ initialValue, onChange, className }: MarkdownEditorProps) {
|
||||||
|
const hostRef = useRef<HTMLDivElement | null>(null)
|
||||||
|
const viewRef = useRef<EditorView | null>(null)
|
||||||
|
const themeCompartment = useRef(new Compartment())
|
||||||
|
const onChangeRef = useRef(onChange)
|
||||||
|
onChangeRef.current = onChange
|
||||||
|
const { theme } = useTheme()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!hostRef.current) return
|
||||||
|
|
||||||
|
const state = EditorState.create({
|
||||||
|
doc: initialValue,
|
||||||
|
extensions: [
|
||||||
|
basicSetup,
|
||||||
|
markdown(),
|
||||||
|
EditorView.lineWrapping,
|
||||||
|
// Fill the host element and scroll internally rather than growing the page.
|
||||||
|
EditorView.theme({
|
||||||
|
'&': { height: '100%' },
|
||||||
|
'.cm-scroller': { overflow: 'auto' },
|
||||||
|
}),
|
||||||
|
themeCompartment.current.of(theme === 'dark' ? oneDark : []),
|
||||||
|
EditorView.updateListener.of((update) => {
|
||||||
|
if (update.docChanged) {
|
||||||
|
onChangeRef.current(update.state.doc.toString())
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const view = new EditorView({ state, parent: hostRef.current })
|
||||||
|
viewRef.current = view
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
view.destroy()
|
||||||
|
viewRef.current = null
|
||||||
|
}
|
||||||
|
// Mount once; initialValue/theme changes are handled below or by remounting.
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
viewRef.current?.dispatch({
|
||||||
|
effects: themeCompartment.current.reconfigure(theme === 'dark' ? oneDark : []),
|
||||||
|
})
|
||||||
|
}, [theme])
|
||||||
|
|
||||||
|
return <div ref={hostRef} className={className} />
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ import { ChatSession } from '../../../types/chat'
|
||||||
import { IconMessage } from '@tabler/icons-react'
|
import { IconMessage } from '@tabler/icons-react'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import KnowledgeBaseModal from './KnowledgeBaseModal'
|
import KnowledgeBaseModal from './KnowledgeBaseModal'
|
||||||
|
import NomadMdModal from './NomadMdModal'
|
||||||
|
|
||||||
interface ChatSidebarProps {
|
interface ChatSidebarProps {
|
||||||
sessions: ChatSession[]
|
sessions: ChatSession[]
|
||||||
|
|
@ -27,6 +28,7 @@ export default function ChatSidebar({
|
||||||
const [isKnowledgeBaseModalOpen, setIsKnowledgeBaseModalOpen] = useState(
|
const [isKnowledgeBaseModalOpen, setIsKnowledgeBaseModalOpen] = useState(
|
||||||
() => new URLSearchParams(window.location.search).get('knowledge_base') === 'true'
|
() => new URLSearchParams(window.location.search).get('knowledge_base') === 'true'
|
||||||
)
|
)
|
||||||
|
const [isNomadMdModalOpen, setIsNomadMdModalOpen] = useState(false)
|
||||||
|
|
||||||
function handleCloseKnowledgeBase() {
|
function handleCloseKnowledgeBase() {
|
||||||
setIsKnowledgeBaseModalOpen(false)
|
setIsKnowledgeBaseModalOpen(false)
|
||||||
|
|
@ -127,6 +129,17 @@ export default function ChatSidebar({
|
||||||
>
|
>
|
||||||
Knowledge Base
|
Knowledge Base
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
|
<StyledButton
|
||||||
|
onClick={() => {
|
||||||
|
setIsNomadMdModalOpen(true)
|
||||||
|
}}
|
||||||
|
icon="IconFileDescription"
|
||||||
|
variant="primary"
|
||||||
|
size="sm"
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
|
NOMAD.md
|
||||||
|
</StyledButton>
|
||||||
{sessions.length > 0 && (
|
{sessions.length > 0 && (
|
||||||
<StyledButton
|
<StyledButton
|
||||||
onClick={onClearHistory}
|
onClick={onClearHistory}
|
||||||
|
|
@ -142,6 +155,12 @@ export default function ChatSidebar({
|
||||||
{isKnowledgeBaseModalOpen && (
|
{isKnowledgeBaseModalOpen && (
|
||||||
<KnowledgeBaseModal aiAssistantName={aiAssistantName} onClose={handleCloseKnowledgeBase} />
|
<KnowledgeBaseModal aiAssistantName={aiAssistantName} onClose={handleCloseKnowledgeBase} />
|
||||||
)}
|
)}
|
||||||
|
{isNomadMdModalOpen && (
|
||||||
|
<NomadMdModal
|
||||||
|
aiAssistantName={aiAssistantName}
|
||||||
|
onClose={() => setIsNomadMdModalOpen(false)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { IconX } from '@tabler/icons-react'
|
||||||
|
import StyledButton from '~/components/StyledButton'
|
||||||
|
import MarkdownEditor from '~/components/MarkdownEditor'
|
||||||
|
import { useNotifications } from '~/context/NotificationContext'
|
||||||
|
import api from '~/lib/api'
|
||||||
|
|
||||||
|
interface NomadMdModalProps {
|
||||||
|
aiAssistantName?: string
|
||||||
|
onClose: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seeded into the editor when no NOMAD.md exists yet. Nothing is written to disk
|
||||||
|
// until the user saves, so this is purely a starting point they can replace.
|
||||||
|
const NOMAD_MD_TEMPLATE = `# NOMAD.md
|
||||||
|
|
||||||
|
<!--
|
||||||
|
This file holds custom instructions for your AI assistant. Everything here is
|
||||||
|
sent to the assistant as a system prompt on every chat — use it to set persona,
|
||||||
|
tone, priorities, and standing rules.
|
||||||
|
|
||||||
|
It is also stored on disk at storage/NOMAD.md, so you can edit it directly.
|
||||||
|
Replace this template with your own instructions, then click Save.
|
||||||
|
-->
|
||||||
|
|
||||||
|
## About me
|
||||||
|
|
||||||
|
- (e.g. I'm setting up an off-grid homestead in a cold climate.)
|
||||||
|
|
||||||
|
## How the assistant should respond
|
||||||
|
|
||||||
|
- Be concise and practical.
|
||||||
|
- Prioritize safety and proven methods.
|
||||||
|
`
|
||||||
|
|
||||||
|
export default function NomadMdModal({ aiAssistantName, onClose }: NomadMdModalProps) {
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
const { addNotification } = useNotifications()
|
||||||
|
const [content, setContent] = useState<string | null>(null)
|
||||||
|
|
||||||
|
const { data, isLoading } = useQuery({
|
||||||
|
queryKey: ['nomad-md'],
|
||||||
|
queryFn: () => api.getNomadMd(),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Seed the editor once the file loads: existing content, or the template when empty.
|
||||||
|
useEffect(() => {
|
||||||
|
if (data && content === null) {
|
||||||
|
setContent(data.content.trim().length > 0 ? data.content : NOMAD_MD_TEMPLATE)
|
||||||
|
}
|
||||||
|
}, [data, content])
|
||||||
|
|
||||||
|
const saveMutation = useMutation({
|
||||||
|
mutationFn: (value: string) => api.saveNomadMd(value),
|
||||||
|
onSuccess: (result) => {
|
||||||
|
if (!result?.success) {
|
||||||
|
addNotification({ type: 'error', message: 'Failed to save NOMAD.md.' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
addNotification({ type: 'success', message: 'NOMAD.md saved. It applies to new messages.' })
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['nomad-md'] })
|
||||||
|
onClose()
|
||||||
|
},
|
||||||
|
onError: (error: any) => {
|
||||||
|
addNotification({ type: 'error', message: error?.message || 'Failed to save NOMAD.md.' })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const assistantName = aiAssistantName?.trim() || 'your AI assistant'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/30 backdrop-blur-sm transition-opacity">
|
||||||
|
<div className="bg-surface-primary rounded-lg shadow-xl max-w-4xl w-full max-h-[90vh] overflow-hidden flex flex-col">
|
||||||
|
<div className="flex items-center justify-between p-6 border-b border-border-subtle shrink-0">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-2xl font-semibold text-text-primary">NOMAD.md</h2>
|
||||||
|
<p className="text-sm text-text-muted mt-1">
|
||||||
|
Custom instructions passed to {assistantName} as a system prompt on every chat.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="p-2 hover:bg-surface-secondary rounded-lg transition-colors"
|
||||||
|
>
|
||||||
|
<IconX className="h-6 w-6 text-text-muted" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="overflow-y-auto flex-1 p-6">
|
||||||
|
{isLoading || content === null ? (
|
||||||
|
<div className="py-16 text-center text-text-muted">Loading…</div>
|
||||||
|
) : (
|
||||||
|
<div className="rounded-lg border border-border-subtle overflow-hidden h-[55vh]">
|
||||||
|
<MarkdownEditor initialValue={content} onChange={setContent} className="h-full text-sm" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<p className="text-xs text-text-muted mt-3">
|
||||||
|
Tip: this file is also stored on disk at{' '}
|
||||||
|
<code className="font-mono">storage/NOMAD.md</code> and can be edited directly.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-end gap-3 p-6 border-t border-border-subtle shrink-0">
|
||||||
|
<StyledButton variant="outline" onClick={onClose} disabled={saveMutation.isPending}>
|
||||||
|
Cancel
|
||||||
|
</StyledButton>
|
||||||
|
<StyledButton
|
||||||
|
variant="primary"
|
||||||
|
icon="IconCircleCheck"
|
||||||
|
onClick={() => content !== null && saveMutation.mutate(content)}
|
||||||
|
loading={saveMutation.isPending}
|
||||||
|
disabled={content === null}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</StyledButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1021,6 +1021,23 @@ class API {
|
||||||
})()
|
})()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getNomadMd() {
|
||||||
|
return catchInternal(async () => {
|
||||||
|
const response = await this.client.get<{ content: string }>('/ai/nomad-md')
|
||||||
|
return response.data
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveNomadMd(content: string) {
|
||||||
|
return catchInternal(async () => {
|
||||||
|
const response = await this.client.put<{ success: boolean; message: string }>(
|
||||||
|
'/ai/nomad-md',
|
||||||
|
{ content }
|
||||||
|
)
|
||||||
|
return response.data
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
|
||||||
async preflightCheck(service_name: string) {
|
async preflightCheck(service_name: string) {
|
||||||
return catchInternal(async () => {
|
return catchInternal(async () => {
|
||||||
const response = await this.client.get<{
|
const response = await this.client.get<{
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,10 @@
|
||||||
"@adonisjs/transmit-client": "1.1.0",
|
"@adonisjs/transmit-client": "1.1.0",
|
||||||
"@adonisjs/vite": "4.0.0",
|
"@adonisjs/vite": "4.0.0",
|
||||||
"@chonkiejs/core": "0.0.7",
|
"@chonkiejs/core": "0.0.7",
|
||||||
|
"@codemirror/lang-markdown": "6.5.0",
|
||||||
|
"@codemirror/state": "6.7.1",
|
||||||
|
"@codemirror/theme-one-dark": "6.1.3",
|
||||||
|
"@codemirror/view": "6.43.6",
|
||||||
"@headlessui/react": "2.2.9",
|
"@headlessui/react": "2.2.9",
|
||||||
"@inertiajs/react": "2.3.13",
|
"@inertiajs/react": "2.3.13",
|
||||||
"@markdoc/markdoc": "0.5.4",
|
"@markdoc/markdoc": "0.5.4",
|
||||||
|
|
@ -43,6 +47,7 @@
|
||||||
"better-sqlite3": "12.6.2",
|
"better-sqlite3": "12.6.2",
|
||||||
"bullmq": "5.77.6",
|
"bullmq": "5.77.6",
|
||||||
"cheerio": "1.2.0",
|
"cheerio": "1.2.0",
|
||||||
|
"codemirror": "6.0.2",
|
||||||
"compression": "1.8.1",
|
"compression": "1.8.1",
|
||||||
"dockerode": "4.0.9",
|
"dockerode": "4.0.9",
|
||||||
"edge.js": "6.4.0",
|
"edge.js": "6.4.0",
|
||||||
|
|
@ -1270,6 +1275,159 @@
|
||||||
"@chonkiejs/chunk": "^0.9.3"
|
"@chonkiejs/chunk": "^0.9.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@codemirror/autocomplete": {
|
||||||
|
"version": "6.20.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.3.tgz",
|
||||||
|
"integrity": "sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.17.0",
|
||||||
|
"@lezer/common": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/commands": {
|
||||||
|
"version": "6.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.4.tgz",
|
||||||
|
"integrity": "sha512-Ryk9y9T0FFVF0cUGhAknveAyUOl/A1qReTFi+qPKtOh2Z9F4AUBz3XOrYD4ZEgZirdugVzHvd/2/Wcwy5OliTg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.7.0",
|
||||||
|
"@codemirror/view": "^6.27.0",
|
||||||
|
"@lezer/common": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/lang-css": {
|
||||||
|
"version": "6.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz",
|
||||||
|
"integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@lezer/common": "^1.0.2",
|
||||||
|
"@lezer/css": "^1.1.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/lang-html": {
|
||||||
|
"version": "6.4.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.11.tgz",
|
||||||
|
"integrity": "sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
|
"@codemirror/lang-css": "^6.0.0",
|
||||||
|
"@codemirror/lang-javascript": "^6.0.0",
|
||||||
|
"@codemirror/language": "^6.4.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.17.0",
|
||||||
|
"@lezer/common": "^1.0.0",
|
||||||
|
"@lezer/css": "^1.1.0",
|
||||||
|
"@lezer/html": "^1.3.12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/lang-javascript": {
|
||||||
|
"version": "6.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.5.tgz",
|
||||||
|
"integrity": "sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
|
"@codemirror/language": "^6.6.0",
|
||||||
|
"@codemirror/lint": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.17.0",
|
||||||
|
"@lezer/common": "^1.0.0",
|
||||||
|
"@lezer/javascript": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/lang-markdown": {
|
||||||
|
"version": "6.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-6.5.0.tgz",
|
||||||
|
"integrity": "sha512-0K40bZ35jpHya6FriukbgaleaqzBLZfOh7HuzqbMxBXkbYMJDxfF39c23xOgxFezR+3G+tR2/Mup+Xk865OMvw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/autocomplete": "^6.7.1",
|
||||||
|
"@codemirror/lang-html": "^6.0.0",
|
||||||
|
"@codemirror/language": "^6.3.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0",
|
||||||
|
"@lezer/common": "^1.2.1",
|
||||||
|
"@lezer/markdown": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/language": {
|
||||||
|
"version": "6.12.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.4.tgz",
|
||||||
|
"integrity": "sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.23.0",
|
||||||
|
"@lezer/common": "^1.5.0",
|
||||||
|
"@lezer/highlight": "^1.0.0",
|
||||||
|
"@lezer/lr": "^1.0.0",
|
||||||
|
"style-mod": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/lint": {
|
||||||
|
"version": "6.9.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.7.tgz",
|
||||||
|
"integrity": "sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.42.0",
|
||||||
|
"crelt": "^1.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/search": {
|
||||||
|
"version": "6.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.7.1.tgz",
|
||||||
|
"integrity": "sha512-uMe5UO6PamJtSHrXhhHOzSX3ReWtiJrva6GnPMwSOrZtiExb5X5eExhr2OUZQVvdxPsKpY3Ro2mFbQadpPWmHA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.37.0",
|
||||||
|
"crelt": "^1.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/state": {
|
||||||
|
"version": "6.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.7.1.tgz",
|
||||||
|
"integrity": "sha512-9QzNDgE4EYDnAHfrTlR2lwiPciiOymLtwKK+8yHQzCc7GXhAP9xdEbEJFy2IWB1j9UGUl9BsgMmTo/ImA02T7A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@marijn/find-cluster-break": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/theme-one-dark": {
|
||||||
|
"version": "6.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz",
|
||||||
|
"integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0",
|
||||||
|
"@lezer/highlight": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/view": {
|
||||||
|
"version": "6.43.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.6.tgz",
|
||||||
|
"integrity": "sha512-EVunGSYN1wz1p75WY1s3Xg7t3i8Yol0kGZGizNdX9BUFgMFILYVe8/u6EVpo7Ff5PwbZuILb4QAq7IZoKzIEQA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/state": "^6.7.0",
|
||||||
|
"crelt": "^1.0.6",
|
||||||
|
"style-mod": "^4.1.0",
|
||||||
|
"w3c-keyname": "^2.2.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@colors/colors": {
|
"node_modules/@colors/colors": {
|
||||||
"version": "1.5.0",
|
"version": "1.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
|
||||||
|
|
@ -3142,6 +3300,73 @@
|
||||||
"url": "https://opencollective.com/js-sdsl"
|
"url": "https://opencollective.com/js-sdsl"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@lezer/common": {
|
||||||
|
"version": "1.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.2.tgz",
|
||||||
|
"integrity": "sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@lezer/css": {
|
||||||
|
"version": "1.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.3.4.tgz",
|
||||||
|
"integrity": "sha512-N+tn9tej2hPvyKgHEApMOQfHczDJCwxrRFS3SPn9QjYN+uwHvEDnCgKRrb3mxDYxRS8sKMM8fhC3+lc04Abz5Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@lezer/common": "^1.2.0",
|
||||||
|
"@lezer/highlight": "^1.0.0",
|
||||||
|
"@lezer/lr": "^1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@lezer/highlight": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@lezer/common": "^1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@lezer/html": {
|
||||||
|
"version": "1.3.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.13.tgz",
|
||||||
|
"integrity": "sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@lezer/common": "^1.2.0",
|
||||||
|
"@lezer/highlight": "^1.0.0",
|
||||||
|
"@lezer/lr": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@lezer/javascript": {
|
||||||
|
"version": "1.5.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz",
|
||||||
|
"integrity": "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@lezer/common": "^1.2.0",
|
||||||
|
"@lezer/highlight": "^1.1.3",
|
||||||
|
"@lezer/lr": "^1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@lezer/lr": {
|
||||||
|
"version": "1.4.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.10.tgz",
|
||||||
|
"integrity": "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@lezer/common": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@lezer/markdown": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.7.0.tgz",
|
||||||
|
"integrity": "sha512-zZDdfKkXl1CBYet/nL2jCskQXC+8DpbhZubbTEGqbqpkaBC4w03F5Kt9ZLDyqbadqIvvZ7VrVhd6d6qgurzAew==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@lezer/common": "^1.5.0",
|
||||||
|
"@lezer/highlight": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@mapbox/geojson-rewind": {
|
"node_modules/@mapbox/geojson-rewind": {
|
||||||
"version": "0.5.2",
|
"version": "0.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz",
|
||||||
|
|
@ -3237,6 +3462,12 @@
|
||||||
"integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==",
|
"integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/@marijn/find-cluster-break": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-FY+MKLBoTsLNJF/eLWaOsXGdz6uh3Iu1axjPf6TUq92IYumcTcXWHoS747JARLkcdlJ/Waiaxc5wQfFO8jC6NA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@markdoc/markdoc": {
|
"node_modules/@markdoc/markdoc": {
|
||||||
"version": "0.5.4",
|
"version": "0.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/@markdoc/markdoc/-/markdoc-0.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@markdoc/markdoc/-/markdoc-0.5.4.tgz",
|
||||||
|
|
@ -7569,6 +7800,21 @@
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/codemirror": {
|
||||||
|
"version": "6.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz",
|
||||||
|
"integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
|
"@codemirror/commands": "^6.0.0",
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/lint": "^6.0.0",
|
||||||
|
"@codemirror/search": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
|
@ -7821,6 +8067,12 @@
|
||||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/crelt": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/cron-parser": {
|
"node_modules/cron-parser": {
|
||||||
"version": "4.9.0",
|
"version": "4.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz",
|
||||||
|
|
@ -15953,6 +16205,12 @@
|
||||||
"url": "https://github.com/sponsors/Borewit"
|
"url": "https://github.com/sponsors/Borewit"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/style-mod": {
|
||||||
|
"version": "4.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz",
|
||||||
|
"integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/style-to-js": {
|
"node_modules/style-to-js": {
|
||||||
"version": "1.1.21",
|
"version": "1.1.21",
|
||||||
"resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz",
|
"resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz",
|
||||||
|
|
@ -17031,6 +17289,12 @@
|
||||||
"pbf": "^3.2.1"
|
"pbf": "^3.2.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/w3c-keyname": {
|
||||||
|
"version": "2.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
|
||||||
|
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/wasm-feature-detect": {
|
"node_modules/wasm-feature-detect": {
|
||||||
"version": "1.8.0",
|
"version": "1.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,10 @@
|
||||||
"@adonisjs/transmit-client": "1.1.0",
|
"@adonisjs/transmit-client": "1.1.0",
|
||||||
"@adonisjs/vite": "4.0.0",
|
"@adonisjs/vite": "4.0.0",
|
||||||
"@chonkiejs/core": "0.0.7",
|
"@chonkiejs/core": "0.0.7",
|
||||||
|
"@codemirror/lang-markdown": "6.5.0",
|
||||||
|
"@codemirror/state": "6.7.1",
|
||||||
|
"@codemirror/theme-one-dark": "6.1.3",
|
||||||
|
"@codemirror/view": "6.43.6",
|
||||||
"@headlessui/react": "2.2.9",
|
"@headlessui/react": "2.2.9",
|
||||||
"@inertiajs/react": "2.3.13",
|
"@inertiajs/react": "2.3.13",
|
||||||
"@markdoc/markdoc": "0.5.4",
|
"@markdoc/markdoc": "0.5.4",
|
||||||
|
|
@ -96,6 +100,7 @@
|
||||||
"better-sqlite3": "12.6.2",
|
"better-sqlite3": "12.6.2",
|
||||||
"bullmq": "5.77.6",
|
"bullmq": "5.77.6",
|
||||||
"cheerio": "1.2.0",
|
"cheerio": "1.2.0",
|
||||||
|
"codemirror": "6.0.2",
|
||||||
"compression": "1.8.1",
|
"compression": "1.8.1",
|
||||||
"dockerode": "4.0.9",
|
"dockerode": "4.0.9",
|
||||||
"edge.js": "6.4.0",
|
"edge.js": "6.4.0",
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import DownloadsController from '#controllers/downloads_controller'
|
||||||
import EasySetupController from '#controllers/easy_setup_controller'
|
import EasySetupController from '#controllers/easy_setup_controller'
|
||||||
import HomeController from '#controllers/home_controller'
|
import HomeController from '#controllers/home_controller'
|
||||||
import MapsController from '#controllers/maps_controller'
|
import MapsController from '#controllers/maps_controller'
|
||||||
|
import NomadMdController from '#controllers/nomad_md_controller'
|
||||||
import OllamaController from '#controllers/ollama_controller'
|
import OllamaController from '#controllers/ollama_controller'
|
||||||
import RagController from '#controllers/rag_controller'
|
import RagController from '#controllers/rag_controller'
|
||||||
import SettingsController from '#controllers/settings_controller'
|
import SettingsController from '#controllers/settings_controller'
|
||||||
|
|
@ -127,6 +128,13 @@ router
|
||||||
})
|
})
|
||||||
.prefix('/api/ollama')
|
.prefix('/api/ollama')
|
||||||
|
|
||||||
|
router
|
||||||
|
.group(() => {
|
||||||
|
router.get('/nomad-md', [NomadMdController, 'show'])
|
||||||
|
router.put('/nomad-md', [NomadMdController, 'update'])
|
||||||
|
})
|
||||||
|
.prefix('/api/ai')
|
||||||
|
|
||||||
router
|
router
|
||||||
.group(() => {
|
.group(() => {
|
||||||
router.get('/', [ChatsController, 'index'])
|
router.get('/', [ChatsController, 'index'])
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue