kalhdrawi commited on
Commit
65ef364
·
1 Parent(s): 0283cd2

Reupload OmniDev clean version

Browse files
app/api/augment/route.ts ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { NextRequest, NextResponse } from "next/server";
3
+ import { GoogleGenAI } from "@google/genai";
4
+ import { InferenceClient } from "@huggingface/inference";
5
+ import type { AugmentRequest, AugmentResponse } from "@/types";
6
+
7
+ const SYS = `You are Omni Engine, an autonomous code evolution system.
8
+ You receive:
9
+ - project context (tree + critical file excerpts)
10
+ - a user instruction
11
+
12
+ You MUST respond ONLY with strict JSON, no comments, no markdown fences.
13
+ JSON schema:
14
+ {
15
+ "ok": true,
16
+ "summary": string,
17
+ "logs": string[],
18
+ "files": [
19
+ { "path": string, "action": "add"|"update"|"delete", "content"?: string, "note"?: string }
20
+ ]
21
+ }
22
+
23
+ Constraints:
24
+ - Ensure paths are POSIX-like and rooted from repo (e.g., /frontend/src/App.tsx)
25
+ - For add/update, provide full file content (ready to run)
26
+ - Keep changes minimal and consistent
27
+ - Produce compilable code for the chosen language/framework
28
+ `;
29
+
30
+ function extractJson(text: string): any {
31
+ try {
32
+ return JSON.parse(text);
33
+ } catch {
34
+ // Try to strip code fences or extraneous text
35
+ const start = text.indexOf("{");
36
+ const end = text.lastIndexOf("}");
37
+ if (start >= 0 && end > start) {
38
+ const candidate = text.slice(start, end + 1);
39
+ return JSON.parse(candidate);
40
+ }
41
+ throw new Error("Model did not return valid JSON");
42
+ }
43
+ }
44
+
45
+ export async function POST(req: NextRequest) {
46
+ try {
47
+ const body = (await req.json()) as AugmentRequest;
48
+ const { context, instruction, language = "javascript", framework = "express-react", response_type = "file_updates", model, provider } = body || {} as AugmentRequest;
49
+ if (!context || !instruction) {
50
+ return NextResponse.json({ ok: false, message: "Missing context or instruction" } as AugmentResponse, { status: 400 });
51
+ }
52
+
53
+ const userPrompt = `Project Context:\n${context}\n\nInstruction:\n${instruction}\n\nTarget:\n- language: ${language}\n- framework: ${framework}\n- response_type: ${response_type}`;
54
+
55
+ let text = "";
56
+ if ((provider || "").toLowerCase() === "google" || (model || "").toLowerCase().startsWith("gemini-")) {
57
+ const apiKey = process.env.GEMINI_API_KEY;
58
+ if (!apiKey) return NextResponse.json({ ok: false, message: "Missing GEMINI_API_KEY" } as AugmentResponse, { status: 500 });
59
+ const ai = new GoogleGenAI({ apiKey });
60
+ const res = await ai.models.generateContent({
61
+ model: model || "gemini-2.5-flash",
62
+ contents: [
63
+ { role: "user", parts: [{ text: SYS }] },
64
+ { role: "user", parts: [{ text: userPrompt }] },
65
+ ],
66
+ config: { maxOutputTokens: 4096 },
67
+ } as any);
68
+ text = (res as any)?.candidates?.[0]?.content?.parts?.[0]?.text || "";
69
+ } else {
70
+ const token = process.env.HF_TOKEN || process.env.DEFAULT_HF_TOKEN;
71
+ const client = new InferenceClient(token);
72
+ const res = await client.chatCompletion({
73
+ model: model || "deepseek-ai/DeepSeek-V3.1",
74
+ provider: (provider as any) || undefined,
75
+ messages: [
76
+ { role: "system", content: SYS },
77
+ { role: "user", content: userPrompt },
78
+ ],
79
+ });
80
+ text = res.choices?.[0]?.message?.content || "";
81
+ }
82
+
83
+ if (!text.trim()) {
84
+ return NextResponse.json({ ok: false, message: "Empty model response" } as AugmentResponse, { status: 500 });
85
+ }
86
+
87
+ let json: any;
88
+ try {
89
+ json = extractJson(text);
90
+ } catch (e: any) {
91
+ return NextResponse.json({ ok: false, message: e?.message || "Invalid JSON from model", raw: text } as any, { status: 500 });
92
+ }
93
+
94
+ // Minimal validation
95
+ if (json && json.ok && Array.isArray(json.files)) {
96
+ return NextResponse.json(json as AugmentResponse, { status: 200 });
97
+ }
98
+
99
+ return NextResponse.json({ ok: false, message: "Model returned unexpected shape", raw: json } as any, { status: 500 });
100
+ } catch (e: any) {
101
+ return NextResponse.json({ ok: false, message: e?.message || "Internal error" } as AugmentResponse, { status: 500 });
102
+ }
103
+ }
104
+
app/api/me/projects/[namespace]/[repoId]/apply/route.ts ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ import { uploadFiles, deleteFiles } from "@huggingface/hub";
3
+
4
+ import { isAuthenticated } from "@/lib/auth";
5
+ import type { FileUpdate } from "@/types";
6
+
7
+ export async function POST(
8
+ req: NextRequest,
9
+ { params }: { params: Promise<{ namespace: string; repoId: string }> }
10
+ ) {
11
+ const user = await isAuthenticated();
12
+ if (user instanceof NextResponse || !user) {
13
+ return NextResponse.json({ message: "Unauthorized" }, { status: 401 });
14
+ }
15
+
16
+ const { files, commitTitle = "OmniDev: Apply changes" } = await req.json();
17
+ if (!Array.isArray(files) || files.length === 0) {
18
+ return NextResponse.json(
19
+ { ok: false, error: "files array is required" },
20
+ { status: 400 }
21
+ );
22
+ }
23
+
24
+ const { namespace, repoId } = await params;
25
+ const repo = { type: "space" as const, name: `${namespace}/${repoId}` };
26
+
27
+ try {
28
+ const toDelete: string[] = [];
29
+ const toUpload: File[] = [];
30
+
31
+ (files as FileUpdate[]).forEach((f) => {
32
+ const path = (f.path || '').replace(/^\/+/, '');
33
+ if (f.action === 'delete') {
34
+ toDelete.push(path);
35
+ } else if ((f.action === 'add' || f.action === 'update') && typeof f.content === 'string') {
36
+ const type = path.endsWith('.html') ? 'text/html' : 'text/plain';
37
+ toUpload.push(new File([f.content], path, { type }));
38
+ }
39
+ });
40
+
41
+ const results: any = { ok: true, commits: [] };
42
+
43
+ if (toDelete.length > 0) {
44
+ const delRes = await deleteFiles({ repo, accessToken: user.token as string, paths: toDelete });
45
+ results.commits.push({ type: 'delete', ...delRes.commit });
46
+ }
47
+
48
+ if (toUpload.length > 0) {
49
+ const upRes = await uploadFiles({ repo, files: toUpload, accessToken: user.token as string, commitTitle });
50
+ results.commits.push({ type: 'upload', ...upRes.commit, title: commitTitle });
51
+ }
52
+
53
+ return NextResponse.json(results);
54
+ } catch (error: any) {
55
+ console.error('Apply changes failed:', error);
56
+ return NextResponse.json({ ok: false, error: error.message || 'Failed to apply changes' }, { status: 500 });
57
+ }
58
+ }
59
+
app/console/page.tsx ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ import OmniConsole from "@/components/omni-console";
2
+
3
+ export default function ConsolePage() {
4
+ return (
5
+ <main className="max-w-5xl mx-auto p-6">
6
+ <OmniConsole />
7
+ </main>
8
+ );
9
+ }
10
+
app/layout.tsx CHANGED
@@ -28,14 +28,14 @@ const ptSans = PT_Sans({
28
 
29
  export const metadata: Metadata = {
30
  ...generateSEO({
31
- title: "DeepSite | Build with AI ✨",
32
  description:
33
- "DeepSite is a web development tool that helps you build websites with AI, no code required. Let's deploy your website with DeepSite and enjoy the magic of AI.",
34
  path: "/",
35
  }),
36
  appleWebApp: {
37
  capable: true,
38
- title: "DeepSite",
39
  statusBarStyle: "black-translucent",
40
  },
41
  icons: {
@@ -77,16 +77,16 @@ export default async function RootLayout({
77
  }>) {
78
  const data = await getMe();
79
 
80
- // Generate structured data
81
  const structuredData = generateStructuredData("WebApplication", {
82
- name: "DeepSite",
83
- description: "Build websites with AI, no code required",
84
- url: "https://deepsite.hf.co",
85
  });
86
 
87
  const organizationData = generateStructuredData("Organization", {
88
- name: "DeepSite",
89
- url: "https://deepsite.hf.co",
90
  });
91
 
92
  return (
@@ -105,11 +105,15 @@ export default async function RootLayout({
105
  }}
106
  />
107
  </head>
108
- <Script
109
- defer
110
- data-domain="deepsite.hf.co"
111
- src="https://plausible.io/js/script.js"
112
- ></Script>
 
 
 
 
113
  <body
114
  className={`${inter.variable} ${ptSans.variable} antialiased bg-black dark h-[100dvh] overflow-hidden`}
115
  >
@@ -128,3 +132,4 @@ export default async function RootLayout({
128
  </html>
129
  );
130
  }
 
 
28
 
29
  export const metadata: Metadata = {
30
  ...generateSEO({
31
+ title: "OmniDev | AI Project Builder",
32
  description:
33
+ "OmniDev is an AI engineering environment that builds complete web projects (frontend + backend) from natural language.",
34
  path: "/",
35
  }),
36
  appleWebApp: {
37
  capable: true,
38
+ title: "OmniDev",
39
  statusBarStyle: "black-translucent",
40
  },
41
  icons: {
 
77
  }>) {
78
  const data = await getMe();
79
 
80
+ // Structured data with OmniDev
81
  const structuredData = generateStructuredData("WebApplication", {
82
+ name: "OmniDev",
83
+ description: "AI-powered full-stack project builder",
84
+ url: process.env.PUBLIC_BASE_URL || "https://omnidev.hf.co",
85
  });
86
 
87
  const organizationData = generateStructuredData("Organization", {
88
+ name: "OmniDev",
89
+ url: process.env.PUBLIC_BASE_URL || "https://omnidev.hf.co",
90
  });
91
 
92
  return (
 
105
  }}
106
  />
107
  </head>
108
+ {process.env.PUBLIC_BASE_URL && (
109
+ <Script
110
+ defer
111
+ data-domain={(process.env.PUBLIC_BASE_URL as string)
112
+ .replace("https://", "")
113
+ .replace("http://", "")}
114
+ src="https://plausible.io/js/script.js"
115
+ />
116
+ )}
117
  <body
118
  className={`${inter.variable} ${ptSans.variable} antialiased bg-black dark h-[100dvh] overflow-hidden`}
119
  >
 
132
  </html>
133
  );
134
  }
135
+
components/editor/index.tsx CHANGED
@@ -49,8 +49,8 @@ export const AppEditor = ({
49
  const editorRef = useRef<editor.IStandaloneCodeEditor | null>(null);
50
 
51
  useMount(() => {
 
52
  if (isNew && pagesStorage) {
53
- setPages(pagesStorage);
54
  removePagesStorage();
55
  }
56
  });
 
49
  const editorRef = useRef<editor.IStandaloneCodeEditor | null>(null);
50
 
51
  useMount(() => {
52
+ // Always start clean for new projects; clear any leftover local storage pages
53
  if (isNew && pagesStorage) {
 
54
  removePagesStorage();
55
  }
56
  });
components/iframe-detector/modal.tsx CHANGED
@@ -59,3 +59,4 @@ IframeWarningModalProps) {
59
  </Dialog>
60
  );
61
  }
 
 
59
  </Dialog>
60
  );
61
  }
62
+
components/omni-console/README.md ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ Omni Console
2
+
3
+ This console demonstrates the Omni Engine augment flow:
4
+
5
+ - Use the Project Context to paste a tree and key file excerpts.
6
+ - Enter an Instruction (e.g., Add JWT auth with /auth/login route).
7
+ - Click Run to get an AugmentResponse including file updates.
8
+ - Provide the target Space "namespace/repoId" then click Apply Changes to commit to your Space.
9
+
components/omni-console/index.tsx ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+ import { useState } from "react";
3
+ import { Button } from "@/components/ui/button";
4
+ import { TextareaHTMLAttributes } from "react";
5
+ import { useAi } from "@/hooks/useAi";
6
+
7
+ export default function OmniConsole() {
8
+ const [instruction, setInstruction] = useState("");
9
+ const [context, setContext] = useState("/frontend\n /src\n /components\n/backend\n /routes\n /controllers\n");
10
+ const [out, setOut] = useState<any>(null);
11
+ const { model, provider } = useAi();
12
+ const [loading, setLoading] = useState(false);
13
+ const [space, setSpace] = useState("");
14
+ const [commitTitle, setCommitTitle] = useState("OmniDev: Apply changes");
15
+
16
+ const run = async () => {
17
+ setLoading(true);
18
+ setOut(null);
19
+ try {
20
+ const res = await fetch('/api/augment', {
21
+ method: 'POST',
22
+ headers: { 'Content-Type': 'application/json' },
23
+ body: JSON.stringify({ context, instruction, language: 'javascript', framework: 'express-react', response_type: 'file_updates', model, provider }),
24
+ });
25
+ const data = await res.json();
26
+ setOut(data);
27
+ } finally {
28
+ setLoading(false);
29
+ }
30
+ };
31
+
32
+ const apply = async () => {
33
+ if (!out?.files || !Array.isArray(out.files) || out.files.length === 0) return;
34
+ if (!space.includes('/')) {
35
+ alert('Specify target space as namespace/repoId');
36
+ return;
37
+ }
38
+ const [namespace, repoId] = space.split('/');
39
+ setLoading(true);
40
+ try {
41
+ const res = await fetch(`/api/me/projects/${namespace}/${repoId}/apply`, {
42
+ method: 'POST',
43
+ headers: { 'Content-Type': 'application/json' },
44
+ body: JSON.stringify({ files: out.files, commitTitle }),
45
+ });
46
+ const data = await res.json();
47
+ setOut((prev: any) => ({ ...prev, apply: data }));
48
+ } finally {
49
+ setLoading(false);
50
+ }
51
+ }
52
+
53
+ return (
54
+ <div className="p-4 border border-neutral-800 bg-neutral-900 rounded-xl text-neutral-200">
55
+ <h2 className="text-xl font-semibold mb-2">Omni Console</h2>
56
+ <p className="text-sm text-neutral-400 mb-4">Type engineering instructions; the engine returns structured file updates.</p>
57
+ <div className="grid gap-2">
58
+ <label className="text-sm">Project Context</label>
59
+ <textarea className="min-h-24 bg-neutral-800 rounded p-2 text-sm" value={context} onChange={(e) => setContext(e.target.value)} />
60
+ <label className="text-sm">Instruction</label>
61
+ <textarea className="min-h-24 bg-neutral-800 rounded p-2 text-sm" placeholder="e.g., Add JWT auth with /auth/login route" value={instruction} onChange={(e) => setInstruction(e.target.value)} />
62
+ <label className="text-sm">Target Space (namespace/repoId)</label>
63
+ <input className="bg-neutral-800 rounded p-2 text-sm" placeholder="yourname/your-space" value={space} onChange={(e) => setSpace(e.target.value)} />
64
+ <label className="text-sm">Commit Title</label>
65
+ <input className="bg-neutral-800 rounded p-2 text-sm" value={commitTitle} onChange={(e) => setCommitTitle(e.target.value)} />
66
+ </div>
67
+ <div className="flex items-center gap-2 mt-3">
68
+ <Button size="sm" onClick={run} disabled={loading || !instruction.trim()}>Run</Button>
69
+ <Button size="sm" variant="outline" onClick={apply} disabled={loading || !out?.files?.length}>Apply Changes</Button>
70
+ </div>
71
+ {out && (
72
+ <pre className="mt-4 text-xs bg-neutral-950 p-3 rounded overflow-x-auto max-h-80">{JSON.stringify(out, null, 2)}</pre>
73
+ )}
74
+ </div>
75
+ );
76
+ }
lib/seo.ts CHANGED
@@ -10,14 +10,14 @@ interface SEOParams {
10
  }
11
 
12
  export function generateSEO({
13
- title = "DeepSite | Build with AI ✨",
14
- description = "DeepSite is a web development tool that helps you build websites with AI, no code required. Let's deploy your website with DeepSite and enjoy the magic of AI.",
15
  path = "",
16
  image = "/banner.png",
17
  noIndex = false,
18
  canonical,
19
  }: SEOParams = {}): Metadata {
20
- const baseUrl = process.env.PUBLIC_BASE_URL || "https://deepsite.hf.co";
21
  const fullUrl = `${baseUrl}${path}`;
22
  const canonicalUrl = canonical || fullUrl;
23
 
@@ -54,13 +54,13 @@ export function generateSEO({
54
  title,
55
  description,
56
  url: canonicalUrl,
57
- siteName: "DeepSite",
58
  images: [
59
  {
60
  url: `${baseUrl}${image}`,
61
  width: 1200,
62
  height: 630,
63
- alt: `${title} - DeepSite`,
64
  },
65
  ],
66
  locale: "en_US",
@@ -71,14 +71,11 @@ export function generateSEO({
71
  title,
72
  description,
73
  images: [`${baseUrl}${image}`],
74
- creator: "@deepsite",
75
  },
76
  other: {
77
- // Prevent iframe embedding from unauthorized domains
78
  'X-Frame-Options': 'SAMEORIGIN',
79
- // Control how the page appears when shared
80
  'og:image:secure_url': `${baseUrl}${image}`,
81
- // Help search engines understand the primary URL
82
  'rel': 'canonical',
83
  },
84
  };
@@ -94,9 +91,9 @@ export function generateStructuredData(type: 'WebApplication' | 'Organization' |
94
  case 'WebApplication':
95
  return {
96
  ...baseStructuredData,
97
- name: 'DeepSite',
98
- description: 'Build websites with AI, no code required',
99
- url: process.env.PUBLIC_BASE_URL || 'https://deepsite.hf.co',
100
  applicationCategory: 'DeveloperApplication',
101
  operatingSystem: 'Web',
102
  offers: {
@@ -106,8 +103,8 @@ export function generateStructuredData(type: 'WebApplication' | 'Organization' |
106
  },
107
  creator: {
108
  '@type': 'Organization',
109
- name: 'DeepSite',
110
- url: process.env.PUBLIC_BASE_URL || 'https://deepsite.hf.co',
111
  },
112
  ...data,
113
  };
@@ -115,12 +112,11 @@ export function generateStructuredData(type: 'WebApplication' | 'Organization' |
115
  case 'Organization':
116
  return {
117
  ...baseStructuredData,
118
- name: 'DeepSite',
119
- url: process.env.PUBLIC_BASE_URL || 'https://deepsite.hf.co',
120
- logo: `${process.env.PUBLIC_BASE_URL || 'https://deepsite.hf.co'}/logo.svg`,
121
  description: 'AI-powered web development platform',
122
  sameAs: [
123
- // Add social media links here if available
124
  ],
125
  ...data,
126
  };
@@ -129,3 +125,4 @@ export function generateStructuredData(type: 'WebApplication' | 'Organization' |
129
  return { ...baseStructuredData, ...data };
130
  }
131
  }
 
 
10
  }
11
 
12
  export function generateSEO({
13
+ title = "OmniDev | AI Project Builder",
14
+ description = "OmniDev is an AI engineering environment that builds complete web projects (frontend + backend) from natural language.",
15
  path = "",
16
  image = "/banner.png",
17
  noIndex = false,
18
  canonical,
19
  }: SEOParams = {}): Metadata {
20
+ const baseUrl = process.env.PUBLIC_BASE_URL || "https://omnidev.hf.co";
21
  const fullUrl = `${baseUrl}${path}`;
22
  const canonicalUrl = canonical || fullUrl;
23
 
 
54
  title,
55
  description,
56
  url: canonicalUrl,
57
+ siteName: "OmniDev",
58
  images: [
59
  {
60
  url: `${baseUrl}${image}`,
61
  width: 1200,
62
  height: 630,
63
+ alt: `${title} - OmniDev`,
64
  },
65
  ],
66
  locale: "en_US",
 
71
  title,
72
  description,
73
  images: [`${baseUrl}${image}`],
74
+ creator: "@omnidev",
75
  },
76
  other: {
 
77
  'X-Frame-Options': 'SAMEORIGIN',
 
78
  'og:image:secure_url': `${baseUrl}${image}`,
 
79
  'rel': 'canonical',
80
  },
81
  };
 
91
  case 'WebApplication':
92
  return {
93
  ...baseStructuredData,
94
+ name: 'OmniDev',
95
+ description: 'AI-powered full-stack project builder',
96
+ url: process.env.PUBLIC_BASE_URL || 'https://omnidev.hf.co',
97
  applicationCategory: 'DeveloperApplication',
98
  operatingSystem: 'Web',
99
  offers: {
 
103
  },
104
  creator: {
105
  '@type': 'Organization',
106
+ name: 'OmniDev',
107
+ url: process.env.PUBLIC_BASE_URL || 'https://omnidev.hf.co',
108
  },
109
  ...data,
110
  };
 
112
  case 'Organization':
113
  return {
114
  ...baseStructuredData,
115
+ name: 'OmniDev',
116
+ url: process.env.PUBLIC_BASE_URL || 'https://omnidev.hf.co',
117
+ logo: `${process.env.PUBLIC_BASE_URL || 'https://omnidev.hf.co'}/logo.svg`,
118
  description: 'AI-powered web development platform',
119
  sameAs: [
 
120
  ],
121
  ...data,
122
  };
 
125
  return { ...baseStructuredData, ...data };
126
  }
127
  }
128
+
types/index.ts CHANGED
@@ -55,3 +55,31 @@ export interface EnhancedSettings {
55
  }
56
 
57
  export type Theme = "light" | "dark" | undefined;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  }
56
 
57
  export type Theme = "light" | "dark" | undefined;
58
+
59
+ // OmniDev Augmentation types
60
+ export type FileAction = "add" | "update" | "delete";
61
+
62
+ export interface FileUpdate {
63
+ path: string; // e.g., /frontend/src/App.tsx or /backend/routes/auth.ts
64
+ action: FileAction;
65
+ content?: string; // required for add/update; omit for delete
66
+ note?: string; // optional human-readable note
67
+ }
68
+
69
+ export interface AugmentRequest {
70
+ context: string; // project structure + important file snippets
71
+ instruction: string; // user instruction (e.g., add JWT auth)
72
+ language?: string; // javascript, typescript, python
73
+ framework?: string; // express-react, nextjs, nestjs, etc.
74
+ response_type?: "file_updates" | "explanation";
75
+ model?: string;
76
+ provider?: string; // 'google' to force Gemini
77
+ }
78
+
79
+ export interface AugmentResponse {
80
+ ok: boolean;
81
+ files?: FileUpdate[];
82
+ logs?: string[];
83
+ summary?: string;
84
+ message?: string;
85
+ }