oomi-ai 0.2.39 → 0.2.40
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 +392 -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 +564 -79
- 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 +120 -39
- 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 +4296 -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 +4336 -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,155 @@
|
|
|
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
|
+
},
|
|
44
|
+
{
|
|
45
|
+
relativePath: path.join('src', 'main.tsx'),
|
|
46
|
+
shouldReplace: (content) =>
|
|
47
|
+
!content.includes('snapdom') &&
|
|
48
|
+
!content.includes('html2canvas') &&
|
|
49
|
+
content.includes('createRoot(document.getElementById("root")!)'),
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
relativePath: path.join('src', 'App.tsx'),
|
|
53
|
+
shouldReplace: (content) =>
|
|
54
|
+
content.includes('<Route path="/" element={<HomePage />} />') ||
|
|
55
|
+
content.includes('<Route path="/scene" element={<ScenePage />} />'),
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
relativePath: path.join('src', 'pages', 'HomePage.tsx'),
|
|
59
|
+
shouldReplace: (content) =>
|
|
60
|
+
content.includes('Open Spatial Scene') &&
|
|
61
|
+
content.includes('Open Scene Route'),
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
relativePath: path.join('src', 'pages', 'ScenePage.tsx'),
|
|
65
|
+
shouldReplace: (content) =>
|
|
66
|
+
!content.includes('enable-xr-monitor') &&
|
|
67
|
+
content.includes(
|
|
68
|
+
'This route is intentionally separate so WebSpatial scene launching has a dedicated',
|
|
69
|
+
),
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
relativePath: path.join('src', 'App.css'),
|
|
73
|
+
shouldReplace: (content) =>
|
|
74
|
+
content.includes('.scene-panel') && !content.includes('.scene-interaction-grid'),
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
relativePath: 'vite.config.ts',
|
|
78
|
+
shouldReplace: (content) =>
|
|
79
|
+
content.includes('webSpatial()') && !content.includes('optimizeDeps'),
|
|
80
|
+
},
|
|
81
|
+
];
|
|
6
82
|
|
|
7
83
|
function resolveNpmCommand() {
|
|
8
84
|
return process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
9
85
|
}
|
|
10
86
|
|
|
11
|
-
function ensureDir(dirPath) {
|
|
12
|
-
fs.mkdirSync(dirPath, { recursive: true });
|
|
13
|
-
}
|
|
87
|
+
function ensureDir(dirPath) {
|
|
88
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function readJsonFile(filePath) {
|
|
92
|
+
if (!fs.existsSync(filePath)) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
98
|
+
} catch {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function copyDirectory(sourcePath, targetPath) {
|
|
104
|
+
ensureDir(targetPath);
|
|
105
|
+
let changed = false;
|
|
106
|
+
const sourceEntries = fs.readdirSync(sourcePath, { withFileTypes: true });
|
|
107
|
+
const sourceEntryNames = new Set(sourceEntries.map((entry) => entry.name));
|
|
108
|
+
const existingTargetEntries = fs.readdirSync(targetPath, { withFileTypes: true });
|
|
109
|
+
|
|
110
|
+
for (const entry of existingTargetEntries) {
|
|
111
|
+
if (sourceEntryNames.has(entry.name)) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
fs.rmSync(path.join(targetPath, entry.name), { recursive: true, force: true });
|
|
115
|
+
changed = true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
for (const entry of sourceEntries) {
|
|
119
|
+
const sourceEntryPath = path.join(sourcePath, entry.name);
|
|
120
|
+
const targetEntryPath = path.join(targetPath, entry.name);
|
|
121
|
+
const targetExists = fs.existsSync(targetEntryPath);
|
|
122
|
+
|
|
123
|
+
if (entry.isDirectory()) {
|
|
124
|
+
if (targetExists && !fs.statSync(targetEntryPath).isDirectory()) {
|
|
125
|
+
fs.rmSync(targetEntryPath, { recursive: true, force: true });
|
|
126
|
+
changed = true;
|
|
127
|
+
}
|
|
128
|
+
if (copyDirectory(sourceEntryPath, targetEntryPath)) {
|
|
129
|
+
changed = true;
|
|
130
|
+
}
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (targetExists && fs.statSync(targetEntryPath).isDirectory()) {
|
|
135
|
+
fs.rmSync(targetEntryPath, { recursive: true, force: true });
|
|
136
|
+
changed = true;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const sourceBuffer = fs.readFileSync(sourceEntryPath);
|
|
140
|
+
const targetBuffer = fs.existsSync(targetEntryPath)
|
|
141
|
+
? fs.readFileSync(targetEntryPath)
|
|
142
|
+
: null;
|
|
143
|
+
if (targetBuffer && sourceBuffer.equals(targetBuffer)) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
fs.copyFileSync(sourceEntryPath, targetEntryPath);
|
|
148
|
+
changed = true;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return changed;
|
|
152
|
+
}
|
|
14
153
|
|
|
15
154
|
function quoteWindowsCommandPart(value) {
|
|
16
155
|
const text = String(value);
|
|
@@ -43,6 +182,191 @@ function trimString(value) {
|
|
|
43
182
|
return typeof value === 'string' ? value.trim() : '';
|
|
44
183
|
}
|
|
45
184
|
|
|
185
|
+
function readPersonaConfigLiteral(source, key) {
|
|
186
|
+
if (!source) {
|
|
187
|
+
return '';
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const match = source.match(new RegExp(`${key}:\\s*"([^"]*)"`, 'u'));
|
|
191
|
+
return trimString(match?.[1] || '');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function resolvePersonaTemplateVariables(workspacePath) {
|
|
195
|
+
const personaConfigSource = fs.existsSync(path.join(workspacePath, 'src', 'persona', 'config.ts'))
|
|
196
|
+
? fs.readFileSync(path.join(workspacePath, 'src', 'persona', 'config.ts'), 'utf8')
|
|
197
|
+
: '';
|
|
198
|
+
const personaJson = readJsonFile(path.join(workspacePath, 'persona.json')) || {};
|
|
199
|
+
const runtimeConfig = readPersonaRuntimeConfig(workspacePath);
|
|
200
|
+
const slug =
|
|
201
|
+
readPersonaConfigLiteral(personaConfigSource, 'slug') ||
|
|
202
|
+
trimString(personaJson.id) ||
|
|
203
|
+
path.basename(path.resolve(workspacePath));
|
|
204
|
+
const name =
|
|
205
|
+
readPersonaConfigLiteral(personaConfigSource, 'name') ||
|
|
206
|
+
trimString(personaJson.name) ||
|
|
207
|
+
slug;
|
|
208
|
+
const description =
|
|
209
|
+
readPersonaConfigLiteral(personaConfigSource, 'description') ||
|
|
210
|
+
trimString(personaJson.summary) ||
|
|
211
|
+
name;
|
|
212
|
+
const templateVersion =
|
|
213
|
+
readPersonaConfigLiteral(personaConfigSource, 'templateVersion') ||
|
|
214
|
+
trimString(personaJson.promptTemplateVersion) ||
|
|
215
|
+
trimString(runtimeConfig.templateVersion) ||
|
|
216
|
+
DEFAULT_TEMPLATE_VERSION;
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
__OOMI_PERSONA_SLUG__: slug,
|
|
220
|
+
__OOMI_PERSONA_NAME__: name,
|
|
221
|
+
__OOMI_PERSONA_DESCRIPTION__: description,
|
|
222
|
+
__OOMI_TEMPLATE_VERSION__: templateVersion,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function renderPersonaTemplateFile({ workspacePath, relativePath }) {
|
|
227
|
+
const sourcePath = path.join(PERSONA_TEMPLATE_ROOT, relativePath);
|
|
228
|
+
const content = fs.readFileSync(sourcePath, 'utf8');
|
|
229
|
+
return renderTemplateFile(content, resolvePersonaTemplateVariables(workspacePath));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function readPersonaRuntimeConfig(workspacePath) {
|
|
233
|
+
if (!workspacePath) {
|
|
234
|
+
return {};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const runtimeConfigPath = path.join(workspacePath, 'oomi.runtime.json');
|
|
238
|
+
if (!fs.existsSync(runtimeConfigPath)) {
|
|
239
|
+
return {};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
const parsed = JSON.parse(fs.readFileSync(runtimeConfigPath, 'utf8'));
|
|
244
|
+
return parsed && typeof parsed === 'object' ? parsed : {};
|
|
245
|
+
} catch {
|
|
246
|
+
return {};
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function isWebSpatialRuntime(workspacePath) {
|
|
251
|
+
const runtimeConfig = readPersonaRuntimeConfig(workspacePath);
|
|
252
|
+
return trimString(runtimeConfig?.renderMode).toLowerCase() === 'webspatial';
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export function syncVendoredWebSpatialPackages({
|
|
256
|
+
workspacePath,
|
|
257
|
+
} = {}) {
|
|
258
|
+
if (!workspacePath || !isWebSpatialRuntime(workspacePath)) {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const packageJsonPath = path.join(workspacePath, 'package.json');
|
|
263
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const vendorTargetRoot = path.join(workspacePath, 'vendor', 'webspatial');
|
|
268
|
+
let changed = false;
|
|
269
|
+
if (fs.existsSync(WEBSPATIAL_VENDOR_ROOT)) {
|
|
270
|
+
changed = copyDirectory(WEBSPATIAL_VENDOR_ROOT, vendorTargetRoot) || changed;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
274
|
+
const dependencies =
|
|
275
|
+
packageJson.dependencies && typeof packageJson.dependencies === 'object'
|
|
276
|
+
? packageJson.dependencies
|
|
277
|
+
: {};
|
|
278
|
+
const devDependencies =
|
|
279
|
+
packageJson.devDependencies && typeof packageJson.devDependencies === 'object'
|
|
280
|
+
? packageJson.devDependencies
|
|
281
|
+
: {};
|
|
282
|
+
const scripts =
|
|
283
|
+
packageJson.scripts && typeof packageJson.scripts === 'object'
|
|
284
|
+
? packageJson.scripts
|
|
285
|
+
: {};
|
|
286
|
+
if (dependencies['@webspatial/core-sdk'] !== VENDORED_WEBSPATIAL_CORE_SPEC) {
|
|
287
|
+
dependencies['@webspatial/core-sdk'] = VENDORED_WEBSPATIAL_CORE_SPEC;
|
|
288
|
+
changed = true;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (dependencies['@webspatial/react-sdk'] !== VENDORED_WEBSPATIAL_REACT_SPEC) {
|
|
292
|
+
dependencies['@webspatial/react-sdk'] = VENDORED_WEBSPATIAL_REACT_SPEC;
|
|
293
|
+
changed = true;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (!dependencies['@zumer/snapdom']) {
|
|
297
|
+
dependencies['@zumer/snapdom'] = '^1.9.14';
|
|
298
|
+
changed = true;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (!dependencies['html2canvas']) {
|
|
302
|
+
dependencies['html2canvas'] = '^1.4.1';
|
|
303
|
+
changed = true;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
for (const dependencyName of WEBSPATIAL_TEMPLATE_DEV_DEPENDENCIES) {
|
|
307
|
+
const expectedVersion = trimString(
|
|
308
|
+
PERSONA_TEMPLATE_PACKAGE_JSON?.devDependencies?.[dependencyName],
|
|
309
|
+
);
|
|
310
|
+
if (!expectedVersion) {
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
if (devDependencies[dependencyName] !== expectedVersion) {
|
|
314
|
+
devDependencies[dependencyName] = expectedVersion;
|
|
315
|
+
changed = true;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
for (const scriptName of ['dev:avp', 'build']) {
|
|
320
|
+
const expectedScript = trimString(PERSONA_TEMPLATE_PACKAGE_JSON?.scripts?.[scriptName]);
|
|
321
|
+
if (!expectedScript || trimString(scripts[scriptName])) {
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
scripts[scriptName] = expectedScript;
|
|
325
|
+
changed = true;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (changed) {
|
|
329
|
+
packageJson.dependencies = dependencies;
|
|
330
|
+
packageJson.devDependencies = devDependencies;
|
|
331
|
+
packageJson.scripts = scripts;
|
|
332
|
+
fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, 'utf8');
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return changed;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export function syncLegacyWebSpatialScaffoldFiles({
|
|
339
|
+
workspacePath,
|
|
340
|
+
} = {}) {
|
|
341
|
+
if (!workspacePath || !isWebSpatialRuntime(workspacePath)) {
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
let changed = false;
|
|
346
|
+
for (const rule of LEGACY_WEBSPATIAL_TEMPLATE_FILE_RULES) {
|
|
347
|
+
const targetPath = path.join(workspacePath, rule.relativePath);
|
|
348
|
+
const targetExists = fs.existsSync(targetPath);
|
|
349
|
+
const currentContent = targetExists ? fs.readFileSync(targetPath, 'utf8') : '';
|
|
350
|
+
if (targetExists && !rule.shouldReplace(currentContent)) {
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const renderedContent = renderPersonaTemplateFile({
|
|
355
|
+
workspacePath,
|
|
356
|
+
relativePath: rule.relativePath,
|
|
357
|
+
});
|
|
358
|
+
if (currentContent === renderedContent) {
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
ensureDir(path.dirname(targetPath));
|
|
363
|
+
fs.writeFileSync(targetPath, renderedContent, 'utf8');
|
|
364
|
+
changed = true;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return changed;
|
|
368
|
+
}
|
|
369
|
+
|
|
46
370
|
function readProcessCommandLine(pid) {
|
|
47
371
|
const safePid = normalizePositiveInteger(pid);
|
|
48
372
|
if (!safePid) {
|
|
@@ -176,25 +500,28 @@ function runProcess({ command, args, cwd }) {
|
|
|
176
500
|
});
|
|
177
501
|
}
|
|
178
502
|
|
|
179
|
-
export async function installPersonaWorkspace({
|
|
180
|
-
workspacePath,
|
|
181
|
-
}) {
|
|
182
|
-
if (!workspacePath) {
|
|
183
|
-
throw new Error('Workspace path is required.');
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
503
|
+
export async function installPersonaWorkspace({
|
|
504
|
+
workspacePath,
|
|
505
|
+
}) {
|
|
506
|
+
if (!workspacePath) {
|
|
507
|
+
throw new Error('Workspace path is required.');
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
syncVendoredWebSpatialPackages({ workspacePath });
|
|
511
|
+
syncLegacyWebSpatialScaffoldFiles({ workspacePath });
|
|
512
|
+
|
|
513
|
+
await runProcess({
|
|
514
|
+
command: resolveNpmCommand(),
|
|
515
|
+
args: ['install', '--silent', '--no-fund', '--no-audit'],
|
|
189
516
|
cwd: workspacePath,
|
|
190
517
|
});
|
|
191
518
|
}
|
|
192
519
|
|
|
193
|
-
export function resolvePersonaDevCommand({
|
|
194
|
-
workspacePath,
|
|
195
|
-
localPort,
|
|
196
|
-
}) {
|
|
197
|
-
const directCommand = resolveDirectViteCommand({ workspacePath, localPort });
|
|
520
|
+
export function resolvePersonaDevCommand({
|
|
521
|
+
workspacePath,
|
|
522
|
+
localPort,
|
|
523
|
+
}) {
|
|
524
|
+
const directCommand = resolveDirectViteCommand({ workspacePath, localPort });
|
|
198
525
|
if (directCommand) {
|
|
199
526
|
return directCommand;
|
|
200
527
|
}
|
|
@@ -206,15 +533,29 @@ export function resolvePersonaDevCommand({
|
|
|
206
533
|
}
|
|
207
534
|
return {
|
|
208
535
|
command: resolveNpmCommand(),
|
|
209
|
-
args,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
export function
|
|
214
|
-
workspacePath,
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
536
|
+
args,
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
export function resolvePersonaDevEnvironment({
|
|
541
|
+
workspacePath,
|
|
542
|
+
} = {}) {
|
|
543
|
+
const runtimeConfig = readPersonaRuntimeConfig(workspacePath);
|
|
544
|
+
const renderMode = trimString(runtimeConfig?.renderMode).toLowerCase();
|
|
545
|
+
if (renderMode === 'webspatial') {
|
|
546
|
+
return {
|
|
547
|
+
XR_ENV: 'avp',
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
return {};
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
export function startPersonaWorkspace({
|
|
555
|
+
workspacePath,
|
|
556
|
+
logFilePath,
|
|
557
|
+
env = {},
|
|
558
|
+
localPort,
|
|
218
559
|
}) {
|
|
219
560
|
if (!workspacePath) {
|
|
220
561
|
throw new Error('Workspace path is required.');
|
|
@@ -238,15 +579,16 @@ export function startPersonaWorkspace({
|
|
|
238
579
|
)} >> "${resolvedLogFilePath.replace(/"/g, '""')}" 2>&1`;
|
|
239
580
|
const child = spawn(process.env.ComSpec || 'cmd.exe', ['/d', '/s', '/c', shellCommand], {
|
|
240
581
|
cwd: workspacePath,
|
|
241
|
-
detached: true,
|
|
242
|
-
stdio: 'ignore',
|
|
243
|
-
shell: false,
|
|
244
|
-
windowsHide: true,
|
|
245
|
-
env: {
|
|
246
|
-
...process.env,
|
|
247
|
-
...
|
|
248
|
-
|
|
249
|
-
|
|
582
|
+
detached: true,
|
|
583
|
+
stdio: 'ignore',
|
|
584
|
+
shell: false,
|
|
585
|
+
windowsHide: true,
|
|
586
|
+
env: {
|
|
587
|
+
...process.env,
|
|
588
|
+
...resolvePersonaDevEnvironment({ workspacePath }),
|
|
589
|
+
...env,
|
|
590
|
+
},
|
|
591
|
+
});
|
|
250
592
|
|
|
251
593
|
child.unref();
|
|
252
594
|
|
|
@@ -265,15 +607,16 @@ export function startPersonaWorkspace({
|
|
|
265
607
|
try {
|
|
266
608
|
child = spawn(devCommand.command, devCommand.args, {
|
|
267
609
|
cwd: workspacePath,
|
|
268
|
-
detached: true,
|
|
269
|
-
stdio: ['ignore', output, output],
|
|
270
|
-
shell: false,
|
|
271
|
-
windowsHide: true,
|
|
272
|
-
env: {
|
|
273
|
-
...process.env,
|
|
274
|
-
...
|
|
275
|
-
|
|
276
|
-
|
|
610
|
+
detached: true,
|
|
611
|
+
stdio: ['ignore', output, output],
|
|
612
|
+
shell: false,
|
|
613
|
+
windowsHide: true,
|
|
614
|
+
env: {
|
|
615
|
+
...process.env,
|
|
616
|
+
...resolvePersonaDevEnvironment({ workspacePath }),
|
|
617
|
+
...env,
|
|
618
|
+
},
|
|
619
|
+
});
|
|
277
620
|
} finally {
|
|
278
621
|
fs.closeSync(output);
|
|
279
622
|
}
|
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.40",
|
|
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",
|