oomi-ai 0.2.39 → 0.2.41
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 +5 -3
- package/lib/personaRuntimeManager.js +21 -17
- package/lib/personaRuntimeProcess.js +417 -49
- package/lib/scaffold.js +14 -14
- package/openclaw.plugin.json +1 -1
- package/package.json +10 -8
- package/templates/persona-app/package.json +6 -4
- package/templates/persona-app/src/App.css +562 -81
- package/templates/persona-app/src/App.tsx +2 -2
- package/templates/persona-app/src/main.tsx +13 -0
- package/templates/persona-app/src/pages/HomePage.tsx +117 -36
- package/templates/persona-app/src/pages/ScenePage.tsx +2 -15
- package/templates/persona-app/src/persona/notes.ts +3 -1
- package/templates/persona-app/src/spatial.ts +82 -0
- package/templates/persona-app/template.json +1 -1
- package/templates/persona-app/vendor/webspatial/FORK.md +6 -0
- package/templates/persona-app/vendor/webspatial/core-sdk/LICENSE +21 -0
- package/templates/persona-app/vendor/webspatial/core-sdk/dist/iife/index.d.ts +906 -0
- package/templates/persona-app/vendor/webspatial/core-sdk/dist/iife/index.global.js +75 -0
- package/templates/persona-app/vendor/webspatial/core-sdk/dist/iife/index.global.js.map +1 -0
- package/templates/persona-app/vendor/webspatial/core-sdk/dist/index.d.ts +906 -0
- package/templates/persona-app/vendor/webspatial/core-sdk/dist/index.js +3131 -0
- package/templates/persona-app/vendor/webspatial/core-sdk/dist/index.js.map +1 -0
- package/templates/persona-app/vendor/webspatial/core-sdk/package.json +45 -0
- package/templates/persona-app/vendor/webspatial/react-sdk/LICENSE +21 -0
- package/templates/persona-app/vendor/webspatial/react-sdk/dist/default/index.d.ts +365 -0
- package/templates/persona-app/vendor/webspatial/react-sdk/dist/default/index.js +4167 -0
- package/templates/persona-app/vendor/webspatial/react-sdk/dist/default/index.js.map +1 -0
- package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-dev-runtime.d.ts +82 -0
- package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-dev-runtime.js +66 -0
- package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-dev-runtime.js.map +1 -0
- package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-dev-runtime.web.d.ts +2 -0
- package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-dev-runtime.web.js +18 -0
- package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-dev-runtime.web.js.map +1 -0
- package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-runtime.d.ts +5 -0
- package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-runtime.js +66 -0
- package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-runtime.js.map +1 -0
- package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-runtime.web.d.ts +1 -0
- package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-runtime.web.js +18 -0
- package/templates/persona-app/vendor/webspatial/react-sdk/dist/jsx/jsx-runtime.web.js.map +1 -0
- package/templates/persona-app/vendor/webspatial/react-sdk/dist/web/index.d.ts +365 -0
- package/templates/persona-app/vendor/webspatial/react-sdk/dist/web/index.js +4207 -0
- package/templates/persona-app/vendor/webspatial/react-sdk/dist/web/index.js.map +1 -0
- package/templates/persona-app/vendor/webspatial/react-sdk/package.json +94 -0
- package/templates/persona-app/vite.config.ts +13 -0
package/README.md
CHANGED
|
@@ -4,9 +4,11 @@ OpenClaw channel plugin and bridge tooling for Oomi managed chat and voice.
|
|
|
4
4
|
|
|
5
5
|
## Current Focus
|
|
6
6
|
|
|
7
|
-
`0.2.
|
|
8
|
-
- WebSpatial-based persona scaffolding for generated Oomi apps
|
|
9
|
-
-
|
|
7
|
+
`0.2.40` keeps the persona automation lane, adds a stable local persona runtime manager, upgrades the Docker dev harness from a package simulator to a real OpenClaw runtime, and introduces a shared OpenClaw profile contract so local onboarding, Docker bootstrap, and future hosted agents use the same setup model:
|
|
8
|
+
- WebSpatial-based persona scaffolding for generated Oomi apps
|
|
9
|
+
- vendored AndroidXR WebSpatial fork sync for persona runtimes, including the npm-safe package metadata needed by OpenClaw installs
|
|
10
|
+
- managed persona runtime installs now detect vendored WebSpatial drift and reinstall automatically so existing workspaces pick up XR runtime fixes
|
|
11
|
+
- a high-level `oomi personas create-managed` command for agent-driven persona creation
|
|
10
12
|
- a stable `oomi personas launch-managed` flow for local persona hosting under `OPENCLAW_WORKSPACE/personas`
|
|
11
13
|
- a matching `oomi personas delete` flow that stops managed runtimes and removes the persona workspace from the OpenClaw machine
|
|
12
14
|
- shared OpenClaw path handling for isolated local or containerized dev roots
|
|
@@ -6,13 +6,15 @@ import { resolveOpenclawLegacyPersonasDir } from './openclawPaths.js';
|
|
|
6
6
|
import {
|
|
7
7
|
buildLocalPersonaRuntime,
|
|
8
8
|
defaultPersonaWorkspaceRoot,
|
|
9
|
-
installPersonaWorkspace,
|
|
10
|
-
isPersonaWorkspaceProcessRunning,
|
|
11
|
-
resolvePersonaDevCommand,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
installPersonaWorkspace,
|
|
10
|
+
isPersonaWorkspaceProcessRunning,
|
|
11
|
+
resolvePersonaDevCommand,
|
|
12
|
+
syncLegacyWebSpatialScaffoldFiles,
|
|
13
|
+
syncVendoredWebSpatialPackages,
|
|
14
|
+
startPersonaWorkspace,
|
|
15
|
+
stopPersonaWorkspace,
|
|
16
|
+
waitForPersonaRuntime,
|
|
17
|
+
} from './personaRuntimeProcess.js';
|
|
16
18
|
import {
|
|
17
19
|
readPersonaRuntimeState,
|
|
18
20
|
resolvePersonaRuntimeLogPath,
|
|
@@ -193,16 +195,18 @@ async function ensureWorkspaceScaffold({
|
|
|
193
195
|
};
|
|
194
196
|
}
|
|
195
197
|
|
|
196
|
-
async function ensureWorkspaceInstall({
|
|
197
|
-
workspacePath,
|
|
198
|
-
forceInstall = false,
|
|
199
|
-
}) {
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
198
|
+
async function ensureWorkspaceInstall({
|
|
199
|
+
workspacePath,
|
|
200
|
+
forceInstall = false,
|
|
201
|
+
}) {
|
|
202
|
+
const packageSyncChanged = syncVendoredWebSpatialPackages({ workspacePath });
|
|
203
|
+
syncLegacyWebSpatialScaffoldFiles({ workspacePath });
|
|
204
|
+
const nodeModulesPath = path.join(workspacePath, 'node_modules');
|
|
205
|
+
if (!forceInstall && fs.existsSync(nodeModulesPath) && !packageSyncChanged) {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
await installPersonaWorkspace({ workspacePath });
|
|
206
210
|
return true;
|
|
207
211
|
}
|
|
208
212
|
|
|
@@ -1,16 +1,180 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
3
|
import { spawn, spawnSync } from 'node:child_process';
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
import { resolveOpenclawPersonasDir } from './openclawPaths.js';
|
|
7
|
+
import {
|
|
8
|
+
DEFAULT_TEMPLATE_ID,
|
|
9
|
+
DEFAULT_TEMPLATE_VERSION,
|
|
10
|
+
renderTemplateFile,
|
|
11
|
+
resolveTemplateRoot,
|
|
12
|
+
} from './template.js';
|
|
13
|
+
|
|
14
|
+
const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
15
|
+
const PERSONA_TEMPLATE_ROOT = resolveTemplateRoot(
|
|
16
|
+
DEFAULT_TEMPLATE_ID,
|
|
17
|
+
DEFAULT_TEMPLATE_VERSION,
|
|
18
|
+
);
|
|
19
|
+
const PERSONA_TEMPLATE_PACKAGE_JSON_PATH = path.join(PERSONA_TEMPLATE_ROOT, 'package.json');
|
|
20
|
+
const PERSONA_TEMPLATE_PACKAGE_JSON = fs.existsSync(PERSONA_TEMPLATE_PACKAGE_JSON_PATH)
|
|
21
|
+
? JSON.parse(fs.readFileSync(PERSONA_TEMPLATE_PACKAGE_JSON_PATH, 'utf8'))
|
|
22
|
+
: {};
|
|
23
|
+
const WEBSPATIAL_VENDOR_ROOT = path.join(
|
|
24
|
+
PACKAGE_ROOT,
|
|
25
|
+
'templates',
|
|
26
|
+
'persona-app',
|
|
27
|
+
'vendor',
|
|
28
|
+
'webspatial',
|
|
29
|
+
);
|
|
30
|
+
const VENDORED_WEBSPATIAL_CORE_SPEC = 'file:./vendor/webspatial/core-sdk';
|
|
31
|
+
const VENDORED_WEBSPATIAL_REACT_SPEC = 'file:./vendor/webspatial/react-sdk';
|
|
32
|
+
const WEBSPATIAL_TEMPLATE_DEV_DEPENDENCIES = [
|
|
33
|
+
'@webspatial/builder',
|
|
34
|
+
'@webspatial/platform-visionos',
|
|
35
|
+
'@webspatial/vite-plugin',
|
|
36
|
+
];
|
|
37
|
+
const LEGACY_WEBSPATIAL_TEMPLATE_FILE_RULES = [
|
|
38
|
+
{
|
|
39
|
+
relativePath: path.join('src', 'spatial.ts'),
|
|
40
|
+
shouldReplace: (content) =>
|
|
41
|
+
!content.includes('WEBSPATIAL_FORK_REPOSITORY') ||
|
|
42
|
+
!content.includes('configurePersonaScene') ||
|
|
43
|
+
content.includes('WEBSPATIAL_FORK_COMMIT = "b2746721e4fe6b4f86dac0ea55938074eea00cda"') ||
|
|
44
|
+
content.includes('WEBSPATIAL_FORK_COMMIT = "8904ac8fec48fe49ee14d1739237bd1afb2894fe"'),
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
relativePath: path.join('src', 'main.tsx'),
|
|
48
|
+
shouldReplace: (content) =>
|
|
49
|
+
!content.includes('snapdom') &&
|
|
50
|
+
!content.includes('html2canvas') &&
|
|
51
|
+
content.includes('createRoot(document.getElementById("root")!)'),
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
relativePath: path.join('src', 'App.tsx'),
|
|
55
|
+
shouldReplace: (content) =>
|
|
56
|
+
content.includes('<Route path="/" element={<HomePage />} />') ||
|
|
57
|
+
content.includes('<Route path="/scene" element={<ScenePage />} />'),
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
relativePath: path.join('src', 'pages', 'HomePage.tsx'),
|
|
61
|
+
shouldReplace: (content) =>
|
|
62
|
+
(content.includes('Open Spatial Scene') &&
|
|
63
|
+
content.includes('Open Scene Route')) ||
|
|
64
|
+
(content.includes('Launch Spatial Surface') &&
|
|
65
|
+
content.includes('Open Browser Preview') &&
|
|
66
|
+
content.includes('Focused surface') &&
|
|
67
|
+
!content.includes('sceneMode')) ||
|
|
68
|
+
content.includes('enable-xr-monitor={sceneMode || undefined}') ||
|
|
69
|
+
content.includes('enable-xr={sceneMode || undefined}') ||
|
|
70
|
+
(
|
|
71
|
+
content.includes('persona-panel persona-runtime" enable-xr') ||
|
|
72
|
+
content.includes('persona-button" onClick={openPersonaScene} enable-xr') ||
|
|
73
|
+
content.includes('persona-link" to="/scene" target="_blank" enable-xr') ||
|
|
74
|
+
content.includes('persona-card" enable-xr style={xrStyle(')
|
|
75
|
+
),
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
relativePath: path.join('src', 'pages', 'ScenePage.tsx'),
|
|
79
|
+
shouldReplace: (content) =>
|
|
80
|
+
(
|
|
81
|
+
!content.includes('enable-xr-monitor') &&
|
|
82
|
+
content.includes(
|
|
83
|
+
'This route is intentionally separate so WebSpatial scene launching has a dedicated',
|
|
84
|
+
)
|
|
85
|
+
) ||
|
|
86
|
+
(
|
|
87
|
+
content.includes('Awaiting AndroidXR interaction') &&
|
|
88
|
+
content.includes('Interaction Console') &&
|
|
89
|
+
content.includes('Fork-backed proof points')
|
|
90
|
+
),
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
relativePath: path.join('src', 'App.css'),
|
|
94
|
+
shouldReplace: (content) =>
|
|
95
|
+
(content.includes('.scene-panel') && !content.includes('.scene-interaction-grid')) ||
|
|
96
|
+
content.includes('html.is-spatial .persona-runtime {') ||
|
|
97
|
+
content.includes('html.is-spatial .persona-scene-root {') ||
|
|
98
|
+
content.includes('html.is-spatial .persona-button,') ||
|
|
99
|
+
content.includes('html.is-spatial .persona-card,'),
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
relativePath: 'vite.config.ts',
|
|
103
|
+
shouldReplace: (content) =>
|
|
104
|
+
content.includes('webSpatial()') && !content.includes('optimizeDeps'),
|
|
105
|
+
},
|
|
106
|
+
];
|
|
6
107
|
|
|
7
108
|
function resolveNpmCommand() {
|
|
8
109
|
return process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
9
110
|
}
|
|
10
111
|
|
|
11
|
-
function ensureDir(dirPath) {
|
|
12
|
-
fs.mkdirSync(dirPath, { recursive: true });
|
|
13
|
-
}
|
|
112
|
+
function ensureDir(dirPath) {
|
|
113
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function readJsonFile(filePath) {
|
|
117
|
+
if (!fs.existsSync(filePath)) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
123
|
+
} catch {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function copyDirectory(sourcePath, targetPath) {
|
|
129
|
+
ensureDir(targetPath);
|
|
130
|
+
let changed = false;
|
|
131
|
+
const sourceEntries = fs.readdirSync(sourcePath, { withFileTypes: true });
|
|
132
|
+
const sourceEntryNames = new Set(sourceEntries.map((entry) => entry.name));
|
|
133
|
+
const existingTargetEntries = fs.readdirSync(targetPath, { withFileTypes: true });
|
|
134
|
+
|
|
135
|
+
for (const entry of existingTargetEntries) {
|
|
136
|
+
if (sourceEntryNames.has(entry.name)) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
fs.rmSync(path.join(targetPath, entry.name), { recursive: true, force: true });
|
|
140
|
+
changed = true;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
for (const entry of sourceEntries) {
|
|
144
|
+
const sourceEntryPath = path.join(sourcePath, entry.name);
|
|
145
|
+
const targetEntryPath = path.join(targetPath, entry.name);
|
|
146
|
+
const targetExists = fs.existsSync(targetEntryPath);
|
|
147
|
+
|
|
148
|
+
if (entry.isDirectory()) {
|
|
149
|
+
if (targetExists && !fs.statSync(targetEntryPath).isDirectory()) {
|
|
150
|
+
fs.rmSync(targetEntryPath, { recursive: true, force: true });
|
|
151
|
+
changed = true;
|
|
152
|
+
}
|
|
153
|
+
if (copyDirectory(sourceEntryPath, targetEntryPath)) {
|
|
154
|
+
changed = true;
|
|
155
|
+
}
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (targetExists && fs.statSync(targetEntryPath).isDirectory()) {
|
|
160
|
+
fs.rmSync(targetEntryPath, { recursive: true, force: true });
|
|
161
|
+
changed = true;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const sourceBuffer = fs.readFileSync(sourceEntryPath);
|
|
165
|
+
const targetBuffer = fs.existsSync(targetEntryPath)
|
|
166
|
+
? fs.readFileSync(targetEntryPath)
|
|
167
|
+
: null;
|
|
168
|
+
if (targetBuffer && sourceBuffer.equals(targetBuffer)) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
fs.copyFileSync(sourceEntryPath, targetEntryPath);
|
|
173
|
+
changed = true;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return changed;
|
|
177
|
+
}
|
|
14
178
|
|
|
15
179
|
function quoteWindowsCommandPart(value) {
|
|
16
180
|
const text = String(value);
|
|
@@ -43,6 +207,191 @@ function trimString(value) {
|
|
|
43
207
|
return typeof value === 'string' ? value.trim() : '';
|
|
44
208
|
}
|
|
45
209
|
|
|
210
|
+
function readPersonaConfigLiteral(source, key) {
|
|
211
|
+
if (!source) {
|
|
212
|
+
return '';
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const match = source.match(new RegExp(`${key}:\\s*"([^"]*)"`, 'u'));
|
|
216
|
+
return trimString(match?.[1] || '');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function resolvePersonaTemplateVariables(workspacePath) {
|
|
220
|
+
const personaConfigSource = fs.existsSync(path.join(workspacePath, 'src', 'persona', 'config.ts'))
|
|
221
|
+
? fs.readFileSync(path.join(workspacePath, 'src', 'persona', 'config.ts'), 'utf8')
|
|
222
|
+
: '';
|
|
223
|
+
const personaJson = readJsonFile(path.join(workspacePath, 'persona.json')) || {};
|
|
224
|
+
const runtimeConfig = readPersonaRuntimeConfig(workspacePath);
|
|
225
|
+
const slug =
|
|
226
|
+
readPersonaConfigLiteral(personaConfigSource, 'slug') ||
|
|
227
|
+
trimString(personaJson.id) ||
|
|
228
|
+
path.basename(path.resolve(workspacePath));
|
|
229
|
+
const name =
|
|
230
|
+
readPersonaConfigLiteral(personaConfigSource, 'name') ||
|
|
231
|
+
trimString(personaJson.name) ||
|
|
232
|
+
slug;
|
|
233
|
+
const description =
|
|
234
|
+
readPersonaConfigLiteral(personaConfigSource, 'description') ||
|
|
235
|
+
trimString(personaJson.summary) ||
|
|
236
|
+
name;
|
|
237
|
+
const templateVersion =
|
|
238
|
+
readPersonaConfigLiteral(personaConfigSource, 'templateVersion') ||
|
|
239
|
+
trimString(personaJson.promptTemplateVersion) ||
|
|
240
|
+
trimString(runtimeConfig.templateVersion) ||
|
|
241
|
+
DEFAULT_TEMPLATE_VERSION;
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
__OOMI_PERSONA_SLUG__: slug,
|
|
245
|
+
__OOMI_PERSONA_NAME__: name,
|
|
246
|
+
__OOMI_PERSONA_DESCRIPTION__: description,
|
|
247
|
+
__OOMI_TEMPLATE_VERSION__: templateVersion,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function renderPersonaTemplateFile({ workspacePath, relativePath }) {
|
|
252
|
+
const sourcePath = path.join(PERSONA_TEMPLATE_ROOT, relativePath);
|
|
253
|
+
const content = fs.readFileSync(sourcePath, 'utf8');
|
|
254
|
+
return renderTemplateFile(content, resolvePersonaTemplateVariables(workspacePath));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function readPersonaRuntimeConfig(workspacePath) {
|
|
258
|
+
if (!workspacePath) {
|
|
259
|
+
return {};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const runtimeConfigPath = path.join(workspacePath, 'oomi.runtime.json');
|
|
263
|
+
if (!fs.existsSync(runtimeConfigPath)) {
|
|
264
|
+
return {};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
const parsed = JSON.parse(fs.readFileSync(runtimeConfigPath, 'utf8'));
|
|
269
|
+
return parsed && typeof parsed === 'object' ? parsed : {};
|
|
270
|
+
} catch {
|
|
271
|
+
return {};
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function isWebSpatialRuntime(workspacePath) {
|
|
276
|
+
const runtimeConfig = readPersonaRuntimeConfig(workspacePath);
|
|
277
|
+
return trimString(runtimeConfig?.renderMode).toLowerCase() === 'webspatial';
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function syncVendoredWebSpatialPackages({
|
|
281
|
+
workspacePath,
|
|
282
|
+
} = {}) {
|
|
283
|
+
if (!workspacePath || !isWebSpatialRuntime(workspacePath)) {
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const packageJsonPath = path.join(workspacePath, 'package.json');
|
|
288
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const vendorTargetRoot = path.join(workspacePath, 'vendor', 'webspatial');
|
|
293
|
+
let changed = false;
|
|
294
|
+
if (fs.existsSync(WEBSPATIAL_VENDOR_ROOT)) {
|
|
295
|
+
changed = copyDirectory(WEBSPATIAL_VENDOR_ROOT, vendorTargetRoot) || changed;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
299
|
+
const dependencies =
|
|
300
|
+
packageJson.dependencies && typeof packageJson.dependencies === 'object'
|
|
301
|
+
? packageJson.dependencies
|
|
302
|
+
: {};
|
|
303
|
+
const devDependencies =
|
|
304
|
+
packageJson.devDependencies && typeof packageJson.devDependencies === 'object'
|
|
305
|
+
? packageJson.devDependencies
|
|
306
|
+
: {};
|
|
307
|
+
const scripts =
|
|
308
|
+
packageJson.scripts && typeof packageJson.scripts === 'object'
|
|
309
|
+
? packageJson.scripts
|
|
310
|
+
: {};
|
|
311
|
+
if (dependencies['@webspatial/core-sdk'] !== VENDORED_WEBSPATIAL_CORE_SPEC) {
|
|
312
|
+
dependencies['@webspatial/core-sdk'] = VENDORED_WEBSPATIAL_CORE_SPEC;
|
|
313
|
+
changed = true;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (dependencies['@webspatial/react-sdk'] !== VENDORED_WEBSPATIAL_REACT_SPEC) {
|
|
317
|
+
dependencies['@webspatial/react-sdk'] = VENDORED_WEBSPATIAL_REACT_SPEC;
|
|
318
|
+
changed = true;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (!dependencies['@zumer/snapdom']) {
|
|
322
|
+
dependencies['@zumer/snapdom'] = '^1.9.14';
|
|
323
|
+
changed = true;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (!dependencies['html2canvas']) {
|
|
327
|
+
dependencies['html2canvas'] = '^1.4.1';
|
|
328
|
+
changed = true;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
for (const dependencyName of WEBSPATIAL_TEMPLATE_DEV_DEPENDENCIES) {
|
|
332
|
+
const expectedVersion = trimString(
|
|
333
|
+
PERSONA_TEMPLATE_PACKAGE_JSON?.devDependencies?.[dependencyName],
|
|
334
|
+
);
|
|
335
|
+
if (!expectedVersion) {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if (devDependencies[dependencyName] !== expectedVersion) {
|
|
339
|
+
devDependencies[dependencyName] = expectedVersion;
|
|
340
|
+
changed = true;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
for (const scriptName of ['dev:avp', 'build']) {
|
|
345
|
+
const expectedScript = trimString(PERSONA_TEMPLATE_PACKAGE_JSON?.scripts?.[scriptName]);
|
|
346
|
+
if (!expectedScript || trimString(scripts[scriptName])) {
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
scripts[scriptName] = expectedScript;
|
|
350
|
+
changed = true;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (changed) {
|
|
354
|
+
packageJson.dependencies = dependencies;
|
|
355
|
+
packageJson.devDependencies = devDependencies;
|
|
356
|
+
packageJson.scripts = scripts;
|
|
357
|
+
fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, 'utf8');
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return changed;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
export function syncLegacyWebSpatialScaffoldFiles({
|
|
364
|
+
workspacePath,
|
|
365
|
+
} = {}) {
|
|
366
|
+
if (!workspacePath || !isWebSpatialRuntime(workspacePath)) {
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
let changed = false;
|
|
371
|
+
for (const rule of LEGACY_WEBSPATIAL_TEMPLATE_FILE_RULES) {
|
|
372
|
+
const targetPath = path.join(workspacePath, rule.relativePath);
|
|
373
|
+
const targetExists = fs.existsSync(targetPath);
|
|
374
|
+
const currentContent = targetExists ? fs.readFileSync(targetPath, 'utf8') : '';
|
|
375
|
+
if (targetExists && !rule.shouldReplace(currentContent)) {
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const renderedContent = renderPersonaTemplateFile({
|
|
380
|
+
workspacePath,
|
|
381
|
+
relativePath: rule.relativePath,
|
|
382
|
+
});
|
|
383
|
+
if (currentContent === renderedContent) {
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
ensureDir(path.dirname(targetPath));
|
|
388
|
+
fs.writeFileSync(targetPath, renderedContent, 'utf8');
|
|
389
|
+
changed = true;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return changed;
|
|
393
|
+
}
|
|
394
|
+
|
|
46
395
|
function readProcessCommandLine(pid) {
|
|
47
396
|
const safePid = normalizePositiveInteger(pid);
|
|
48
397
|
if (!safePid) {
|
|
@@ -176,25 +525,28 @@ function runProcess({ command, args, cwd }) {
|
|
|
176
525
|
});
|
|
177
526
|
}
|
|
178
527
|
|
|
179
|
-
export async function installPersonaWorkspace({
|
|
180
|
-
workspacePath,
|
|
181
|
-
}) {
|
|
182
|
-
if (!workspacePath) {
|
|
183
|
-
throw new Error('Workspace path is required.');
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
528
|
+
export async function installPersonaWorkspace({
|
|
529
|
+
workspacePath,
|
|
530
|
+
}) {
|
|
531
|
+
if (!workspacePath) {
|
|
532
|
+
throw new Error('Workspace path is required.');
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
syncVendoredWebSpatialPackages({ workspacePath });
|
|
536
|
+
syncLegacyWebSpatialScaffoldFiles({ workspacePath });
|
|
537
|
+
|
|
538
|
+
await runProcess({
|
|
539
|
+
command: resolveNpmCommand(),
|
|
540
|
+
args: ['install', '--silent', '--no-fund', '--no-audit'],
|
|
189
541
|
cwd: workspacePath,
|
|
190
542
|
});
|
|
191
543
|
}
|
|
192
544
|
|
|
193
|
-
export function resolvePersonaDevCommand({
|
|
194
|
-
workspacePath,
|
|
195
|
-
localPort,
|
|
196
|
-
}) {
|
|
197
|
-
const directCommand = resolveDirectViteCommand({ workspacePath, localPort });
|
|
545
|
+
export function resolvePersonaDevCommand({
|
|
546
|
+
workspacePath,
|
|
547
|
+
localPort,
|
|
548
|
+
}) {
|
|
549
|
+
const directCommand = resolveDirectViteCommand({ workspacePath, localPort });
|
|
198
550
|
if (directCommand) {
|
|
199
551
|
return directCommand;
|
|
200
552
|
}
|
|
@@ -206,15 +558,29 @@ export function resolvePersonaDevCommand({
|
|
|
206
558
|
}
|
|
207
559
|
return {
|
|
208
560
|
command: resolveNpmCommand(),
|
|
209
|
-
args,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
export function
|
|
214
|
-
workspacePath,
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
561
|
+
args,
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
export function resolvePersonaDevEnvironment({
|
|
566
|
+
workspacePath,
|
|
567
|
+
} = {}) {
|
|
568
|
+
const runtimeConfig = readPersonaRuntimeConfig(workspacePath);
|
|
569
|
+
const renderMode = trimString(runtimeConfig?.renderMode).toLowerCase();
|
|
570
|
+
if (renderMode === 'webspatial') {
|
|
571
|
+
return {
|
|
572
|
+
XR_ENV: 'avp',
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
return {};
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
export function startPersonaWorkspace({
|
|
580
|
+
workspacePath,
|
|
581
|
+
logFilePath,
|
|
582
|
+
env = {},
|
|
583
|
+
localPort,
|
|
218
584
|
}) {
|
|
219
585
|
if (!workspacePath) {
|
|
220
586
|
throw new Error('Workspace path is required.');
|
|
@@ -238,15 +604,16 @@ export function startPersonaWorkspace({
|
|
|
238
604
|
)} >> "${resolvedLogFilePath.replace(/"/g, '""')}" 2>&1`;
|
|
239
605
|
const child = spawn(process.env.ComSpec || 'cmd.exe', ['/d', '/s', '/c', shellCommand], {
|
|
240
606
|
cwd: workspacePath,
|
|
241
|
-
detached: true,
|
|
242
|
-
stdio: 'ignore',
|
|
243
|
-
shell: false,
|
|
244
|
-
windowsHide: true,
|
|
245
|
-
env: {
|
|
246
|
-
...process.env,
|
|
247
|
-
...
|
|
248
|
-
|
|
249
|
-
|
|
607
|
+
detached: true,
|
|
608
|
+
stdio: 'ignore',
|
|
609
|
+
shell: false,
|
|
610
|
+
windowsHide: true,
|
|
611
|
+
env: {
|
|
612
|
+
...process.env,
|
|
613
|
+
...resolvePersonaDevEnvironment({ workspacePath }),
|
|
614
|
+
...env,
|
|
615
|
+
},
|
|
616
|
+
});
|
|
250
617
|
|
|
251
618
|
child.unref();
|
|
252
619
|
|
|
@@ -265,15 +632,16 @@ export function startPersonaWorkspace({
|
|
|
265
632
|
try {
|
|
266
633
|
child = spawn(devCommand.command, devCommand.args, {
|
|
267
634
|
cwd: workspacePath,
|
|
268
|
-
detached: true,
|
|
269
|
-
stdio: ['ignore', output, output],
|
|
270
|
-
shell: false,
|
|
271
|
-
windowsHide: true,
|
|
272
|
-
env: {
|
|
273
|
-
...process.env,
|
|
274
|
-
...
|
|
275
|
-
|
|
276
|
-
|
|
635
|
+
detached: true,
|
|
636
|
+
stdio: ['ignore', output, output],
|
|
637
|
+
shell: false,
|
|
638
|
+
windowsHide: true,
|
|
639
|
+
env: {
|
|
640
|
+
...process.env,
|
|
641
|
+
...resolvePersonaDevEnvironment({ workspacePath }),
|
|
642
|
+
...env,
|
|
643
|
+
},
|
|
644
|
+
});
|
|
277
645
|
} finally {
|
|
278
646
|
fs.closeSync(output);
|
|
279
647
|
}
|
package/lib/scaffold.js
CHANGED
|
@@ -82,9 +82,9 @@ export function scaffoldPersonaApp({
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
const resolvedOutDir = path.resolve(outDir);
|
|
85
|
-
const templateRoot = resolveTemplateRoot(templateId, templateVersion);
|
|
86
|
-
const descriptor = readTemplateDescriptor(templateRoot);
|
|
87
|
-
const variables = {
|
|
85
|
+
const templateRoot = resolveTemplateRoot(templateId, templateVersion);
|
|
86
|
+
const descriptor = readTemplateDescriptor(templateRoot);
|
|
87
|
+
const variables = {
|
|
88
88
|
__OOMI_PERSONA_SLUG__: safeSlug,
|
|
89
89
|
__OOMI_PERSONA_NAME__: safeName,
|
|
90
90
|
__OOMI_PERSONA_DESCRIPTION__: safeDescription,
|
|
@@ -95,14 +95,14 @@ export function scaffoldPersonaApp({
|
|
|
95
95
|
copyTemplateTree(templateRoot, resolvedOutDir, variables);
|
|
96
96
|
|
|
97
97
|
return {
|
|
98
|
-
ok: true,
|
|
99
|
-
templateId,
|
|
100
|
-
templateVersion,
|
|
101
|
-
slug: safeSlug,
|
|
102
|
-
outDir: resolvedOutDir,
|
|
103
|
-
startCommand: `cd ${resolvedOutDir} && npm install && npm run dev`,
|
|
104
|
-
healthPath: descriptor.healthPath,
|
|
105
|
-
editableZones: descriptor.editableZones,
|
|
106
|
-
defaultPort: descriptor.defaultPort,
|
|
107
|
-
};
|
|
108
|
-
}
|
|
98
|
+
ok: true,
|
|
99
|
+
templateId,
|
|
100
|
+
templateVersion,
|
|
101
|
+
slug: safeSlug,
|
|
102
|
+
outDir: resolvedOutDir,
|
|
103
|
+
startCommand: `cd ${resolvedOutDir} && npm install && ${descriptor.startCommand || 'npm run dev'}`,
|
|
104
|
+
healthPath: descriptor.healthPath,
|
|
105
|
+
editableZones: descriptor.editableZones,
|
|
106
|
+
defaultPort: descriptor.defaultPort,
|
|
107
|
+
};
|
|
108
|
+
}
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oomi-ai",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.41",
|
|
4
4
|
"description": "Oomi OpenClaw channel plugin and bridge tooling",
|
|
5
5
|
"bin": {
|
|
6
6
|
"oomi": "bin/oomi-ai.js"
|
|
@@ -58,10 +58,12 @@
|
|
|
58
58
|
"bin/sessionBridgeState.js",
|
|
59
59
|
"lib",
|
|
60
60
|
"openclaw.plugin.json",
|
|
61
|
-
"openclaw.extension.js",
|
|
62
|
-
"agent_instructions.md",
|
|
63
|
-
"README.md",
|
|
64
|
-
"skills/oomi",
|
|
65
|
-
"templates/persona-app"
|
|
66
|
-
|
|
67
|
-
|
|
61
|
+
"openclaw.extension.js",
|
|
62
|
+
"agent_instructions.md",
|
|
63
|
+
"README.md",
|
|
64
|
+
"skills/oomi",
|
|
65
|
+
"templates/persona-app",
|
|
66
|
+
"templates/persona-app/vendor/webspatial/core-sdk/dist",
|
|
67
|
+
"templates/persona-app/vendor/webspatial/react-sdk/dist"
|
|
68
|
+
]
|
|
69
|
+
}
|
|
@@ -12,8 +12,10 @@
|
|
|
12
12
|
"lint": "eslint ."
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@webspatial/core-sdk": "
|
|
16
|
-
"@webspatial/react-sdk": "
|
|
15
|
+
"@webspatial/core-sdk": "file:./vendor/webspatial/core-sdk",
|
|
16
|
+
"@webspatial/react-sdk": "file:./vendor/webspatial/react-sdk",
|
|
17
|
+
"@zumer/snapdom": "^1.9.14",
|
|
18
|
+
"html2canvas": "^1.4.1",
|
|
17
19
|
"react": "^19.0.0",
|
|
18
20
|
"react-dom": "^19.0.0",
|
|
19
21
|
"react-router-dom": "^7.4.0",
|
|
@@ -24,8 +26,8 @@
|
|
|
24
26
|
"@types/react": "^19.0.10",
|
|
25
27
|
"@types/react-dom": "^19.0.4",
|
|
26
28
|
"@vitejs/plugin-react": "^4.3.4",
|
|
27
|
-
"@webspatial/builder": "^
|
|
28
|
-
"@webspatial/platform-visionos": "^
|
|
29
|
+
"@webspatial/builder": "^1.2.1",
|
|
30
|
+
"@webspatial/platform-visionos": "^1.2.1",
|
|
29
31
|
"@webspatial/vite-plugin": "^0.1.7",
|
|
30
32
|
"cross-env": "^7.0.3",
|
|
31
33
|
"dotenv-cli": "^8.0.0",
|