All the MD files via Database

This commit is contained in:
2026-04-11 21:14:09 +02:00
parent c67259ebe0
commit fdb56490dd
14 changed files with 1004 additions and 240 deletions

View File

@@ -1,6 +1,6 @@
"use client";
import { useState, useCallback } from "react";
import { useState, useCallback, useEffect, useRef } from "react";
import { useTranslations } from "next-intl";
import { Card } from "@/components/ui/card";
import { PACKAGE_CATALOG, type PackageDef } from "@/lib/packages";
@@ -9,7 +9,8 @@ type Step = "welcome" | "configure" | "billing" | "confirm";
const STEPS: Step[] = ["welcome", "configure", "billing", "confirm"];
const DEFAULT_SOUL = `# AI Assistant
// Inline fallbacks — only used if the API call to /api/workspace-defaults fails
const FALLBACK_SOUL = `# AI Assistant
You are a helpful AI assistant for {company}. You are professional, concise, and friendly.
@@ -20,6 +21,25 @@ You are a helpful AI assistant for {company}. You are professional, concise, and
- Respect privacy and confidentiality
`;
const FALLBACK_AGENTS = `# Agents
On session start, read the following workspace files in order:
1. SOUL.md — your personality and behavioural guidelines
2. TOOLS.md — available tools and how to use them
3. USER.md — information about the current user (if present)
Follow the instructions in SOUL.md for every interaction.
`;
const FALLBACK_TOOLS = `# Tools
The following tools are available to you as an AI assistant.
## LLM
You have access to a large language model for text generation, summarisation,
translation, and general question answering.
`;
const CATEGORIES = [
{ key: "channel" as const, labelKey: "categories.channels" },
{ key: "skill" as const, labelKey: "categories.skills" },
@@ -38,10 +58,13 @@ export function OnboardingWizard({ orgName, onComplete }: WizardProps) {
const [step, setStep] = useState<Step>("welcome");
const [submitting, setSubmitting] = useState(false);
const [error, setError] = useState("");
const [advancedOpen, setAdvancedOpen] = useState(false);
const [defaultsLoaded, setDefaultsLoaded] = useState(false);
const [config, setConfig] = useState({
agentName: "Assistant",
soulMd: DEFAULT_SOUL.replace("{company}", orgName),
soulMd: FALLBACK_SOUL.replace("{company}", orgName),
agentsMd: FALLBACK_AGENTS,
packages: [] as string[],
billingAddress: {
company: orgName,
@@ -53,6 +76,9 @@ export function OnboardingWizard({ orgName, onComplete }: WizardProps) {
billingNotes: "",
});
// TOOLS.md preview — readonly, auto-generated
const [toolsMdPreview, setToolsMdPreview] = useState(FALLBACK_TOOLS);
// Per-package collected secrets: { "telegram": { "bot-token": "123:ABC" }, ... }
const [packageSecrets, setPackageSecrets] = useState<
Record<string, Record<string, string>>
@@ -62,6 +88,42 @@ export function OnboardingWizard({ orgName, onComplete }: WizardProps) {
Record<string, boolean>
>({});
// Fetch DB-stored defaults on mount
useEffect(() => {
fetch(`/api/workspace-defaults?orgName=${encodeURIComponent(orgName)}`)
.then((r) => (r.ok ? r.json() : null))
.then((data) => {
if (data) {
setConfig((prev) => ({
...prev,
soulMd: data.soulMd ?? prev.soulMd,
agentsMd: data.agentsMd ?? prev.agentsMd,
}));
setToolsMdPreview(data.toolsMd ?? FALLBACK_TOOLS);
setDefaultsLoaded(true);
}
})
.catch(() => {
/* use inline fallbacks */
});
}, [orgName]);
// Re-fetch TOOLS.md preview when packages change
const packagesKey = config.packages.sort().join(",");
const prevPackagesKey = useRef(packagesKey);
useEffect(() => {
if (prevPackagesKey.current === packagesKey && defaultsLoaded) return;
prevPackagesKey.current = packagesKey;
fetch(
`/api/workspace-defaults?orgName=${encodeURIComponent(orgName)}&packages=${encodeURIComponent(packagesKey)}`
)
.then((r) => (r.ok ? r.json() : null))
.then((data) => {
if (data?.toolsMd) setToolsMdPreview(data.toolsMd);
})
.catch(() => {});
}, [packagesKey, orgName, defaultsLoaded]);
const stepIndex = STEPS.indexOf(step);
const goNext = () => {
@@ -274,6 +336,74 @@ export function OnboardingWizard({ orgName, onComplete }: WizardProps) {
</p>
</div>
{/* Advanced: AGENTS.md + TOOLS.md preview */}
<div className="border border-border rounded-lg overflow-hidden">
<button
type="button"
onClick={() => setAdvancedOpen((o) => !o)}
className="w-full flex items-center justify-between px-3 py-2.5 text-left hover:bg-surface-3/30 transition-colors"
>
<span className="text-xs font-semibold uppercase tracking-wider text-text-muted">
{t("advancedConfig")}
</span>
<svg
className={`h-4 w-4 text-text-muted transition-transform ${
advancedOpen ? "rotate-180" : ""
}`}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M19 9l-7 7-7-7"
/>
</svg>
</button>
{advancedOpen && (
<div className="border-t border-border px-3 py-4 space-y-4 bg-surface-1/30">
{/* AGENTS.md */}
<div>
<label className="block text-xs font-semibold uppercase tracking-wider text-text-muted mb-1.5">
{t("agentsMd")}
</label>
<textarea
value={config.agentsMd}
onChange={(e) =>
setConfig((prev) => ({
...prev,
agentsMd: e.target.value,
}))
}
rows={6}
className="w-full px-3 py-2 bg-surface-2 border border-border rounded-lg text-sm text-text-primary font-mono text-xs focus:outline-none focus:ring-1 focus:ring-accent focus:border-accent transition-colors resize-y"
/>
<p className="text-xs text-text-muted mt-1">
{t("agentsMdHint")}
</p>
</div>
{/* TOOLS.md — readonly preview */}
<div>
<label className="block text-xs font-semibold uppercase tracking-wider text-text-muted mb-1.5">
{t("toolsMd")}
</label>
<textarea
value={toolsMdPreview}
readOnly
rows={6}
className="w-full px-3 py-2 bg-surface-3/50 border border-border rounded-lg text-sm text-text-secondary font-mono text-xs cursor-not-allowed resize-y"
/>
<p className="text-xs text-text-muted mt-1">
{t("toolsMdHint")}
</p>
</div>
</div>
)}
</div>
{/* Packages — grouped by category */}
<div>
<label className="block text-xs font-semibold uppercase tracking-wider text-text-muted mb-2">