openclaw-openagent 1.0.11 → 1.0.12
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/dist/index.js +1 -1
- package/dist/src/app/remote-agent-tool.js +110 -14
- package/dist/src/app/types.d.ts +2 -2
- package/dist/src/plugin-ui/adapters/adapters/oc-2026-04.js +103 -0
- package/dist/src/plugin-ui/adapters/adapters/oc-2026-05.js +125 -0
- package/dist/src/plugin-ui/adapters/adapters/oc-2026-06.js +125 -0
- package/dist/src/plugin-ui/adapters/adapters/oc-unknown.js +48 -0
- package/dist/src/plugin-ui/adapters/oc-2026-04.js +103 -0
- package/dist/src/plugin-ui/adapters/oc-2026-05.js +125 -0
- package/dist/src/plugin-ui/adapters/oc-2026-06.js +125 -0
- package/dist/src/plugin-ui/adapters/oc-unknown.js +48 -0
- package/dist/src/plugin-ui/assets/openagent-override.js +1007 -258
- package/dist/src/plugin-ui/index.d.ts +1 -1
- package/dist/src/plugin-ui/index.js +2 -2
- package/dist/src/plugin-ui/ui-extension-loader/index.d.ts +2 -1
- package/dist/src/plugin-ui/ui-extension-loader/index.js +5 -5
- package/dist/src/plugin-ui/ui-extension-loader/registry-regex.js +75 -8
- package/dist/src/plugin-ui/ui-extension-loader/types.d.ts +4 -1
- package/dist/src/state/store.d.ts +21 -0
- package/dist/src/state/store.js +54 -0
- package/dist/src/transport/oasn/oasn-invocation.d.ts +3 -0
- package/dist/src/transport/oasn/oasn-invocation.js +28 -12
- package/dist/src/transport/oasn/oasn-types.d.ts +8 -4
- package/index.ts +1 -1
- package/package.json +4 -3
- package/src/app/remote-agent-tool.ts +131 -16
- package/src/app/types.ts +2 -2
- package/src/plugin-ui/adapters/oc-2026-04.js +103 -0
- package/src/plugin-ui/adapters/oc-2026-05.js +125 -0
- package/src/plugin-ui/adapters/oc-2026-06.js +125 -0
- package/src/plugin-ui/adapters/oc-unknown.js +48 -0
- package/src/plugin-ui/assets/openagent-override.js +1007 -258
- package/src/plugin-ui/build.cjs +249 -38
- package/src/plugin-ui/index.ts +2 -2
- package/src/plugin-ui/modules/agent-book/panel/agent-book.js +102 -12
- package/src/plugin-ui/modules/agent-book/panel/inject-ui.js +105 -3
- package/src/plugin-ui/modules/agent-book/panel/styles.js +42 -35
- package/src/plugin-ui/modules/agent-book/remote-agent-tool/components-core.js +4 -2
- package/src/plugin-ui/modules/agent-book/remote-agent-tool/thought-chain-card.js +4 -1
- package/src/plugin-ui/modules/agent-book/scanner.js +17 -5
- package/src/plugin-ui/modules/agent-book/travelcard/travel-styles.js +5 -0
- package/src/plugin-ui/modules/loader/shared-state.js +244 -20
- package/src/plugin-ui/modules/remote-agent/execution-card.js +54 -16
- package/src/plugin-ui/modules/remote-agent/native-style-adapter.js +5 -23
- package/src/plugin-ui/modules/remote-agent/output-card.js +13 -7
- package/src/plugin-ui/modules/remote-agent/render-hooks.js +53 -41
- package/src/plugin-ui/modules/remote-agent/styles.js +230 -88
- package/src/plugin-ui/modules/remote-agent/tool-card-model.js +72 -4
- package/src/plugin-ui/postinstall-deploy.cjs +52 -0
- package/src/plugin-ui/ui-extension-loader/index.ts +6 -6
- package/src/plugin-ui/ui-extension-loader/registry-regex.ts +81 -9
- package/src/plugin-ui/ui-extension-loader/types.ts +5 -1
- package/src/state/store.ts +80 -0
- package/src/transport/oasn/oasn-invocation.ts +47 -12
- package/src/transport/oasn/oasn-types.ts +6 -2
- package/src/types/openclaw-plugin-sdk-media-store.d.ts +9 -0
package/src/plugin-ui/build.cjs
CHANGED
|
@@ -13,25 +13,33 @@
|
|
|
13
13
|
|
|
14
14
|
const fs = require('fs');
|
|
15
15
|
const path = require('path');
|
|
16
|
+
const crypto = require('crypto');
|
|
17
|
+
const childProcess = require('child_process');
|
|
16
18
|
|
|
17
19
|
// ── 读取 OpenClaw 版本(用于注入到产物中)──
|
|
18
|
-
function
|
|
20
|
+
function resolveBuildHostVersion() {
|
|
19
21
|
// 优先使用环境变量
|
|
20
|
-
if (process.env.OPENCLAW_HOST_VERSION) return process.env.OPENCLAW_HOST_VERSION;
|
|
22
|
+
if (process.env.OPENCLAW_HOST_VERSION) return normalizeHostVersion(process.env.OPENCLAW_HOST_VERSION);
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
var packageCandidates = [
|
|
25
|
+
path.join(__dirname, '..', '..', 'node_modules', 'openclaw', 'package.json'),
|
|
26
|
+
path.join(__dirname, '..', '..', 'package.json'),
|
|
27
|
+
];
|
|
28
|
+
for (var i = 0; i < packageCandidates.length; i++) {
|
|
29
|
+
try {
|
|
30
|
+
if (!fs.existsSync(packageCandidates[i])) continue;
|
|
31
|
+
var candidatePkg = JSON.parse(fs.readFileSync(packageCandidates[i], 'utf-8'));
|
|
32
|
+
if (candidatePkg && candidatePkg.name === 'openclaw') {
|
|
33
|
+
var exactVersion = normalizeHostVersion(candidatePkg.version);
|
|
34
|
+
if (exactVersion) return exactVersion;
|
|
35
|
+
}
|
|
36
|
+
var depVersion = candidatePkg && candidatePkg.devDependencies && candidatePkg.devDependencies.openclaw;
|
|
37
|
+
depVersion = normalizeHostVersion(String(depVersion || '').replace(/^[\^~>=<]+/, '').trim());
|
|
38
|
+
if (depVersion) return depVersion;
|
|
39
|
+
} catch (e) {}
|
|
40
|
+
}
|
|
33
41
|
|
|
34
|
-
return '
|
|
42
|
+
return '';
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
// ═══════════════════════════════════════════════════════════════
|
|
@@ -78,10 +86,201 @@ const MANIFEST = [
|
|
|
78
86
|
// ═══════════════════════════════════════════════════════════════
|
|
79
87
|
|
|
80
88
|
const modulesDir = path.join(__dirname, 'modules');
|
|
89
|
+
const adaptersDir = path.join(__dirname, 'adapters');
|
|
81
90
|
const assetsDir = path.join(__dirname, 'assets');
|
|
82
91
|
const outFile = path.join(assetsDir, 'openagent-override.js');
|
|
83
92
|
const iconFile = path.join(assetsDir, 'icon.png');
|
|
84
93
|
const bgFile = path.join(assetsDir, 'bg.png');
|
|
94
|
+
const HOST_VERSION_TOKEN = "hostVersion: '__OPENCLAW_HOST_VERSION__'";
|
|
95
|
+
const BUILD_ID_TOKEN = "buildId: '__OPENAGENT_BUILD_ID__'";
|
|
96
|
+
const BUILD_ID_ASSIGNMENT_RE = /buildId:\s*(?:"[^"]*"|'[^']*')/;
|
|
97
|
+
const HOST_ADAPTER_START = '/*__OPENAGENT_HOST_ADAPTER_START__*/';
|
|
98
|
+
const HOST_ADAPTER_END = '/*__OPENAGENT_HOST_ADAPTER_END__*/';
|
|
99
|
+
const HOST_ADAPTER_BLOCK_RE = /\/\*__OPENAGENT_HOST_ADAPTER_START__\*\/[\s\S]*?\/\*__OPENAGENT_HOST_ADAPTER_END__\*\//;
|
|
100
|
+
|
|
101
|
+
function normalizeHostVersion(version) {
|
|
102
|
+
var match = String(version || '').match(/20\d{2}\.\d{1,2}\.\d{1,2}(?:-[A-Za-z0-9._-]+)?/);
|
|
103
|
+
return match ? match[0] : '';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function injectHostVersion(content, hostVersion) {
|
|
107
|
+
hostVersion = normalizeHostVersion(hostVersion);
|
|
108
|
+
if (!hostVersion) return content;
|
|
109
|
+
return content.replace(HOST_VERSION_TOKEN, 'hostVersion: ' + JSON.stringify(hostVersion));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function resolveHostAdapterId(hostVersion) {
|
|
113
|
+
var parsed = normalizeHostVersion(hostVersion).split('.');
|
|
114
|
+
var year = parseInt(parsed[0], 10);
|
|
115
|
+
var month = parseInt(parsed[1], 10);
|
|
116
|
+
if (year === 2026 && month === 4) return 'oc-2026-04';
|
|
117
|
+
if (year === 2026 && month === 5) return 'oc-2026-05';
|
|
118
|
+
if (year === 2026 && month === 6) return 'oc-2026-06';
|
|
119
|
+
return 'oc-unknown';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function getHostAdapterBlock(hostVersion) {
|
|
123
|
+
var adapterId = resolveHostAdapterId(hostVersion);
|
|
124
|
+
var adapterPath = path.join(adaptersDir, adapterId + '.js');
|
|
125
|
+
if (!fs.existsSync(adapterPath)) {
|
|
126
|
+
adapterId = 'oc-unknown';
|
|
127
|
+
adapterPath = path.join(adaptersDir, adapterId + '.js');
|
|
128
|
+
}
|
|
129
|
+
var adapterContent = fs.readFileSync(adapterPath, 'utf-8').trimEnd();
|
|
130
|
+
return [
|
|
131
|
+
HOST_ADAPTER_START,
|
|
132
|
+
'// HOST ADAPTER: ' + adapterId,
|
|
133
|
+
adapterContent,
|
|
134
|
+
HOST_ADAPTER_END,
|
|
135
|
+
].join('\n');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function injectHostAdapter(content, hostVersion) {
|
|
139
|
+
var block = getHostAdapterBlock(hostVersion);
|
|
140
|
+
if (HOST_ADAPTER_BLOCK_RE.test(content)) {
|
|
141
|
+
return content.replace(HOST_ADAPTER_BLOCK_RE, block);
|
|
142
|
+
}
|
|
143
|
+
return content;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function renderOverrideContent(content, hostVersion) {
|
|
147
|
+
return injectBuildId(injectHostAdapter(injectHostVersion(content, hostVersion), hostVersion));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function hashContent(content) {
|
|
151
|
+
return crypto.createHash('sha256').update(content).digest('hex').slice(0, 12);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function injectBuildId(content) {
|
|
155
|
+
var normalized = content.replace(BUILD_ID_ASSIGNMENT_RE, BUILD_ID_TOKEN);
|
|
156
|
+
var buildId = hashContent(normalized);
|
|
157
|
+
return normalized.replace(BUILD_ID_TOKEN, 'buildId: ' + JSON.stringify(buildId));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function resolveControlUiHostVersion(controlUiDir) {
|
|
161
|
+
if (process.env.OPENCLAW_HOST_VERSION) return normalizeHostVersion(process.env.OPENCLAW_HOST_VERSION);
|
|
162
|
+
|
|
163
|
+
var candidates = [
|
|
164
|
+
path.join(controlUiDir, '..', '..', 'package.json'),
|
|
165
|
+
path.join(controlUiDir, '..', 'package.json'),
|
|
166
|
+
];
|
|
167
|
+
for (var i = 0; i < candidates.length; i++) {
|
|
168
|
+
try {
|
|
169
|
+
if (!fs.existsSync(candidates[i])) continue;
|
|
170
|
+
var pkg = JSON.parse(fs.readFileSync(candidates[i], 'utf-8'));
|
|
171
|
+
if (pkg && pkg.name === 'openclaw') return normalizeHostVersion(pkg.version);
|
|
172
|
+
} catch (e) {}
|
|
173
|
+
}
|
|
174
|
+
return '';
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function pushUniquePath(list, candidate) {
|
|
178
|
+
if (!list || !candidate) return;
|
|
179
|
+
var resolved = path.resolve(candidate);
|
|
180
|
+
for (var i = 0; i < list.length; i++) {
|
|
181
|
+
if (path.resolve(list[i]) === resolved) return;
|
|
182
|
+
}
|
|
183
|
+
list.push(resolved);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function collectCommandOutputLines(command, args) {
|
|
187
|
+
try {
|
|
188
|
+
var result = childProcess.spawnSync(command, args, {
|
|
189
|
+
encoding: 'utf-8',
|
|
190
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
191
|
+
});
|
|
192
|
+
return String(result.stdout || '')
|
|
193
|
+
.split(/\r?\n/)
|
|
194
|
+
.map(function(line) { return line.trim(); })
|
|
195
|
+
.filter(Boolean);
|
|
196
|
+
} catch (e) {
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function collectGlobalNodeModuleRoots() {
|
|
202
|
+
var roots = [];
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
pushUniquePath(roots, path.join(path.dirname(process.execPath), '..', 'lib', 'node_modules'));
|
|
206
|
+
} catch (e) {}
|
|
207
|
+
|
|
208
|
+
var npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
209
|
+
var npmRoots = collectCommandOutputLines(npmCommand, ['root', '-g']);
|
|
210
|
+
for (var n = 0; n < npmRoots.length; n++) {
|
|
211
|
+
pushUniquePath(roots, npmRoots[n]);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
var openclawLocator = process.platform === 'win32' ? 'where' : 'which';
|
|
215
|
+
var openclawBins = collectCommandOutputLines(openclawLocator, ['openclaw']);
|
|
216
|
+
for (var b = 0; b < openclawBins.length; b++) {
|
|
217
|
+
var binPath = openclawBins[b];
|
|
218
|
+
try {
|
|
219
|
+
pushUniquePath(roots, path.join(path.dirname(binPath), '..', 'lib', 'node_modules'));
|
|
220
|
+
} catch (e) {}
|
|
221
|
+
try {
|
|
222
|
+
var realBin = fs.realpathSync(binPath);
|
|
223
|
+
var needle = path.join('node_modules', 'openclaw');
|
|
224
|
+
var idx = realBin.lastIndexOf(needle);
|
|
225
|
+
if (idx >= 0) {
|
|
226
|
+
pushUniquePath(roots, realBin.substring(0, idx + 'node_modules'.length));
|
|
227
|
+
}
|
|
228
|
+
} catch (e) {}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return roots;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function addGlobalOpenclawTargets(deployTargets, assetTargets) {
|
|
235
|
+
var roots = collectGlobalNodeModuleRoots();
|
|
236
|
+
for (var i = 0; i < roots.length; i++) {
|
|
237
|
+
var root = roots[i];
|
|
238
|
+
pushUniquePath(deployTargets, path.join(root, 'openclaw', 'dist', 'control-ui', 'openagent-override.js'));
|
|
239
|
+
pushUniquePath(deployTargets, path.join(root, 'openclaw-openagent', 'src', 'plugin-ui', 'assets', 'openagent-override.js'));
|
|
240
|
+
pushUniquePath(deployTargets, path.join(root, 'openclaw-openagent', 'dist', 'src', 'plugin-ui', 'assets', 'openagent-override.js'));
|
|
241
|
+
|
|
242
|
+
pushUniquePath(assetTargets, path.join(root, 'openclaw', 'dist', 'control-ui'));
|
|
243
|
+
pushUniquePath(assetTargets, path.join(root, 'openclaw-openagent', 'src', 'plugin-ui', 'assets'));
|
|
244
|
+
pushUniquePath(assetTargets, path.join(root, 'openclaw-openagent', 'dist', 'src', 'plugin-ui', 'assets'));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function updateControlUiIndex(controlUiDir, contentHash, hostVersion) {
|
|
249
|
+
var indexPath = path.join(controlUiDir, 'index.html');
|
|
250
|
+
if (!fs.existsSync(indexPath)) return false;
|
|
251
|
+
|
|
252
|
+
var html = fs.readFileSync(indexPath, 'utf-8');
|
|
253
|
+
var htmlMarker = '<!--openagent:html-script-tag-->';
|
|
254
|
+
var hostVersionAttr = hostVersion ? ' data-openagent-host-version="' + hostVersion + '"' : '';
|
|
255
|
+
var scriptTag = htmlMarker + '<script src="./openagent-override.js?v=' + contentHash + '"' + hostVersionAttr + '></script>';
|
|
256
|
+
var markedScriptPattern = /(?:\/\*openagent:html-script-tag\*\/|<!--openagent:html-script-tag-->)<script[^>]*openagent-override[^>]*><\/script>\n?\s*/g;
|
|
257
|
+
var oldScriptPattern = /<script[^>]*openagent-override[^>]*><\/script>\n?\s*/g;
|
|
258
|
+
var nextHtml;
|
|
259
|
+
|
|
260
|
+
if (/(?:\/\*openagent:html-script-tag\*\/|<!--openagent:html-script-tag-->)/.test(html)) {
|
|
261
|
+
nextHtml = html.replace(markedScriptPattern, scriptTag + '\n ');
|
|
262
|
+
if (nextHtml === html && !nextHtml.includes('openagent-override.js')) {
|
|
263
|
+
nextHtml = nextHtml.replace(/(?:\/\*openagent:html-script-tag\*\/|<!--openagent:html-script-tag-->)/, scriptTag + '\n ');
|
|
264
|
+
} else if (nextHtml === html && nextHtml.includes('openagent-override.js')) {
|
|
265
|
+
nextHtml = nextHtml.replace(oldScriptPattern, '').replace(/(?:\/\*openagent:html-script-tag\*\/|<!--openagent:html-script-tag-->)/, scriptTag + '\n ');
|
|
266
|
+
}
|
|
267
|
+
} else {
|
|
268
|
+
nextHtml = html.replace(oldScriptPattern, '');
|
|
269
|
+
var viteAnchor = '<script type="module" crossorigin src="./assets/index-';
|
|
270
|
+
var anchorIdx = nextHtml.indexOf(viteAnchor);
|
|
271
|
+
if (anchorIdx >= 0) {
|
|
272
|
+
nextHtml = nextHtml.substring(0, anchorIdx) + scriptTag + '\n ' + nextHtml.substring(anchorIdx);
|
|
273
|
+
} else {
|
|
274
|
+
nextHtml = nextHtml.replace('</head>', scriptTag + '\n</head>');
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (nextHtml && nextHtml !== html) {
|
|
279
|
+
fs.writeFileSync(indexPath, nextHtml, 'utf-8');
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
85
284
|
|
|
86
285
|
const HEADER = `/**
|
|
87
286
|
* OpenAgent UI Override — Auto-generated, do not edit directly!
|
|
@@ -122,6 +321,12 @@ function build() {
|
|
|
122
321
|
body += `// MODULE: ${file}\n`;
|
|
123
322
|
body += `// ════════════════════════════════════════════════════════════════\n\n`;
|
|
124
323
|
body += content.trimEnd() + '\n';
|
|
324
|
+
if (file === 'loader/shared-state.js') {
|
|
325
|
+
body += `\n// ════════════════════════════════════════════════════════════════\n`;
|
|
326
|
+
body += `// MODULE: host-adapter/selected\n`;
|
|
327
|
+
body += `// ════════════════════════════════════════════════════════════════\n\n`;
|
|
328
|
+
body += getHostAdapterBlock('') + '\n';
|
|
329
|
+
}
|
|
125
330
|
}
|
|
126
331
|
|
|
127
332
|
if (errors > 0) {
|
|
@@ -133,11 +338,12 @@ function build() {
|
|
|
133
338
|
// ── 不做构建时版本注入,由运行时 DOM/CSS 特征检测 ──
|
|
134
339
|
// (build.cjs 无法知道实际运行的 OpenClaw 版本)
|
|
135
340
|
var output = HEADER + body + FOOTER;
|
|
341
|
+
var assetOutput = renderOverrideContent(output, '');
|
|
136
342
|
|
|
137
343
|
fs.mkdirSync(path.dirname(outFile), { recursive: true });
|
|
138
|
-
fs.writeFileSync(outFile,
|
|
344
|
+
fs.writeFileSync(outFile, assetOutput, 'utf-8');
|
|
139
345
|
|
|
140
|
-
const lineCount =
|
|
346
|
+
const lineCount = assetOutput.split('\n').length;
|
|
141
347
|
console.log(`✅ Built: ${path.relative(process.cwd(), outFile)} (${lineCount} lines)`);
|
|
142
348
|
|
|
143
349
|
// ── Auto-deploy to runtime targets ──────────────────────────────────────
|
|
@@ -162,19 +368,24 @@ function build() {
|
|
|
162
368
|
path.join(homedir, '.nvm/versions/node/v22.21.1/lib/node_modules/openclaw-openagent/src/plugin-ui/assets/openagent-override.js'),
|
|
163
369
|
path.join(homedir, '.nvm/versions/node/v22.21.1/lib/node_modules/openclaw-openagent/dist/src/plugin-ui/assets/openagent-override.js'),
|
|
164
370
|
];
|
|
165
|
-
|
|
166
|
-
// 动态发现 control-ui 目录(通过 node 可执行文件路径推断)
|
|
167
|
-
try {
|
|
168
|
-
const nodeDir = path.dirname(process.execPath);
|
|
169
|
-
const controlUiCandidate = path.join(nodeDir, '..', 'lib', 'node_modules', 'openclaw', 'dist', 'control-ui', 'openagent-override.js');
|
|
170
|
-
DEPLOY_TARGETS.push(path.resolve(controlUiCandidate));
|
|
171
|
-
} catch {}
|
|
371
|
+
addGlobalOpenclawTargets(DEPLOY_TARGETS, null);
|
|
172
372
|
|
|
173
373
|
let deployed = 0;
|
|
174
374
|
for (const target of DEPLOY_TARGETS) {
|
|
175
375
|
try {
|
|
176
376
|
if (fs.existsSync(path.dirname(target))) {
|
|
177
|
-
|
|
377
|
+
const targetDir = path.dirname(target);
|
|
378
|
+
const hostVersion = path.basename(targetDir) === 'control-ui'
|
|
379
|
+
? resolveControlUiHostVersion(targetDir)
|
|
380
|
+
: '';
|
|
381
|
+
const targetOutput = renderOverrideContent(output, hostVersion);
|
|
382
|
+
fs.writeFileSync(target, targetOutput, 'utf-8');
|
|
383
|
+
if (hostVersion) {
|
|
384
|
+
const contentHash = hashContent(targetOutput);
|
|
385
|
+
if (updateControlUiIndex(targetDir, contentHash, hostVersion)) {
|
|
386
|
+
console.log(` ↻ updated index.html cache-bust (${hostVersion}, ${resolveHostAdapterId(hostVersion)}, ${contentHash})`);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
178
389
|
console.log(` → ${path.relative(homedir, target)}`);
|
|
179
390
|
deployed++;
|
|
180
391
|
}
|
|
@@ -200,11 +411,7 @@ function build() {
|
|
|
200
411
|
path.join(homedir, '.nvm/versions/node/v22.21.1/lib/node_modules/openclaw-openagent/src/plugin-ui/assets'),
|
|
201
412
|
path.join(homedir, '.nvm/versions/node/v22.21.1/lib/node_modules/openclaw-openagent/dist/src/plugin-ui/assets'),
|
|
202
413
|
];
|
|
203
|
-
|
|
204
|
-
try {
|
|
205
|
-
const nodeDir = path.dirname(process.execPath);
|
|
206
|
-
ASSET_DEPLOY_TARGETS.push(path.resolve(path.join(nodeDir, '..', 'lib', 'node_modules', 'openclaw', 'dist', 'control-ui')));
|
|
207
|
-
} catch {}
|
|
414
|
+
addGlobalOpenclawTargets(null, ASSET_DEPLOY_TARGETS);
|
|
208
415
|
|
|
209
416
|
// ── Deploy image assets (icon.png, bg.png) ──
|
|
210
417
|
const IMAGE_ASSETS = [
|
|
@@ -237,16 +444,20 @@ const isWatchMode = process.argv.includes('--watch');
|
|
|
237
444
|
build();
|
|
238
445
|
|
|
239
446
|
if (isWatchMode) {
|
|
240
|
-
console.log(`\n👀 Watching for changes in ${path.relative(process.cwd(), modulesDir)}...`);
|
|
447
|
+
console.log(`\n👀 Watching for changes in ${path.relative(process.cwd(), modulesDir)} and ${path.relative(process.cwd(), adaptersDir)}...`);
|
|
241
448
|
|
|
242
449
|
let debounceTimer;
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
450
|
+
function watchSourceDir(sourceDir) {
|
|
451
|
+
fs.watch(sourceDir, { recursive: true }, (eventType, filename) => {
|
|
452
|
+
// Ignore dot files and non-js files
|
|
453
|
+
if (!filename || filename.startsWith('.') || !filename.endsWith('.js')) return;
|
|
246
454
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
455
|
+
clearTimeout(debounceTimer);
|
|
456
|
+
debounceTimer = setTimeout(() => {
|
|
457
|
+
build();
|
|
458
|
+
}, 100);
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
watchSourceDir(modulesDir);
|
|
462
|
+
watchSourceDir(adaptersDir);
|
|
252
463
|
}
|
package/src/plugin-ui/index.ts
CHANGED
|
@@ -13,6 +13,6 @@ import { loadUIExtensions } from './ui-extension-loader/index.js';
|
|
|
13
13
|
* 由 index.ts register() 调用。内部通过 Manifest Reconciler
|
|
14
14
|
* 自动完成扩展点的 apply / revert / skip。
|
|
15
15
|
*/
|
|
16
|
-
export async function injectControlUiOverride(): Promise<void> {
|
|
17
|
-
await loadUIExtensions();
|
|
16
|
+
export async function injectControlUiOverride(hostVersion?: string): Promise<void> {
|
|
17
|
+
await loadUIExtensions({ hostVersion });
|
|
18
18
|
}
|
|
@@ -6,6 +6,7 @@ let panel = null;
|
|
|
6
6
|
let backdrop = null;
|
|
7
7
|
|
|
8
8
|
let inputWrapperRef = null; // 保存输入框包裹层引用
|
|
9
|
+
let panelPositionResizeObserver = null;
|
|
9
10
|
|
|
10
11
|
// ── 键盘导航状态(§1.5)──
|
|
11
12
|
// 对标 TIM index.ts:13 "let selectedIndex: number = 0"
|
|
@@ -40,7 +41,7 @@ panel.className = 'openagent-agent-panel';
|
|
|
40
41
|
panel.innerHTML = `
|
|
41
42
|
<div class="openagent-panel-head">
|
|
42
43
|
<span class="openagent-panel-title">
|
|
43
|
-
<svg class="openagent-panel-title-icon" width="
|
|
44
|
+
<svg class="openagent-panel-title-icon" width="24" height="24" viewBox="0 0 56 56" fill="none">
|
|
44
45
|
<path d="M28 25H32C34.2091 25 36 26.7909 36 29V39C36 41.2091 34.2091 43 32 43H14C11.7909 43 10 41.2091 10 39V29C10 26.7909 11.7909 25 14 25H15" stroke="#12CCF6" stroke-width="3.3" stroke-linecap="round"/>
|
|
45
46
|
<path d="M39 31H42C44.2091 31 46 29.2091 46 27V17C46 14.7909 44.2091 13 42 13H24C21.7909 13 20 14.7909 20 17V27C20 29.2091 21.7909 31 24 31H30.0652C30.236 31 30.3282 31.2004 30.217 31.3302L27.5 34.5" stroke="#12CCF6" stroke-width="3.3" stroke-linecap="round"/>
|
|
46
47
|
<path d="M27 25.5L32.5 21" stroke="#12CCF6" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
|
@@ -83,12 +84,71 @@ const width = Math.min(rect.width, window.innerWidth - viewportPadding * 2);
|
|
|
83
84
|
const left = Math.max(viewportPadding, Math.min(rect.left, window.innerWidth - width - viewportPadding));
|
|
84
85
|
panel.style.left = left + 'px';
|
|
85
86
|
panel.style.width = width + 'px';
|
|
86
|
-
|
|
87
|
+
const toolbar = _findPanelToolbar();
|
|
88
|
+
const isComposerWrapper = inputWrapperRef.classList
|
|
89
|
+
&& inputWrapperRef.classList.contains('agent-chat__composer-combobox');
|
|
90
|
+
if (isComposerWrapper) {
|
|
91
|
+
panel.style.bottom = (_readElementHeight(inputWrapperRef) + _readElementHeight(toolbar) + 35) + 'px';
|
|
92
|
+
} else {
|
|
93
|
+
panel.style.bottom = (window.innerHeight - rect.top + 8) + 'px';
|
|
94
|
+
}
|
|
87
95
|
}
|
|
88
96
|
|
|
89
97
|
/** resize 时实时跟随输入框 */
|
|
90
98
|
function _onResize() { _positionPanel(); }
|
|
91
99
|
|
|
100
|
+
function _findPanelToolbar() {
|
|
101
|
+
if (!inputWrapperRef) return null;
|
|
102
|
+
var chatInput = inputWrapperRef.closest && inputWrapperRef.closest('.agent-chat__input');
|
|
103
|
+
if (chatInput) {
|
|
104
|
+
var toolbar = chatInput.querySelector('.agent-chat__toolbar');
|
|
105
|
+
if (toolbar) return toolbar;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
var parentEl = inputWrapperRef.parentElement;
|
|
109
|
+
if (parentEl) {
|
|
110
|
+
for (var i = 0; i < parentEl.children.length; i++) {
|
|
111
|
+
var child = parentEl.children[i];
|
|
112
|
+
if (child !== inputWrapperRef && child.className && String(child.className).indexOf('agent-chat__toolbar') !== -1) {
|
|
113
|
+
return child;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function _readElementHeight(el) {
|
|
121
|
+
if (!el) return 0;
|
|
122
|
+
var rect = el.getBoundingClientRect ? el.getBoundingClientRect() : null;
|
|
123
|
+
if (rect && rect.height > 0) return rect.height;
|
|
124
|
+
return typeof el.offsetHeight === 'number' ? el.offsetHeight : 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function _schedulePanelPosition() {
|
|
128
|
+
window.requestAnimationFrame
|
|
129
|
+
? window.requestAnimationFrame(_positionPanel)
|
|
130
|
+
: setTimeout(_positionPanel, 0);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function _installPanelPositionObservers() {
|
|
134
|
+
if (panelPositionResizeObserver) {
|
|
135
|
+
panelPositionResizeObserver.disconnect();
|
|
136
|
+
panelPositionResizeObserver = null;
|
|
137
|
+
}
|
|
138
|
+
if (typeof ResizeObserver !== 'function' || !inputWrapperRef) return;
|
|
139
|
+
|
|
140
|
+
panelPositionResizeObserver = new ResizeObserver(_schedulePanelPosition);
|
|
141
|
+
panelPositionResizeObserver.observe(inputWrapperRef);
|
|
142
|
+
var toolbar = _findPanelToolbar();
|
|
143
|
+
if (toolbar) panelPositionResizeObserver.observe(toolbar);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function _teardownPanelPositionObservers() {
|
|
147
|
+
if (!panelPositionResizeObserver) return;
|
|
148
|
+
panelPositionResizeObserver.disconnect();
|
|
149
|
+
panelPositionResizeObserver = null;
|
|
150
|
+
}
|
|
151
|
+
|
|
92
152
|
// 搜索状态管理
|
|
93
153
|
let currentAbort = null; // AbortController for in-flight search
|
|
94
154
|
let searchDebounceId = null;
|
|
@@ -115,6 +175,7 @@ if (oldEmpty) oldEmpty.remove();
|
|
|
115
175
|
|
|
116
176
|
const p = ensurePanel();
|
|
117
177
|
_positionPanel(); // 动态定位到 input 上方
|
|
178
|
+
_installPanelPositionObservers();
|
|
118
179
|
p.classList.add('visible');
|
|
119
180
|
backdrop.classList.add('visible');
|
|
120
181
|
window.addEventListener('resize', _onResize);
|
|
@@ -177,6 +238,7 @@ function closePanel() {
|
|
|
177
238
|
if (panel) panel.classList.remove('visible');
|
|
178
239
|
if (backdrop) backdrop.classList.remove('visible');
|
|
179
240
|
window.removeEventListener('resize', _onResize);
|
|
241
|
+
_teardownPanelPositionObservers();
|
|
180
242
|
// 清理搜索状态
|
|
181
243
|
if (currentAbort) { currentAbort.abort(); currentAbort = null; }
|
|
182
244
|
if (searchDebounceId) { clearTimeout(searchDebounceId); searchDebounceId = null; }
|
|
@@ -231,7 +293,6 @@ function _scrollItemIntoView(index) {
|
|
|
231
293
|
|
|
232
294
|
function _appendParentSection(container, parentTitle, children, textarea, renderedAgents) {
|
|
233
295
|
const parentSec = _el('section', 'openagent-parent-section');
|
|
234
|
-
parentSec.appendChild(_text('div', 'openagent-parent-heading', parentTitle));
|
|
235
296
|
children.forEach(function(child) {
|
|
236
297
|
if (child.agents && child.agents.length > 0) {
|
|
237
298
|
_appendAgentSection(parentSec, child.title, child.agents, textarea, renderedAgents);
|
|
@@ -306,7 +367,12 @@ function _dismissEmptyToast() {
|
|
|
306
367
|
}
|
|
307
368
|
|
|
308
369
|
/** 独立浮层空状态(不在面板内) */
|
|
309
|
-
function
|
|
370
|
+
function _getEmptyMessage(query) {
|
|
371
|
+
return CL.agentBookLastError ||
|
|
372
|
+
(query ? '暂未匹配到您想要的Agent 我们将持续上新~' : '暂无在线 Agent,请输入关键词搜索');
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function _showEmptyToast(query, message) {
|
|
310
376
|
_dismissEmptyToast();
|
|
311
377
|
var toast = _el('div', 'openagent-empty-toast');
|
|
312
378
|
|
|
@@ -316,8 +382,7 @@ function _showEmptyToast(query) {
|
|
|
316
382
|
'</svg>';
|
|
317
383
|
toast.appendChild(iconWrap);
|
|
318
384
|
|
|
319
|
-
toast.appendChild(_text('span', 'openagent-empty-state__text',
|
|
320
|
-
query ? '暂未匹配到您想要的Agent 我们将持续上新~' : '暂无在线 Agent,请输入关键词搜索'));
|
|
385
|
+
toast.appendChild(_text('span', 'openagent-empty-state__text', message || _getEmptyMessage(query)));
|
|
321
386
|
|
|
322
387
|
var closeBtn = _el('button', 'openagent-empty-state__close');
|
|
323
388
|
closeBtn.innerHTML = '<svg width="12" height="12" viewBox="0 0 12 12" fill="none">' +
|
|
@@ -333,12 +398,17 @@ function _showEmptyToast(query) {
|
|
|
333
398
|
toast.style.left = Math.max(16, rect.left) + 'px';
|
|
334
399
|
toast.style.width = Math.min(rect.width, window.innerWidth - 32) + 'px';
|
|
335
400
|
toast.style.bottom = (window.innerHeight - rect.top + 8) + 'px';
|
|
401
|
+
// ── V2 版本兼容:v1 bottom 固定 117px ──
|
|
402
|
+
var isV2Toast = typeof _clIsHostVersionGte === 'function' && _clIsHostVersionGte('2026.6.9');
|
|
403
|
+
if (!isV2Toast) {
|
|
404
|
+
toast.style.bottom = '117px';
|
|
405
|
+
}
|
|
336
406
|
toast.style.zIndex = '100000';
|
|
337
407
|
}
|
|
338
408
|
document.body.appendChild(toast);
|
|
339
409
|
}
|
|
340
410
|
|
|
341
|
-
function _createEmptyState(query) {
|
|
411
|
+
function _createEmptyState(query, message) {
|
|
342
412
|
var wrapper = _el('div', 'openagent-empty-state');
|
|
343
413
|
var row = _el('div', 'openagent-empty-state__row');
|
|
344
414
|
|
|
@@ -348,8 +418,7 @@ function _createEmptyState(query) {
|
|
|
348
418
|
'</svg>';
|
|
349
419
|
row.appendChild(iconWrap);
|
|
350
420
|
|
|
351
|
-
var text = _text('span', 'openagent-empty-state__text',
|
|
352
|
-
query ? '暂未匹配到您想要的Agent 我们将持续上新~' : '暂无在线 Agent,请输入关键词搜索');
|
|
421
|
+
var text = _text('span', 'openagent-empty-state__text', message || _getEmptyMessage(query));
|
|
353
422
|
row.appendChild(text);
|
|
354
423
|
|
|
355
424
|
var closeBtn = _el('button', 'openagent-empty-state__close');
|
|
@@ -367,10 +436,11 @@ function _createEmptyState(query) {
|
|
|
367
436
|
|
|
368
437
|
function renderAgentList(container, agents, textarea, query) {
|
|
369
438
|
if (!agents.length) {
|
|
439
|
+
var emptyMessage = _getEmptyMessage(query);
|
|
370
440
|
_currentAgents = [];
|
|
371
441
|
_currentTextarea = textarea;
|
|
372
442
|
closePanel();
|
|
373
|
-
_showEmptyToast(query);
|
|
443
|
+
_showEmptyToast(query, emptyMessage);
|
|
374
444
|
return;
|
|
375
445
|
}
|
|
376
446
|
|
|
@@ -407,6 +477,19 @@ _highlightItem(0);
|
|
|
407
477
|
* 对齐 openagent-frontend NewChatPanel L92-105:
|
|
408
478
|
* 找到最后一个 @,替换其后内容为 @agentName
|
|
409
479
|
*/
|
|
480
|
+
function _pickMentionAgentDescription(agent) {
|
|
481
|
+
if (!agent || typeof agent !== 'object') return '';
|
|
482
|
+
if (CL && typeof CL.pickAgentIdentityDescription === 'function') {
|
|
483
|
+
return CL.pickAgentIdentityDescription(agent);
|
|
484
|
+
}
|
|
485
|
+
const keys = ['agent_bio', 'agentBio', 'bio', 'description', 'desc', 'agent_description', 'agentDescription', 'MOM', 'mom'];
|
|
486
|
+
for (let i = 0; i < keys.length; i++) {
|
|
487
|
+
const value = agent[keys[i]];
|
|
488
|
+
if (typeof value === 'string' && value.trim()) return value.trim();
|
|
489
|
+
}
|
|
490
|
+
return '';
|
|
491
|
+
}
|
|
492
|
+
|
|
410
493
|
function insertMention(textarea, agentName, agentData) {
|
|
411
494
|
// 重新查找 textarea(Lit 可能重新渲染导致引用失效)
|
|
412
495
|
const liveTextarea = document.querySelector(
|
|
@@ -426,9 +509,11 @@ liveTextarea.focus();
|
|
|
426
509
|
const agentId = agentData?.agent_id || agentData?.agentId || agentData?.id || agentData?.tag ||
|
|
427
510
|
agentData?.user_id || agentData?.userId || agentData?.identifier || agentData?.tim_user_id || agentData?.timUserId || '';
|
|
428
511
|
const agentAvatar = agentData?.avatar || agentData?.avatar_url || agentData?.avatarUrl || agentData?.image || agentData?.logo || agentData?.photo || null;
|
|
512
|
+
const agentBio = _pickMentionAgentDescription(agentData);
|
|
429
513
|
console.log('[cl-diag][mention-select]', {
|
|
430
514
|
agentName,
|
|
431
515
|
resolvedAgentId: agentId || null,
|
|
516
|
+
hasAgentBio: !!agentBio,
|
|
432
517
|
rawKeys: Object.keys(agentData || {}),
|
|
433
518
|
rawAgent: agentData,
|
|
434
519
|
});
|
|
@@ -437,9 +522,14 @@ if (!agentId) {
|
|
|
437
522
|
console.warn('[openagent] mention skipped: missing agent identifier for', agentName, agentData);
|
|
438
523
|
return;
|
|
439
524
|
}
|
|
440
|
-
CL.pendingMention = { agent_id: agentId, name: agentName, avatar: agentAvatar };
|
|
525
|
+
CL.pendingMention = { agent_id: agentId, name: agentName, avatar: agentAvatar, agent_bio: agentBio };
|
|
441
526
|
if (CL && typeof CL.storeAgentIdentity === 'function') {
|
|
442
|
-
CL.storeAgentIdentity({
|
|
527
|
+
CL.storeAgentIdentity(Object.assign({}, agentData || {}, {
|
|
528
|
+
agent_id: agentId,
|
|
529
|
+
name: agentName,
|
|
530
|
+
avatar: agentAvatar,
|
|
531
|
+
agent_bio: agentBio,
|
|
532
|
+
}));
|
|
443
533
|
}
|
|
444
534
|
console.log('[openagent] mention stored:', CL.pendingMention);
|
|
445
535
|
|