oomi-ai 0.2.48 → 0.2.50
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 +186 -98
- package/agent_instructions.md +49 -10
- package/bin/oomi-ai.js +22 -22
- package/lib/personaRuntimeProcess.js +21 -8
- package/openclaw.plugin.json +1 -1
- package/package.json +15 -10
- package/templates/persona-app/README.md +30 -7
- package/templates/persona-app/src/App.css +210 -492
- package/templates/persona-app/src/App.tsx +4 -1
- package/templates/persona-app/src/index.css +24 -3
- package/templates/persona-app/src/pages/HomePage.tsx +67 -94
- package/templates/persona-app/src/pages/ScenePage.tsx +155 -2
- package/templates/persona-app/src/persona/notes.ts +5 -2
- package/templates/persona-app/src/spatial.ts +1 -1
|
@@ -2,11 +2,14 @@ import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
|
|
|
2
2
|
import { HomePage } from "./pages/HomePage";
|
|
3
3
|
import { ScenePage } from "./pages/ScenePage";
|
|
4
4
|
|
|
5
|
+
const isSpatialRuntime = __XR_ENV_BASE__.startsWith("/webspatial/avp");
|
|
6
|
+
|
|
5
7
|
export default function App() {
|
|
6
8
|
return (
|
|
7
9
|
<Router basename={__XR_ENV_BASE__}>
|
|
8
10
|
<Routes>
|
|
9
|
-
<Route index element={<HomePage />} />
|
|
11
|
+
<Route index element={isSpatialRuntime ? <ScenePage /> : <HomePage />} />
|
|
12
|
+
<Route path="home" element={<HomePage />} />
|
|
10
13
|
<Route path="scene" element={<ScenePage />} />
|
|
11
14
|
</Routes>
|
|
12
15
|
</Router>
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
font-family: "Segoe UI", system-ui, sans-serif;
|
|
3
3
|
line-height: 1.5;
|
|
4
4
|
font-weight: 400;
|
|
5
|
-
color: #
|
|
5
|
+
color: #1f1b16;
|
|
6
6
|
background:
|
|
7
|
-
radial-gradient(circle at top, rgba(
|
|
8
|
-
linear-gradient(180deg, #
|
|
7
|
+
radial-gradient(circle at top left, rgba(222, 207, 180, 0.5), transparent 34%),
|
|
8
|
+
linear-gradient(180deg, #faf7f1 0%, #efe7db 100%);
|
|
9
9
|
font-synthesis: none;
|
|
10
10
|
text-rendering: optimizeLegibility;
|
|
11
11
|
-webkit-font-smoothing: antialiased;
|
|
@@ -16,17 +16,38 @@
|
|
|
16
16
|
box-sizing: border-box;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
html,
|
|
20
|
+
body,
|
|
21
|
+
#root {
|
|
22
|
+
min-height: 100%;
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
body {
|
|
20
26
|
margin: 0;
|
|
21
27
|
min-width: 320px;
|
|
22
28
|
min-height: 100vh;
|
|
29
|
+
background:
|
|
30
|
+
radial-gradient(circle at top left, rgba(255, 255, 255, 0.7), transparent 28%),
|
|
31
|
+
radial-gradient(circle at top right, rgba(228, 216, 196, 0.62), transparent 26%),
|
|
32
|
+
linear-gradient(180deg, #faf7f1 0%, #eee5d8 100%);
|
|
23
33
|
}
|
|
24
34
|
|
|
25
35
|
button,
|
|
26
36
|
a {
|
|
27
37
|
font: inherit;
|
|
38
|
+
color: inherit;
|
|
39
|
+
text-decoration: none;
|
|
28
40
|
}
|
|
29
41
|
|
|
30
42
|
button {
|
|
31
43
|
cursor: pointer;
|
|
32
44
|
}
|
|
45
|
+
|
|
46
|
+
html.is-spatial,
|
|
47
|
+
html.is-spatial body {
|
|
48
|
+
background: transparent;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
html.is-spatial #root {
|
|
52
|
+
min-height: 100vh;
|
|
53
|
+
}
|
|
@@ -1,151 +1,124 @@
|
|
|
1
|
-
import { useEffect } from "react";
|
|
2
1
|
import { Link } from "react-router-dom";
|
|
3
2
|
import "../App.css";
|
|
4
3
|
import { personaConfig } from "../persona/config";
|
|
5
4
|
import { personaNotes } from "../persona/notes";
|
|
6
5
|
import {
|
|
7
6
|
WEBSPATIAL_FORK_COMMIT,
|
|
8
|
-
configurePersonaScene,
|
|
9
7
|
detectSpatialEnvironment,
|
|
10
8
|
openPersonaScene,
|
|
11
|
-
xrStyle,
|
|
12
9
|
} from "../spatial";
|
|
13
10
|
|
|
14
|
-
const
|
|
11
|
+
const browserCards = [
|
|
15
12
|
{
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
body: "Use the persona site as a real operator board, planner, studio, or dashboard instead of another chat clone.",
|
|
19
|
-
depth: 34,
|
|
20
|
-
material: "translucent",
|
|
13
|
+
title: "Browser route stays valid",
|
|
14
|
+
body: "Keep a normal web route for previews, fallback browsers, and non-spatial editing.",
|
|
21
15
|
},
|
|
22
16
|
{
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
body: "Key controls should declare their own depth and material so AndroidXR reads them as layered surfaces.",
|
|
26
|
-
depth: 84,
|
|
27
|
-
material: "thin",
|
|
17
|
+
title: "XR route stays separate",
|
|
18
|
+
body: "The mounted scene component should own scene bootstrap, diagnostics, and the real spatial surfaces.",
|
|
28
19
|
},
|
|
29
20
|
{
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
body: "Let Oomi keep the live conversation thread while the persona page stays focused on structured work.",
|
|
33
|
-
depth: 140,
|
|
34
|
-
material: "thick",
|
|
21
|
+
title: "Oomi opens the entry URL",
|
|
22
|
+
body: "When the runtime is spatial, the entry route should land directly in the scene instead of a flat homepage.",
|
|
35
23
|
},
|
|
36
24
|
];
|
|
37
25
|
|
|
38
|
-
|
|
39
|
-
sceneMode?: boolean;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export function HomePage({ sceneMode = false }: HomePageProps) {
|
|
26
|
+
export function HomePage() {
|
|
43
27
|
const environment = detectSpatialEnvironment();
|
|
44
28
|
|
|
45
|
-
useEffect(() => {
|
|
46
|
-
configurePersonaScene();
|
|
47
|
-
}, []);
|
|
48
|
-
|
|
49
29
|
return (
|
|
50
|
-
<main className=
|
|
51
|
-
<section className="
|
|
52
|
-
<
|
|
30
|
+
<main className="persona-home">
|
|
31
|
+
<section className="home-grid">
|
|
32
|
+
<article className="home-panel home-hero">
|
|
53
33
|
<div>
|
|
54
|
-
<p className="
|
|
55
|
-
<h1 className="
|
|
56
|
-
<p className="
|
|
34
|
+
<p className="home-eyebrow">Oomi Persona Surface</p>
|
|
35
|
+
<h1 className="home-title">{personaConfig.name}</h1>
|
|
36
|
+
<p className="home-description">{personaConfig.description}</p>
|
|
37
|
+
<p className="home-supporting-copy">
|
|
38
|
+
This browser route exists for non-spatial parity. In XR mode, the index route should
|
|
39
|
+
open directly into the mounted scene so Oomi lands on a real WebSpatial workspace
|
|
40
|
+
instead of another 2D page.
|
|
41
|
+
</p>
|
|
57
42
|
</div>
|
|
58
43
|
|
|
59
|
-
<div className="
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
Open Spatial Preview
|
|
67
|
-
</Link>
|
|
68
|
-
</>
|
|
69
|
-
) : (
|
|
70
|
-
<Link className="persona-button" to="/">
|
|
71
|
-
Return To Browser View
|
|
72
|
-
</Link>
|
|
73
|
-
)}
|
|
44
|
+
<div className="home-actions">
|
|
45
|
+
<button className="home-primary-button" onClick={openPersonaScene}>
|
|
46
|
+
Launch XR Workspace
|
|
47
|
+
</button>
|
|
48
|
+
<Link className="home-secondary-link" to="/scene" target="_blank" rel="noreferrer">
|
|
49
|
+
Open Scene Route
|
|
50
|
+
</Link>
|
|
74
51
|
</div>
|
|
75
52
|
|
|
76
|
-
<div className="
|
|
77
|
-
{
|
|
78
|
-
<article
|
|
79
|
-
|
|
80
|
-
className="persona-preview-card"
|
|
81
|
-
data-tone={card.tone}
|
|
82
|
-
enable-xr
|
|
83
|
-
style={xrStyle(card.depth, card.material)}
|
|
84
|
-
>
|
|
85
|
-
<h3>{card.title}</h3>
|
|
53
|
+
<div className="home-card-grid">
|
|
54
|
+
{browserCards.map(card => (
|
|
55
|
+
<article key={card.title} className="home-panel home-feature-card">
|
|
56
|
+
<h2>{card.title}</h2>
|
|
86
57
|
<p>{card.body}</p>
|
|
87
58
|
</article>
|
|
88
59
|
))}
|
|
89
60
|
</div>
|
|
90
|
-
</
|
|
61
|
+
</article>
|
|
91
62
|
|
|
92
|
-
<aside className="
|
|
93
|
-
<p className="
|
|
94
|
-
<div className="runtime-list">
|
|
95
|
-
<div className="runtime-row">
|
|
96
|
-
<span
|
|
97
|
-
<span
|
|
63
|
+
<aside className="home-panel home-runtime">
|
|
64
|
+
<p className="home-eyebrow">Runtime Snapshot</p>
|
|
65
|
+
<div className="home-runtime-list">
|
|
66
|
+
<div className="home-runtime-row">
|
|
67
|
+
<span>Slug</span>
|
|
68
|
+
<span>{personaConfig.slug}</span>
|
|
69
|
+
</div>
|
|
70
|
+
<div className="home-runtime-row">
|
|
71
|
+
<span>SDK</span>
|
|
72
|
+
<span>@webspatial/react-sdk {environment.sdkVersion}</span>
|
|
98
73
|
</div>
|
|
99
|
-
<div className="runtime-row">
|
|
100
|
-
<span
|
|
101
|
-
<span
|
|
74
|
+
<div className="home-runtime-row">
|
|
75
|
+
<span>Bridge</span>
|
|
76
|
+
<span>{environment.hasBridge ? "available" : "waiting"}</span>
|
|
102
77
|
</div>
|
|
103
|
-
<div className="runtime-row">
|
|
104
|
-
<span
|
|
105
|
-
<span
|
|
78
|
+
<div className="home-runtime-row">
|
|
79
|
+
<span>Native</span>
|
|
80
|
+
<span>{environment.nativeVersion ?? "browser fallback"}</span>
|
|
106
81
|
</div>
|
|
107
|
-
<div className="runtime-row">
|
|
108
|
-
<span
|
|
109
|
-
<span
|
|
82
|
+
<div className="home-runtime-row">
|
|
83
|
+
<span>Fork</span>
|
|
84
|
+
<span>{WEBSPATIAL_FORK_COMMIT.slice(0, 7)}</span>
|
|
110
85
|
</div>
|
|
111
|
-
<div className="runtime-row">
|
|
112
|
-
<span
|
|
113
|
-
<span
|
|
86
|
+
<div className="home-runtime-row">
|
|
87
|
+
<span>Spatial mode</span>
|
|
88
|
+
<span>{environment.isWebSpatial ? "active" : "not active"}</span>
|
|
114
89
|
</div>
|
|
115
90
|
</div>
|
|
116
91
|
</aside>
|
|
117
92
|
</section>
|
|
118
93
|
|
|
119
|
-
<section className="
|
|
120
|
-
<article className="
|
|
121
|
-
<h2>
|
|
94
|
+
<section className="home-card-grid">
|
|
95
|
+
<article className="home-panel home-copy-card">
|
|
96
|
+
<h2>Template Contract</h2>
|
|
122
97
|
<p>
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
top instead of swapping in a disconnected demo board.
|
|
98
|
+
XR mode should default into <code>ScenePage</code>, mount scene bootstrap from that
|
|
99
|
+
component, and render multiple authored panels with explicit depth and material.
|
|
126
100
|
</p>
|
|
127
101
|
<p>
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
: "This home route exists for browser parity and one-click launch into the spatial work surface."}
|
|
102
|
+
The scaffold should teach agents to build surfaces, not just pages wrapped in one
|
|
103
|
+
captured DOM card.
|
|
131
104
|
</p>
|
|
132
105
|
</article>
|
|
133
106
|
|
|
134
|
-
<article className="
|
|
107
|
+
<article className="home-panel home-copy-card">
|
|
135
108
|
<h2>Editing Notes</h2>
|
|
136
|
-
<ul className="
|
|
109
|
+
<ul className="home-note-list">
|
|
137
110
|
{personaNotes.map(note => (
|
|
138
111
|
<li key={note}>{note}</li>
|
|
139
112
|
))}
|
|
140
113
|
</ul>
|
|
141
114
|
</article>
|
|
142
115
|
|
|
143
|
-
<article className="
|
|
144
|
-
<h2>
|
|
116
|
+
<article className="home-panel home-copy-card">
|
|
117
|
+
<h2>Spatial Defaults</h2>
|
|
145
118
|
<p>
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
119
|
+
Preserve the transparent spatial shell, the vendored WebSpatial fork, and the DOM
|
|
120
|
+
capture hooks in <code>main.tsx</code>. Those pieces are part of the runtime contract,
|
|
121
|
+
not optional polish.
|
|
149
122
|
</p>
|
|
150
123
|
</article>
|
|
151
124
|
</section>
|
|
@@ -1,5 +1,158 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { Link } from "react-router-dom";
|
|
3
|
+
import "../App.css";
|
|
4
|
+
import { personaConfig } from "../persona/config";
|
|
5
|
+
import { personaNotes } from "../persona/notes";
|
|
6
|
+
import {
|
|
7
|
+
WEBSPATIAL_FORK_COMMIT,
|
|
8
|
+
configurePersonaScene,
|
|
9
|
+
detectSpatialEnvironment,
|
|
10
|
+
xrStyle,
|
|
11
|
+
} from "../spatial";
|
|
12
|
+
|
|
13
|
+
const spatialWorkflows = [
|
|
14
|
+
{
|
|
15
|
+
title: "Primary workspace panel",
|
|
16
|
+
body: "Use this as the main operator surface for the persona's task instead of duplicating a generic homepage.",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
title: "Secondary workspace panel",
|
|
20
|
+
body: "Reserve a separate surface for supporting context, previews, or step-by-step controls so the XR scene reads as a composed environment.",
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const sceneChips = [
|
|
25
|
+
"header surface",
|
|
26
|
+
"workspace panel",
|
|
27
|
+
"status panel",
|
|
28
|
+
"completion panel",
|
|
29
|
+
"tool tray",
|
|
30
|
+
];
|
|
2
31
|
|
|
3
32
|
export function ScenePage() {
|
|
4
|
-
|
|
33
|
+
const environment = detectSpatialEnvironment();
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
configurePersonaScene();
|
|
37
|
+
|
|
38
|
+
const snapshot = detectSpatialEnvironment();
|
|
39
|
+
console.info("[persona] spatial runtime", snapshot);
|
|
40
|
+
}, []);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<main className="scene-shell">
|
|
44
|
+
<header className="scene-header" enable-xr style={xrStyle(14, "thin")}>
|
|
45
|
+
<div>
|
|
46
|
+
<p className="scene-eyebrow">Spatial Scene</p>
|
|
47
|
+
<h1 className="scene-title">{personaConfig.name}</h1>
|
|
48
|
+
<p className="scene-copy">
|
|
49
|
+
{personaConfig.description} This route is the default experience when Oomi opens the
|
|
50
|
+
app inside the WebSpatial runtime, so it should behave like a real XR workspace instead
|
|
51
|
+
of a browser homepage.
|
|
52
|
+
</p>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div className="scene-badge-stack">
|
|
56
|
+
<span className="scene-badge">{environment.isWebSpatial ? "WebSpatial active" : "Browser preview"}</span>
|
|
57
|
+
<span className="scene-badge">SDK {environment.sdkVersion}</span>
|
|
58
|
+
<span className="scene-badge">Fork {WEBSPATIAL_FORK_COMMIT.slice(0, 7)}</span>
|
|
59
|
+
</div>
|
|
60
|
+
</header>
|
|
61
|
+
|
|
62
|
+
<section className="scene-workspace-grid">
|
|
63
|
+
<article className="scene-surface scene-workspace-primary" enable-xr style={xrStyle(40, "thick")}>
|
|
64
|
+
<p className="scene-surface-label">Primary Workspace</p>
|
|
65
|
+
<h2 className="scene-surface-title">Make spatial panels the default, not the afterthought</h2>
|
|
66
|
+
<p className="scene-copy">
|
|
67
|
+
The mounted scene component is where the persona should bootstrap the scene, verify the
|
|
68
|
+
runtime, and lay out the main work surfaces. If the scene route only wraps a normal
|
|
69
|
+
home page, the app will still feel flat in-headset even when the SDK is bundled.
|
|
70
|
+
</p>
|
|
71
|
+
|
|
72
|
+
<div className="scene-step-grid">
|
|
73
|
+
{spatialWorkflows.map(workflow => (
|
|
74
|
+
<article key={workflow.title} className="scene-step-card">
|
|
75
|
+
<h3>{workflow.title}</h3>
|
|
76
|
+
<p>{workflow.body}</p>
|
|
77
|
+
</article>
|
|
78
|
+
))}
|
|
79
|
+
</div>
|
|
80
|
+
</article>
|
|
81
|
+
|
|
82
|
+
<div className="scene-secondary-stack">
|
|
83
|
+
<aside className="scene-surface" enable-xr style={xrStyle(24, "thin")}>
|
|
84
|
+
<p className="scene-surface-label">Runtime Diagnostics</p>
|
|
85
|
+
<div className="scene-status-grid">
|
|
86
|
+
<div className="scene-status-row">
|
|
87
|
+
<span>isWebSpatial</span>
|
|
88
|
+
<span>{environment.isWebSpatial ? "true" : "false"}</span>
|
|
89
|
+
</div>
|
|
90
|
+
<div className="scene-status-row">
|
|
91
|
+
<span>hasBridge</span>
|
|
92
|
+
<span>{environment.hasBridge ? "true" : "false"}</span>
|
|
93
|
+
</div>
|
|
94
|
+
<div className="scene-status-row">
|
|
95
|
+
<span>hasWebSpatialData</span>
|
|
96
|
+
<span>{environment.hasWebSpatialData ? "true" : "false"}</span>
|
|
97
|
+
</div>
|
|
98
|
+
<div className="scene-status-row">
|
|
99
|
+
<span>nativeVersion</span>
|
|
100
|
+
<span>{environment.nativeVersion ?? "browser fallback"}</span>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
</aside>
|
|
104
|
+
|
|
105
|
+
<aside className="scene-surface" enable-xr style={xrStyle(18, "thin")}>
|
|
106
|
+
<p className="scene-surface-label">Tool Tray</p>
|
|
107
|
+
<div className="scene-chip-row">
|
|
108
|
+
{sceneChips.map(chip => (
|
|
109
|
+
<span key={chip} className="scene-tool-chip">
|
|
110
|
+
{chip}
|
|
111
|
+
</span>
|
|
112
|
+
))}
|
|
113
|
+
</div>
|
|
114
|
+
</aside>
|
|
115
|
+
</div>
|
|
116
|
+
</section>
|
|
117
|
+
|
|
118
|
+
<section className="scene-support-grid">
|
|
119
|
+
<article className="scene-surface" enable-xr style={xrStyle(18, "thin")}>
|
|
120
|
+
<p className="scene-surface-label">Spatial Surface Checklist</p>
|
|
121
|
+
<h2 className="scene-surface-title">At least three authored panels</h2>
|
|
122
|
+
<p className="scene-copy">
|
|
123
|
+
Add <code>enable-xr</code> and <code>xrStyle()</code> to meaningful panels like the
|
|
124
|
+
scene header, the primary workspace, and supporting status or completion surfaces.
|
|
125
|
+
</p>
|
|
126
|
+
</article>
|
|
127
|
+
|
|
128
|
+
<article className="scene-surface" enable-xr style={xrStyle(28, "regular")}>
|
|
129
|
+
<p className="scene-surface-label">Completion Panel</p>
|
|
130
|
+
<h2 className="scene-surface-title">Keep the host transparent</h2>
|
|
131
|
+
<p className="scene-copy">
|
|
132
|
+
The XR shell should recede behind the panels. Use <code>html.is-spatial</code> styles
|
|
133
|
+
so the host background stays transparent while the authored panels carry material.
|
|
134
|
+
</p>
|
|
135
|
+
</article>
|
|
136
|
+
|
|
137
|
+
<article className="scene-surface" enable-xr style={xrStyle(22, "thin")}>
|
|
138
|
+
<p className="scene-surface-label">Agent Notes</p>
|
|
139
|
+
<ul className="scene-note-list">
|
|
140
|
+
{personaNotes.map(note => (
|
|
141
|
+
<li key={note}>{note}</li>
|
|
142
|
+
))}
|
|
143
|
+
</ul>
|
|
144
|
+
</article>
|
|
145
|
+
</section>
|
|
146
|
+
|
|
147
|
+
<footer className="scene-footer">
|
|
148
|
+
<p className="scene-footer-copy">
|
|
149
|
+
Browser editing still lives at the non-spatial route while XR mode defaults directly into
|
|
150
|
+
the scene.
|
|
151
|
+
</p>
|
|
152
|
+
<Link className="scene-link" to="/home">
|
|
153
|
+
Open browser route
|
|
154
|
+
</Link>
|
|
155
|
+
</footer>
|
|
156
|
+
</main>
|
|
157
|
+
);
|
|
5
158
|
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
export const personaNotes = [
|
|
2
2
|
"Keep persona-specific logic inside src/persona/ unless Oomi explicitly instructs otherwise.",
|
|
3
|
-
"Preserve the WebSpatial router basename, the
|
|
3
|
+
"Preserve the WebSpatial router basename, and default the index route to ScenePage whenever the runtime is running under the XR base path.",
|
|
4
|
+
"Call configurePersonaScene() from the mounted scene component and log detectSpatialEnvironment() when the scene boots.",
|
|
4
5
|
"Keep using the vendored AndroidXR-enabled WebSpatial fork instead of switching back to the stock npm packages.",
|
|
5
|
-
"Author
|
|
6
|
+
"Author multiple meaningful surfaces with explicit enable-xr, --xr-back, and --xr-background-material values so the app reads as spatial instead of one captured webpage.",
|
|
7
|
+
"Keep html.is-spatial shell styles transparent so the host recedes and the panels carry the visual material.",
|
|
8
|
+
"Keep the managed runtime compatible with `oomi personas launch-managed`; do not require manual npm run dev for Oomi to consider the persona live.",
|
|
6
9
|
"Do not remove public/oomi.runtime.json, public/oomi.health.json, or public/manifest.webmanifest.",
|
|
7
10
|
];
|