echoclaw-relay-agent 0.33.2 → 0.33.4
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/{chunk-BCFYE35W.js → chunk-HDBJBULU.js} +163 -16
- package/dist/cli.js +1 -6
- package/dist/core/AgentCore.d.ts +0 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -3094,7 +3094,7 @@ var API_CAPABILITY_MAP = [
|
|
|
3094
3094
|
{ pattern: /navigator\.geolocation/g, capability: "location", label: "navigator.geolocation" },
|
|
3095
3095
|
{ pattern: /getDisplayMedia\s*\(/g, capability: "media_capture", label: "getDisplayMedia()" },
|
|
3096
3096
|
{ pattern: /requestFullscreen\s*\(/g, capability: "fullscreen", label: "requestFullscreen()" },
|
|
3097
|
-
{ pattern: /new\s+Audio\
|
|
3097
|
+
{ pattern: /new\s+Audio\b/g, capability: "audio", label: "new Audio()" },
|
|
3098
3098
|
{ pattern: /<audio[\s>]/gi, capability: "audio", label: "<audio>" },
|
|
3099
3099
|
{ pattern: /AudioContext/g, capability: "audio", label: "AudioContext" },
|
|
3100
3100
|
// Bridge APIs → capability
|
|
@@ -3109,9 +3109,9 @@ var PROMPT_TIER_CAPABILITIES = new Set(
|
|
|
3109
3109
|
Object.entries(CAPABILITY_TIERS).filter(([, tier]) => tier === "prompt").map(([cap]) => cap)
|
|
3110
3110
|
);
|
|
3111
3111
|
var SIDEBAR_PATTERNS = [
|
|
3112
|
-
/(?:class|id)\s*=\s*["'][^"']*\b(?:left-panel|left-sidebar|left-nav|left-sidenav)\b[^"']*["']/gi,
|
|
3113
|
-
/\.(?:left-panel|left-sidebar|left-nav|left-sidenav)\b/gi,
|
|
3114
|
-
/#(?:left-panel|left-sidebar|left-nav|left-sidenav)\b/gi
|
|
3112
|
+
/(?:class|id)\s*=\s*["'][^"']*\b(?:left-panel|left-sidebar|left-nav|left-sidenav|sidebar-left|panel-left|nav-left)\b[^"']*["']/gi,
|
|
3113
|
+
/\.(?:left-panel|left-sidebar|left-nav|left-sidenav|sidebar-left|panel-left|nav-left)\b/gi,
|
|
3114
|
+
/#(?:left-panel|left-sidebar|left-nav|left-sidenav|sidebar-left|panel-left|nav-left)\b/gi
|
|
3115
3115
|
];
|
|
3116
3116
|
function validateClawHtml(html, manifest, options) {
|
|
3117
3117
|
const phase = options?.phase ?? "hard";
|
|
@@ -3133,6 +3133,13 @@ function validateClawHtml(html, manifest, options) {
|
|
|
3133
3133
|
if (/d\s*=\s*["'][^"']*$/.test(before)) continue;
|
|
3134
3134
|
if (/href\s*=\s*["']$/.test(before)) continue;
|
|
3135
3135
|
if (/id\s*=\s*["']$/.test(before)) continue;
|
|
3136
|
+
if (!isInStyleAttribute(html, hexMatch.index) && !isInScriptBlock(html, hexMatch.index)) {
|
|
3137
|
+
const afterHex = html.slice(hexMatch.index + fullHex.length, hexMatch.index + fullHex.length + 60);
|
|
3138
|
+
if (/^\s*[\{,\s:>+~\[]/.test(afterHex) || /^\s*$/.test(afterHex)) {
|
|
3139
|
+
const beforeCtx = html.slice(Math.max(0, hexMatch.index - 10), hexMatch.index);
|
|
3140
|
+
if (!/[:;]\s*$/.test(beforeCtx)) continue;
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3136
3143
|
const beforeFull = html.slice(Math.max(0, hexMatch.index - 500), hexMatch.index);
|
|
3137
3144
|
const lastCommentOpen = beforeFull.lastIndexOf("<!--");
|
|
3138
3145
|
const lastCommentClose = beforeFull.lastIndexOf("-->");
|
|
@@ -3241,6 +3248,36 @@ function validateClawHtml(html, manifest, options) {
|
|
|
3241
3248
|
line: lineAt(html, linkMatch.index)
|
|
3242
3249
|
});
|
|
3243
3250
|
}
|
|
3251
|
+
const fetchUrlPattern = /\bfetch\s*\(\s*["'`]((?:https?:)?\/\/[^\s"'`]+)["'`]/gi;
|
|
3252
|
+
let fetchMatch;
|
|
3253
|
+
while ((fetchMatch = fetchUrlPattern.exec(stripped)) !== null) {
|
|
3254
|
+
const url = fetchMatch[1];
|
|
3255
|
+
if (/^(?:https?:)?\/\/(?:localhost|127\.0\.0\.1)\b/.test(url)) continue;
|
|
3256
|
+
if (isDomainAllowed(url)) continue;
|
|
3257
|
+
externalUrlCount++;
|
|
3258
|
+
errors.push({
|
|
3259
|
+
rule: 3,
|
|
3260
|
+
severity: "critical",
|
|
3261
|
+
message: `External fetch("${snippet(url, 80)}") found in script. Apps must be self-contained or declare network capability with network_allow.`,
|
|
3262
|
+
match: snippet(fetchMatch[0]),
|
|
3263
|
+
line: lineAt(html, fetchMatch.index)
|
|
3264
|
+
});
|
|
3265
|
+
}
|
|
3266
|
+
const xhrOpenPattern = /\.open\s*\(\s*["'][A-Z]+["']\s*,\s*["']((?:https?:)?\/\/[^\s"']+)["']/gi;
|
|
3267
|
+
let xhrMatch;
|
|
3268
|
+
while ((xhrMatch = xhrOpenPattern.exec(stripped)) !== null) {
|
|
3269
|
+
const url = xhrMatch[1];
|
|
3270
|
+
if (/^(?:https?:)?\/\/(?:localhost|127\.0\.0\.1)\b/.test(url)) continue;
|
|
3271
|
+
if (isDomainAllowed(url)) continue;
|
|
3272
|
+
externalUrlCount++;
|
|
3273
|
+
errors.push({
|
|
3274
|
+
rule: 3,
|
|
3275
|
+
severity: "critical",
|
|
3276
|
+
message: `External XMLHttpRequest to "${snippet(url, 80)}" found. Apps must be self-contained or declare network capability with network_allow.`,
|
|
3277
|
+
match: snippet(xhrMatch[0]),
|
|
3278
|
+
line: lineAt(html, xhrMatch.index)
|
|
3279
|
+
});
|
|
3280
|
+
}
|
|
3244
3281
|
const detectedCaps = /* @__PURE__ */ new Set();
|
|
3245
3282
|
for (const { pattern, capability, label } of API_CAPABILITY_MAP) {
|
|
3246
3283
|
pattern.lastIndex = 0;
|
|
@@ -3353,6 +3390,13 @@ function validateClawHtml(html, manifest, options) {
|
|
|
3353
3390
|
});
|
|
3354
3391
|
}
|
|
3355
3392
|
if (declaredCaps.has("network") && manifest.network_allow) {
|
|
3393
|
+
if (manifest.network_allow.length > RESOURCE_LIMITS.NETWORK_ALLOW_MAX_ENTRIES) {
|
|
3394
|
+
errors.push({
|
|
3395
|
+
rule: 5,
|
|
3396
|
+
severity: "critical",
|
|
3397
|
+
message: `network_allow has ${manifest.network_allow.length} entries, max ${RESOURCE_LIMITS.NETWORK_ALLOW_MAX_ENTRIES}.`
|
|
3398
|
+
});
|
|
3399
|
+
}
|
|
3356
3400
|
for (const url of manifest.network_allow) {
|
|
3357
3401
|
const isWildcard = /^\*\.[a-zA-Z0-9][\w\-.]+$/.test(url);
|
|
3358
3402
|
if (!isWildcard && !/^(wss?|https?):\/\/.+/.test(url)) {
|
|
@@ -3385,7 +3429,10 @@ function validateClawHtml(html, manifest, options) {
|
|
|
3385
3429
|
// Bare location.* patterns — negative lookbehind excludes window. prefix to avoid double-reporting
|
|
3386
3430
|
{ pattern: /(?<!window\.)location\.href\s*=/g, fix: "use in-app routing (history.pushState, show/hide views)", label: "location.href =" },
|
|
3387
3431
|
{ pattern: /(?<!window\.)location\.assign\s*\(/g, fix: "use in-app routing (history.pushState, show/hide views)", label: "location.assign()" },
|
|
3388
|
-
{ pattern: /(?<!window\.)location\.replace\s*\(/g, fix: "use in-app routing (history.pushState, show/hide views)", label: "location.replace()" }
|
|
3432
|
+
{ pattern: /(?<!window\.)location\.replace\s*\(/g, fix: "use in-app routing (history.pushState, show/hide views)", label: "location.replace()" },
|
|
3433
|
+
// setTimeout/setInterval with string argument — equivalent to eval (blocked by CSP)
|
|
3434
|
+
{ pattern: /setTimeout\s*\(\s*["']/g, fix: "pass a function reference to setTimeout, not a string", label: 'setTimeout("string")' },
|
|
3435
|
+
{ pattern: /setInterval\s*\(\s*["']/g, fix: "pass a function reference to setInterval, not a string", label: 'setInterval("string")' }
|
|
3389
3436
|
];
|
|
3390
3437
|
for (const { pattern, fix, label } of FORBIDDEN_DIRECT_APIS) {
|
|
3391
3438
|
pattern.lastIndex = 0;
|
|
@@ -3473,6 +3520,110 @@ function validateClawHtml(html, manifest, options) {
|
|
|
3473
3520
|
});
|
|
3474
3521
|
}
|
|
3475
3522
|
}
|
|
3523
|
+
if (/async\s+function\s+render\s*\(/.test(html)) {
|
|
3524
|
+
const m = html.match(/async\s+function\s+render\s*\(/);
|
|
3525
|
+
const line = m ? html.substring(0, m.index).split("\n").length : void 0;
|
|
3526
|
+
errors.push({
|
|
3527
|
+
rule: 18,
|
|
3528
|
+
severity: "critical",
|
|
3529
|
+
line,
|
|
3530
|
+
message: "render() must be synchronous. The EC Runtime does NOT await it \u2014 async render causes race conditions.",
|
|
3531
|
+
suggestion: "Remove async keyword: `function render() { ... }`. Move async operations outside render.",
|
|
3532
|
+
autoFixable: false
|
|
3533
|
+
});
|
|
3534
|
+
}
|
|
3535
|
+
{
|
|
3536
|
+
const renderMatch = html.match(/function\s+render\s*\([^)]*\)\s*\{/);
|
|
3537
|
+
if (renderMatch && renderMatch.index !== void 0) {
|
|
3538
|
+
let depth = 0;
|
|
3539
|
+
let start = renderMatch.index + renderMatch[0].length - 1;
|
|
3540
|
+
let end = start;
|
|
3541
|
+
for (let i = start; i < html.length; i++) {
|
|
3542
|
+
if (html[i] === "{") depth++;
|
|
3543
|
+
else if (html[i] === "}") {
|
|
3544
|
+
depth--;
|
|
3545
|
+
if (depth === 0) {
|
|
3546
|
+
end = i;
|
|
3547
|
+
break;
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3550
|
+
}
|
|
3551
|
+
const body = html.substring(start, end);
|
|
3552
|
+
if (/setInterval\s*\(/.test(body)) {
|
|
3553
|
+
errors.push({
|
|
3554
|
+
rule: 19,
|
|
3555
|
+
severity: "warning",
|
|
3556
|
+
message: "setInterval() inside render() \u2014 intervals accumulate on every re-render. Move outside render or clear previous interval first.",
|
|
3557
|
+
autoFixable: false
|
|
3558
|
+
});
|
|
3559
|
+
}
|
|
3560
|
+
if (/addEventListener\s*\(/.test(body)) {
|
|
3561
|
+
errors.push({
|
|
3562
|
+
rule: 19,
|
|
3563
|
+
severity: "warning",
|
|
3564
|
+
message: "addEventListener() inside render() \u2014 listeners accumulate on every re-render. Move to init() or use named functions.",
|
|
3565
|
+
autoFixable: false
|
|
3566
|
+
});
|
|
3567
|
+
}
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
{
|
|
3571
|
+
const lsPattern = /localStorage\.(setItem|getItem|removeItem)\s*\(/g;
|
|
3572
|
+
let lsMatch;
|
|
3573
|
+
while ((lsMatch = lsPattern.exec(html)) !== null) {
|
|
3574
|
+
if (isInScriptBlock(html, lsMatch.index)) {
|
|
3575
|
+
const line = html.substring(0, lsMatch.index).split("\n").length;
|
|
3576
|
+
errors.push({
|
|
3577
|
+
rule: 20,
|
|
3578
|
+
severity: "warning",
|
|
3579
|
+
line,
|
|
3580
|
+
match: lsMatch[0],
|
|
3581
|
+
message: "Direct localStorage is not app-scoped and may collide. Use initState/mutateState or echoclaw.db instead.",
|
|
3582
|
+
autoFixable: false
|
|
3583
|
+
});
|
|
3584
|
+
break;
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
if (/JSON\.parse\(\s*\w+\.body\s*\)/.test(html)) {
|
|
3589
|
+
errors.push({
|
|
3590
|
+
rule: 21,
|
|
3591
|
+
severity: "warning",
|
|
3592
|
+
message: "Bridge fetch response.body is already parsed (object for JSON). Do NOT JSON.parse(res.body) \u2014 use res.body directly.",
|
|
3593
|
+
autoFixable: false
|
|
3594
|
+
});
|
|
3595
|
+
}
|
|
3596
|
+
{
|
|
3597
|
+
const stateOverride = /\b(var|let|const)\s+state\s*=/.exec(html);
|
|
3598
|
+
if (stateOverride && isInScriptBlock(html, stateOverride.index)) {
|
|
3599
|
+
const line = html.substring(0, stateOverride.index).split("\n").length;
|
|
3600
|
+
errors.push({
|
|
3601
|
+
rule: 22,
|
|
3602
|
+
severity: "critical",
|
|
3603
|
+
line,
|
|
3604
|
+
match: stateOverride[0],
|
|
3605
|
+
message: "`state` is a reserved EC Runtime global. Declaring it with var/let/const shadows or conflicts with window.state.",
|
|
3606
|
+
suggestion: "Use a different variable name (e.g., appState, localState).",
|
|
3607
|
+
autoFixable: false
|
|
3608
|
+
});
|
|
3609
|
+
}
|
|
3610
|
+
}
|
|
3611
|
+
{
|
|
3612
|
+
const cssVarPattern = /var\(--(?!ec-)(?:bg-|txt-|agent|border-|radius-|space-)/g;
|
|
3613
|
+
let cssMatch;
|
|
3614
|
+
while ((cssMatch = cssVarPattern.exec(html)) !== null) {
|
|
3615
|
+
if (isInStyleAttribute(html, cssMatch.index) || isInStyleBlock(html, cssMatch.index)) {
|
|
3616
|
+
errors.push({
|
|
3617
|
+
rule: 23,
|
|
3618
|
+
severity: "warning",
|
|
3619
|
+
match: cssMatch[0],
|
|
3620
|
+
message: `CSS variable missing --ec- prefix: ${cssMatch[0]}). Use --ec-bg-page, --ec-txt-primary, --ec-agent, etc.`,
|
|
3621
|
+
autoFixable: false
|
|
3622
|
+
});
|
|
3623
|
+
break;
|
|
3624
|
+
}
|
|
3625
|
+
}
|
|
3626
|
+
}
|
|
3476
3627
|
const autoFixableCount = errors.filter((e) => e.autoFixable === true).length;
|
|
3477
3628
|
const filteredErrors = phase === "soft" ? errors.filter((e) => e.autoFixable === true) : errors;
|
|
3478
3629
|
const hasCritical = filteredErrors.some((e) => e.severity === "critical");
|
|
@@ -4221,6 +4372,7 @@ I build and deliver; the client validates and monitors. If violations found, cli
|
|
|
4221
4372
|
| \`<!--CLAW_FEEDBACK:{"appId":"APP-<id>","code":"sandbox_violations","message":"..."}-->\` | Fix ONLY the listed violations \u2192 re-write files \u2192 emit CLAW_APP_READY. |
|
|
4222
4373
|
| \`<!--CLAW_FEEDBACK:{"appId":"APP-<id>","code":"sync_failed","message":"..."}-->\` | Package \`index.html\` + \`claw.json\` + \`session.json\` into a \`.claw\` JSON \u2192 output in \`<details><summary>Click to install</summary>\u2026</details>\`. |
|
|
4223
4374
|
| \`<!--CLAW_FEEDBACK:{"appId":"APP-<id>","code":"runtime_diagnostic","message":"..."}-->\` | \`message\` has \`csp_violations\`, \`js_errors\`, \`fetch_log\`, \`state_ops\`. Fix root cause \u2192 re-deliver. If platform bug (not my code) \u2192 output \`CLAW_BUG_REPORT\`. |
|
|
4375
|
+
| \`<!--CLAW_FEEDBACK:{"appId":"APP-<id>","code":"runtime_validation","message":"..."}-->\` | **Compiler errors** with line numbers + source snippets. App already installed \u2014 fix blockers \u2192 re-deliver as update. |
|
|
4224
4376
|
| Neither | Normal chat. Never output markers. |
|
|
4225
4377
|
|
|
4226
4378
|
Normal chat + user describes an app idea \u2192 "Use the Create App button in EchoClaw to get started."
|
|
@@ -4248,7 +4400,7 @@ Client responds with \`CLAW_FEEDBACK code="runtime_diagnostic"\`. Wait \u2014 do
|
|
|
4248
4400
|
**CLAW_BUG_REPORT** \u2014 Platform bug needing official fix (not app code issues):
|
|
4249
4401
|
\`<!--CLAW_BUG_REPORT:{"appId":"APP-<id>","issue_type":"client_rendering_bug","severity":"blocker","failure":"what went wrong","suggestion":"proposed fix"}-->\`
|
|
4250
4402
|
issue_type: client_rendering_bug, capability_not_implemented, api_inconsistency. severity: blocker, degraded, cosmetic.
|
|
4251
|
-
Only output CLAW_BUG_REPORT after exhausting
|
|
4403
|
+
Before requesting new capabilities, **verify existing APIs can't solve it** (bridge fetch, WebSocket, auth, SQLite, shared_libs, NPM deps). Only output CLAW_BUG_REPORT after exhausting existing capabilities AND confirming the issue is in the platform, not my code. When unsure, keep debugging \u2014 never blame the client prematurely.
|
|
4252
4404
|
|
|
4253
4405
|
Markers are progress signals, NOT stopping points. After CLAW_STEP, continue immediately. Entire build + deliver in ONE response. I decide design \u2014 no options offered, but confirm plan before building (create only).
|
|
4254
4406
|
|
|
@@ -5635,7 +5787,9 @@ var WorkspaceFileReader = class {
|
|
|
5635
5787
|
await this._atomicWrite(path4.join(dir, WORKSPACE_FILES.STATUS), "writing");
|
|
5636
5788
|
const files = splitPackageToFiles(pkg, status);
|
|
5637
5789
|
await this._atomicWrite(path4.join(dir, WORKSPACE_FILES.PACKAGE), files.packageJson);
|
|
5638
|
-
|
|
5790
|
+
if (files.html) {
|
|
5791
|
+
await this._atomicWrite(path4.join(dir, WORKSPACE_FILES.HTML), files.html);
|
|
5792
|
+
}
|
|
5639
5793
|
if (files.devlogJson) {
|
|
5640
5794
|
await this._atomicWrite(path4.join(dir, WORKSPACE_FILES.DEVLOG), files.devlogJson);
|
|
5641
5795
|
}
|
|
@@ -9419,9 +9573,8 @@ var SyncCommandHandler = class {
|
|
|
9419
9573
|
if (!appId || typeof appId !== "string") continue;
|
|
9420
9574
|
desktopAppIds.add(appId);
|
|
9421
9575
|
try {
|
|
9422
|
-
await this._reader.
|
|
9423
|
-
} catch
|
|
9424
|
-
console.warn(`[sync] full_sync skip appId=${appId}: ${err.message}`);
|
|
9576
|
+
await this._reader.writeAppStatus(appId, "published");
|
|
9577
|
+
} catch {
|
|
9425
9578
|
}
|
|
9426
9579
|
}
|
|
9427
9580
|
const workspaceAppIds = await this._reader.listApps();
|
|
@@ -9501,8 +9654,6 @@ var AgentCore = class {
|
|
|
9501
9654
|
_workspaceReader;
|
|
9502
9655
|
_syncCommandHandler = null;
|
|
9503
9656
|
_agentVersion;
|
|
9504
|
-
/** Called when desktop reports a different version than this agent. */
|
|
9505
|
-
onVersionMismatch = null;
|
|
9506
9657
|
/** Currently attached transport (Phase 1: single transport). */
|
|
9507
9658
|
_transport = null;
|
|
9508
9659
|
/** Workspace directory watcher for detecting new apps from OpenClaw. */
|
|
@@ -9835,10 +9986,6 @@ var AgentCore = class {
|
|
|
9835
9986
|
if (payloadType === "client_hello") {
|
|
9836
9987
|
const clientVer = payload.version || "unknown";
|
|
9837
9988
|
console.log(` [client_hello] desktop version=${clientVer}`);
|
|
9838
|
-
if (clientVer !== this._agentVersion && clientVer !== "unknown") {
|
|
9839
|
-
console.log(` [version] agent=${this._agentVersion} desktop=${clientVer} \u2014 versions differ`);
|
|
9840
|
-
this.onVersionMismatch?.(this._agentVersion, clientVer);
|
|
9841
|
-
}
|
|
9842
9989
|
return;
|
|
9843
9990
|
}
|
|
9844
9991
|
if (payloadType === "vm_start") {
|
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
AgentCore,
|
|
4
4
|
RelayAgent
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-HDBJBULU.js";
|
|
6
6
|
import "./chunk-STI2237I.js";
|
|
7
7
|
|
|
8
8
|
// src/service/platform.ts
|
|
@@ -1106,11 +1106,6 @@ async function runDaemon(relay, code, gateway, bridgePort, vmOpts) {
|
|
|
1106
1106
|
if (vmConfig) {
|
|
1107
1107
|
console.log(` [vm] enabled (backend=${vmConfig.backend ?? "docker"} image=${vmConfig.image ?? "default"})`);
|
|
1108
1108
|
}
|
|
1109
|
-
agentCore.onVersionMismatch = (_agentVer, clientVer) => {
|
|
1110
|
-
console.log(` [ota] desktop requests version ${clientVer} \u2014 triggering update`);
|
|
1111
|
-
updater.updateToVersion(clientVer).catch(() => {
|
|
1112
|
-
});
|
|
1113
|
-
};
|
|
1114
1109
|
updater.on("update_available", (info) => {
|
|
1115
1110
|
console.log(` ${GREEN}[ota]${RESET} update available: ${info.current} \u2192 ${info.latest}`);
|
|
1116
1111
|
});
|
package/dist/core/AgentCore.d.ts
CHANGED
|
@@ -21,8 +21,6 @@ export declare class AgentCore {
|
|
|
21
21
|
private readonly _workspaceReader;
|
|
22
22
|
private _syncCommandHandler;
|
|
23
23
|
private readonly _agentVersion;
|
|
24
|
-
/** Called when desktop reports a different version than this agent. */
|
|
25
|
-
onVersionMismatch: ((agentVersion: string, clientVersion: string) => void) | null;
|
|
26
24
|
/** Currently attached transport (Phase 1: single transport). */
|
|
27
25
|
private _transport;
|
|
28
26
|
/** Workspace directory watcher for detecting new apps from OpenClaw. */
|
package/dist/index.js
CHANGED
package/package.json
CHANGED