JS Eyes
Use this skill to turn a ClawHub-installed js-eyes bundle into a working OpenClaw browser automation stack.
Treat {baseDir} as the installed skill root. The plugin path that must be registered in OpenClaw is {baseDir}/openclaw-plugin, not {baseDir} itself.
Use This Skill When
- The user wants to install or configure JS Eyes from a ClawHub skill bundle.
js_eyes_*tools are missing after installation.- The browser extension is installed but still shows
Disconnected. - The user wants to verify the built-in server, plugin config, or extension connection.
- The user wants to discover or install JS Eyes extension skills after the base stack is working.
- The user wants to mount a custom / external extension skill (a directory outside the bundle that contains a
skill.contract.js) into a running OpenClaw, or to verify that such a skill is actually loaded. - The user wants to author a new extension skill from scratch — in that case, point them at the starter template and authoring guide (see
Authoring A New Extension Skillbelow) rather than writing files from nothing.
What Success Looks Like
A successful setup has all of the following:
npm installhas been run in{baseDir}with Node.js 22 or newer.- OpenClaw loads
{baseDir}/openclaw-pluginviaplugins.load.paths. plugins.entries["js-eyes"].enabledistrue.tools.alsoAllow(preferred) ortools.allowincludesjs-eyes, so the plugin's optional tools are actually exposed to the model.- The user can run
openclaw js-eyes status. - The browser extension is connected to
http://<serverHost>:<serverPort>, the popup Server Token field is populated (2.2.0+), andjs_eyes_get_tabsreturns real tabs. - The bundled first-party extension skills under
{baseDir}/skillsare enabled by default in the JS Eyes host config, and the main plugin auto-loads enabled skills from{baseDir}/skillsor the configuredskillsDir(primary), plus any read-only directories listed inextraSkillDirs(extras). The user can later runjs_eyes_discover_skills/js_eyes_install_skillto add more extension skills dynamically. js-eyes doctorreports an acceptable security posture for this skill's deployment (token present,allowAnonymous=false,allowRawEval=truein~/.js-eyes/config/config.json, host bound to loopback, skill integrity OK).doctorwill still label raw eval as insecure — that is expected when following this skill.
Deployment Modes
Treat {baseDir} as the bundle or repository root that contains openclaw-plugin/, skills/, and the package manifests.
There are two supported complete deployment modes:
-
ClawHub / bundle deployment
{baseDir}is the installed JS Eyes bundle root.- Run
npm installin{baseDir}so the plugin runtime can resolve its dependencies. - Register
{baseDir}/openclaw-pluginin OpenClaw.
-
Source-repo / development deployment
{baseDir}is the root of a localjs-eyesgit clone.- Run
npm installin the repo root, not insideopenclaw-plugin/. - Point OpenClaw
plugins.load.pathsat the repo-rootopenclaw-plugindirectory. - If you are debugging the browser side, load the extension from
extensions/chrome/orextensions/firefox/manifest.jsonas appropriate. - Restart scope: only changes to files under
openclaw-plugin/itself (or anypackages/*module the plugin imports directly) require restarting / refreshing OpenClaw so the plugin module is re-required. Edits to individual skill files (skills/<id>/skill.contract.js, anextraSkillDirsentry's contract, etc.) are picked up hot by theSkillRegistry+ chokidar watcher — no restart needed.
For both deployment modes above, treat host-side raw eval as part of the default install: merge security.allowRawEval: true into ~/.js-eyes/config/config.json (Windows: %USERPROFILE%/.js-eyes/config/config.json) during deployment — not in openclaw.json. The js-eyes server reads this file; the browser extension syncs the value on WebSocket handshake (2.5.1+). Restart the server / OpenClaw after changing it so execute_script raw JavaScript is not blocked with RAW_EVAL_DISABLED.
Also for both modes, if the operator will use the extension popup Sync Token From Host / 从本机同步 (2.4.0+), register the Native Messaging host on that machine. Preferred (local launcher, no network): from the checked-out repo run bin/js-eyes-native-host-install.sh --browser all (macOS/Linux) or ./bin/js-eyes-native-host-install.ps1 -Browser all (Windows); both forward to node apps/cli/bin/js-eyes.js native-host install, which never touches the npm registry. Since 2.6.3 the launchers also run js-eyes server token init (idempotent) at the end, so the host has a token file to read on the very first popup sync attempt — without that step the host returns token-missing until the operator either runs js-eyes server token init manually or starts OpenClaw / the server (ensureToken() runs on server boot). Pass --skip-token-init (-SkipTokenInit on Windows) to opt out of the auto-init. Fallback: npx js-eyes native-host install --browser all — only recommended when js-eyes is globally installed and the operator trusts the npm registry at install time; this fallback does not seed the token, so pair it with npx js-eyes server token init explicitly. Confirm with node apps/cli/bin/js-eyes.js native-host status (manifest + launcher must exist; on Windows the .bat lives under %LOCALAPPDATA%\js-eyes\native-host\). Then restart the browser or reload the extension. Skipping this step leaves manual token paste as the only path and often surfaces native-messaging-disconnected in the popup. See docs/native-messaging.md.
Safe Default Mode (no raw eval)
This section is informational — the default Setup Workflow below still opts
into allowRawEval=true for full compatibility with the 2.6.x SKILL contract.
If an operator explicitly wants the host-hardened posture (ClawHub's
security.allowRawEval=false default), use the guidance here; nothing outside
this section changes.
- What still works without
allowRawEval:js_eyes_open_url,js_eyes_get_tabs,js_eyes_screenshot,js_eyes_xpath*,js_eyes_click,js_eyes_type,js_eyes_hover,js_eyes_scroll,js_eyes_press_key,js_eyes_execute_action(declarative action DSL),js_eyes_find_element,js_eyes_reload_skills,js_eyes_reload_security, plus every extension-skill tool that goes throughexecute_action. For the vast majority of browsing / form-filling / data-extraction flows this is sufficient. - What gets refused with
RAW_EVAL_DISABLED:js_eyes_execute_scriptandjs_eyes_execute_script_async— the raw-JS entry points. Sensitive tools that also happen to run raw JS (e.g. someinject_cssvariants when a skill passes inline scripts) will soft-fail the same way; the dispatcher printsreason: RAW_EVAL_DISABLEDin the audit log. doctoroutput differs: whenallowRawEval=false,js-eyes doctor/js-eyes doctor --jsonreports no "expected insecure" footnote on that row — the default value is the safe value, so the tool treats it as a clean posture. Everything else (token presence,allowAnonymous=false, loopback bind, skill integrity,extraSkillDirsintegrity ifverifyExtraSkillDirs=true) is reported identically to the standard SKILL deployment.- How to opt in: omit the
security.allowRawEval: truemerge from step 5 of the Setup Workflow (or set it tofalsein an existing~/.js-eyes/config/config.json), restart the js-eyes server / OpenClaw, and optionally flipsecurity.verifyExtraSkillDirs=trueto close the extraSkillDirs gap described in SECURITY_SCAN_NOTES.md. - Behaviour guarantee: if an operator follows the default SKILL workflow
below without any of these tweaks, the runtime behaviour in 2.6.3 is
identical to 2.6.1 — Safe Default Mode is purely additive guidance.
(2.6.3 only changes the install-time UX around
server.tokenseeding; it does not alter the policy / consent / egress runtime.)
Setup Workflow
When the user asks to install, configure, or repair JS Eyes, follow this exact order:
-
Determine the operating system first and choose commands accordingly.
-
Resolve the OpenClaw config path before editing anything.
-
Verify prerequisites:
node -vmust be>= 22- if the user expects OpenClaw plugin mode,
openclaw --versionshould work
-
From
{baseDir}, runnpm installif dependencies are missing or if the user just installed the bundle. -
Update the resolved
openclaw.json:- ensure
plugins.load.pathscontains the absolute path to{baseDir}/openclaw-plugin - ensure
plugins.entries["js-eyes"].enabledistrue - ensure
tools.alsoAllowcontainsjs-eyes(preferred additive mode), or ensuretools.allowincludesjs-eyes - if needed, create
plugins.entries["js-eyes"].configwith:serverHost: "localhost"serverPort: 18080autoStartServer: true
- merge or create the JS Eyes host config at
~/.js-eyes/config/config.json(Windows:%USERPROFILE%/.js-eyes/config/config.json) withsecurity.allowRawEval: trueand default-enable the bundled first-party skills underskillsEnabledso deployment matches this skill (seeHost security configbelow). Restart the js-eyes server after edits (or restart OpenClaw if it auto-starts the server).
- ensure
-
Restart or refresh OpenClaw so the plugin is reloaded.
-
Verify with
openclaw js-eyes status. -
Make sure
~/.js-eyes/runtime/server.tokenexists before anyone clicks the popup's Sync Token From Host / 从本机同步 button — otherwise the native host repliestoken-missingand the popup falls back to manual paste. Three ways to create it, any one is enough:- Run
js-eyes server token initdirectly (idempotent — no-op if the file already exists). - Run the local launcher
bin/js-eyes-native-host-install.sh --browser all(macOS/Linux) /./bin/js-eyes-native-host-install.ps1 -Browser all(Windows) — since 2.6.3 it does thetoken initfor you (skip with--skip-token-init). - Start OpenClaw (with
autoStartServer: true) orjs-eyes server startonce — the server callsensureToken()on boot.
Then run the local launcher above (or the equivalent
node apps/cli/bin/js-eyes.js native-host install --browser all) to register the Native Messaging manifest without hitting the npm registry.npx js-eyes native-host installremains as a fallback when the operator already hasjs-eyesglobally installed; the npx path does not seed the token, so pair it withnpx js-eyes server token init. If the operator can't or won't use Native Messaging at all, runjs-eyes server token show --revealand paste the value into the extension popup Server Token field under Advanced. - Run
-
If the server is healthy but no browser is connected, guide the user through browser extension installation, server-token entry, and connection.
-
After the base setup works, pick the right path for extension skills:
- Registry / first-party skills: prefer
js_eyes_discover_skills+js_eyes_install_skill— 2.2.0+ writes a plan underruntime/pending-skills/<id>.json; finalize withjs-eyes skills approve <id>thenjs-eyes skills enable <id>. - Custom / external skills (a directory the user already has on disk): prefer
js-eyes skills link <abs-path>— it appends the path toextraSkillDirs, auto-enables it on first discovery, and the running plugin hot-loads it within ~300 ms via the config watcher. Reverse withjs-eyes skills unlink <abs-path>. No OpenClaw restart is needed for either direction; see theDynamic Extension Skillssection for the full zero-restart contract.
- Registry / first-party skills: prefer
-
Run
js-eyes doctorto confirm the deployment posture (token present,allowAnonymous=false,allowRawEvalenabled —doctormay still print it as insecure, which is expected here, loopback-bound, skill integrity OK) before handing off.
When asked to fix a broken setup, prefer repairing the existing config instead of repeating the whole installation.
Resolve The OpenClaw Config Path
Use this precedence order:
OPENCLAW_CONFIG_PATHOPENCLAW_STATE_DIR/openclaw.jsonOPENCLAW_HOME/.openclaw/openclaw.json- Default:
- macOS / Linux:
~/.openclaw/openclaw.json - Windows:
%USERPROFILE%/.openclaw/openclaw.json
- macOS / Linux:
Do not assume ~/.openclaw/openclaw.json if any of the environment variables above are set.
Recommended Config Shape
Update the resolved OpenClaw config so it contains the plugin path and enablement entry. Append to existing arrays and objects; do not remove unrelated plugins.
{
"tools": {
"alsoAllow": ["js-eyes"]
},
"plugins": {
"load": {
"paths": ["/absolute/path/to/js-eyes/openclaw-plugin"]
},
"entries": {
"js-eyes": {
"enabled": true,
"config": {
"serverHost": "localhost",
"serverPort": 18080,
"autoStartServer": true
}
}
}
}
}
Host security config (~/.js-eyes/config/config.json)
This file is not openclaw.json. For deployments following this skill, ensure at least:
{
"skillsEnabled": {
"js-bilibili-ops-skill": true,
"js-browser-ops-skill": true,
"js-github-ops-skill": true,
"js-jike-ops-skill": true,
"js-reddit-ops-skill": true,
"js-wechat-ops-skill": true,
"js-x-ops-skill": true,
"js-xiaohongshu-ops-skill": true,
"js-youtube-ops-skill": true,
"js-zhihu-ops-skill": true
},
"security": {
"allowRawEval": true
}
}
Merge with existing keys (for example skillsDir, skillsEnabled). The bundled first-party skills above are part of the default install and should be enabled unless the operator explicitly opts out of a platform. Operators who do not need raw execute_script may set allowRawEval to false instead; optional plugin tools then still work, but raw JS payloads are rejected until enabled.
Important details:
- The path must end in
openclaw-plugin. - On Windows JSON paths, prefer forward slashes such as
C:/Users/name/skills/js-eyes/openclaw-plugin. - If
pathsorentriesalready exist, merge rather than overwrite. js-eyesregisters its tools as optional plugin tools, so a complete deployment also needstools.alsoAllow: ["js-eyes"]or an equivalenttools.allowentry.skillsEnabledlives in the JS Eyes host config, not inopenclaw.json. Enabling a bundled skill registers its extension tools through the mainjs-eyesplugin; disabling it leaves the files on disk but skips loading it.- To mount extension skills from outside
{baseDir}/skills(e.g. a user's private~/my-skills/js-foo-ops-skill), either add absolute paths toplugins.entries["js-eyes"].config.extraSkillDirs: [...]directly, or let the CLI handle it:js-eyes skills link <abs-path>does the dedup append and also triggers an in-memory reload on the running plugin. Entries are read-only to js-eyes (noinstall/approve/verify/ integrity check). - Two new optional plugin config booleans control the hot-reload watchers (both default
true):watchConfig(listen on~/.js-eyes/config/config.json) anddevWatchSkills(listen on discovered skill directories). Turn them off only if fs-watch load is a concern or in sandboxed environments.
Host security hot-reload (2.5.2+)
Some security.* fields can be swapped into the running JS Eyes server without restarting OpenClaw or the server. The server-core ships its own chokidar watcher on ~/.js-eyes/config/config.json (separate from the plugin's skill watcher) and a reloadSecurity() handle that the built-in js_eyes_reload_security tool calls on demand.
- Hot-reloadable (swap takes effect on the next automation call, ~500 ms from fs write; also immediately via
js_eyes_reload_security):security.egressAllowlistsecurity.toolPoliciessecurity.sensitiveCookieDomainssecurity.allowedOriginssecurity.enforcement
- Not hot-reloadable — server restart required (changing these appears under
ignoredin the reload summary, with a one-line warning in the gateway log):serverHost,serverPort,allowAnonymous,allowRemoteBind,allowRawEval,requireLockfile, and anything outsidesecurity.*(token rotation,requestTimeout, etc.). - Caveat — session-level egress approvals reset: when the allowlist flips, each live automation connection rebuilds its
PolicyContext, which means per-sessionjs-eyes egress approve <id>grants are dropped. Agents re-issue the approval on the nextpending-egressresponse; no action needed for standardallow <domain>edits because those are part of the static allowlist and get picked up automatically. - Operator triggers (any one is sufficient):
- Edit
~/.js-eyes/config/config.jsonand save — chokidar debounces 300 ms and firesreloadSecurity({ source: 'fs-watch' }). - Agent call:
js_eyes_reload_securitybuilt-in tool (returns{ changed, applied, ignored, generation, egressAllowlist }). - CLI preview:
js-eyes security reload— read-only dry run that prints what would be applied (CLI does not own the server event loop, so trigger #1 or #2 is required for the actual swap).
- Edit
- Observability: the audit log (
~/.js-eyes/logs/audit.log) gains three new events —config.hot-reload,config.hot-reload.error,automation.policy-rebuilt— andGET /api/browser/statusnow includesdata.policy.generation/data.policy.egressAllowlistso operators can externally confirm the live generation.
Verification Workflow
After setup, verify the stack in this order:
openclaw plugins inspect js-eyesopenclaw js-eyes status- Check whether the built-in server is reachable and reports uptime.
- Confirm that at least one browser extension client is connected.
- Ask the agent to use
js_eyes_get_tabsor runopenclaw js-eyes tabs. - If the user wants extension skills, call
js_eyes_discover_skillsonly after the base stack works.
Expected status checks:
openclaw plugins inspect js-eyesshows the plugin as loaded.- Server responds on
http://localhost:18080by default. openclaw js-eyes statusshows uptime and browser client counts.js_eyes_get_tabsreturns tabs instead of an empty browser list.
Verifying A Specific Extension Skill Is Loaded
When the user asks "did my skill get picked up?", check all four:
~/.js-eyes/config/config.jsonhasskillsEnabled["<id>"]: trueand (for externals) the skill's directory or a parent is listed inextraSkillDirs.- Tail the gateway log and look for one of these lines from
[js-eyes]:Skill sources: primary=<dir> extras=<N>— confirms extras were seen at startup.Loaded local skill "<id>" with K tool(s)— initial load at plugin boot.Hot-loaded skill "<id>" with K tool(s)— loaded at runtime by the config / skill-dir watcher.Discovered <N> skill(s): <K> active— gives a numeric sanity check.
- Ask the agent to call the built-in tool
js_eyes_reload_skills; the returned summary must contain the id underaddedorreloaded, andfailedDispatchersmust be empty. - Run
js-eyes skills listfrom the host shell; each entry is annotated withSource: primaryorSource: extra (<path>).
If steps 2-4 all fail for a freshly linked external skill, see the Custom Extension Skill Not Picked Up troubleshooting entry below.
Browser Extension Connection
If the plugin is enabled but no browser is connected:
- Install the JS Eyes browser extension separately from GitHub Releases or the website.
- Make sure the host has a token to share: confirm
~/.js-eyes/runtime/server.tokenexists (Windows:%USERPROFILE%/.js-eyes/runtime/server.token). If it doesn't, runjs-eyes server token init, or start OpenClaw /js-eyes server startonce so the server'sensureToken()creates it. Without this file the native host returnstoken-missingand the popup sync silently falls back to manual paste. - (Preferred, 2.4.0+) Run the local launcher
bin/js-eyes-native-host-install.sh --browser all(macOS/Linux) or./bin/js-eyes-native-host-install.ps1 -Browser all(Windows) — equivalent tonode apps/cli/bin/js-eyes.js native-host install --browser all; this never contacts the npm registry. Since 2.6.3 the launcher also runsjs-eyes server token initfor you (skip with--skip-token-init/-SkipTokenInit), so steps 2 and 3 collapse into one command in the common case.npx js-eyes native-host install --browser allremains as a fallback but does not seed the token — pair it withnpx js-eyes server token initexplicitly. Either path sets up Native Messaging so the extension auto-syncsserver.tokenand the HTTP URL; restart the browser (or reload the extension) so it picks up the new manifest, then open the popup and click Sync Token From Host. - Manual fallback: open the extension popup, expand Advanced, set the server address to
http://<serverHost>:<serverPort>, paste the output ofjs-eyes server token show --revealinto the Server Token (2.2.0+) field, and clickConnect. - Re-run
openclaw js-eyes status.
The browser extension is not bundled inside the main ClawHub skill. It must be installed separately. Connections without a matching server token are rejected unless the operator has set security.allowAnonymous=true.
Authoring A New Extension Skill
When the user wants to create a brand-new extension skill (not install / mount an existing one), do not scaffold files from scratch. Guide them through the canonical starter flow:
- Copy the reference starter
examples/js-eyes-skills/js-hello-ops-skill/to a directory of their choice (typically~/my-skills/js-<domain>-ops-skill/). Point out that the starter already ships a workingskill.contract.js,package.json, anasync runtime.dispose()hook, a sample tool, and aSKILL.mdfrontmatter. - Rename the three identifiers in lockstep:
package.json.name,SKILL.mdfrontmattername:, andskill.contract.js→id+name. Discovery resolves id viacontract.id || pkg.name || path.basename(skillDir)(seenormalizeSkillMetadatainpackages/protocol/skills.js), so mismatches do not break load — butskillsEnabled.<id>,js-eyes skills link/enable/disable <id>, and log messages all key off whatever the contract finally resolves to. Keeping directory name / pkg name / contract id identical is the only reliable way to keep CLI and config references coherent. - Read the authoring guides before wiring real logic — the canonical references are:
docs/dev/js-eyes-skills/authoring.zh.md— directory layout, discovery rules, quick-start.docs/dev/js-eyes-skills/contract.zh.md—skill.contract.jssurface (tools / runtime /runtime.dispose()lifecycle).docs/dev/js-eyes-skills/deployment.zh.md— the zero-restart deployment flow the skill will end up in.
- Install local deps with
npm installinside the new skill directory sows/@js-eyes/client-sdkresolve at load time. - Mount it zero-restart with
js-eyes skills link <abs-path>; the running plugin auto-discovers it within ~300 ms. Iterate onskill.contract.jsin place — saves are hot-reloaded by theSkillRegistryskill-dir watcher; no OpenClaw restart is needed until the skill introduces a brand-new tool name the host has never registered (rare, and the reload diff will say so viafailedDispatchers). - When ready to publish, decide between contributing it back to the first-party
skills/directory (registry / ClawHub distribution) or keeping it external viaextraSkillDirs/linkforever — both paths are supported and zero-restart after mount.
Do not invent a different layout. Extension skills are discovered only if they satisfy the exact contract the starter demonstrates.
Dynamic Extension Skills
The main js-eyes bundle ships first-party extension skills under {baseDir}/skills. A complete default install enables those bundled skills through ~/.js-eyes/config/config.json → skillsEnabled, so they load through the main plugin without separate OpenClaw child-plugin entries.
Bundled first-party skills:
js-bilibili-ops-skilljs-browser-ops-skilljs-github-ops-skilljs-jike-ops-skilljs-reddit-ops-skilljs-wechat-ops-skilljs-x-ops-skilljs-xiaohongshu-ops-skilljs-youtube-ops-skilljs-zhihu-ops-skill
There are two complementary discovery surfaces — pick the right one when the user asks "what skills do I have?":
- Local / installed view (what is actually mounted right now):
js-eyes skills listfrom the host shell, orjs_eyes_reload_skillswhich returns a live diff. Each entry carries aSource: primaryvsSource: extra (<path>)annotation. - Registry / installable view (what could be installed from
skills.json):js_eyes_discover_skills(agent tool). Installed rows are marked✓ 已安装, installable rows○ 未安装.
After the base plugin works:
- Bundled first-party skills should already be enabled by the default host config above; use
js-eyes skills listorjs_eyes_reload_skillsto verify they are loaded. - Use
js_eyes_discover_skillsto list registry skills when the user wants to install a skill that is not already present in{baseDir}/skills, or to compare installed versions with the registry. - Use
js_eyes_install_skillto stage a plan — 2.2.0+ downloads the bundle, verifies itssha256againstskills.json, and writesruntime/pending-skills/<id>.jsonwithout installing. - Finalize the plan with
js-eyes skills approve <id>, then enable it withjs-eyes skills enable <id>. - Use
js-eyes skills verify(orjs-eyes doctor) to confirm.integrity.jsonstill matches the on-disk skill files. - Since 2026-04-19 the main plugin hot-loads newly enabled or linked skills (and hot-disposes disabled ones) via
SkillRegistry+ a chokidar watcher on the host config — no OpenClaw restart needed. For external custom skills, preferjs-eyes skills link <abs-path>/js-eyes skills unlink <abs-path>; to force a refresh, calljs-eyes skills reloador have the agent invoke thejs_eyes_reload_skillstool (it returns anadded/removed/reloaded/toggledOff/conflicts/failedDispatchersdiff). - Recommend an OpenClaw restart only when
js_eyes_reload_skillsreports non-emptyfailedDispatchers— that means the host refused to register a brand-new tool name at runtime, which a restart will resolve. Everything else (install, enable, disable, replace, unlink) is zero-restart.
Skill Lifecycle Cheat Sheet
Use this table to pick the correct command for any user intent. All rows are zero-restart unless otherwise noted.
| Intent | Registry skill (shipped in skills.json) | External skill (arbitrary directory on disk) |
|---|---|---|
| Inspect what is installed locally | js-eyes skills list | same (entries annotated Source: extra (<path>)) |
| Browse what can be installed | js_eyes_discover_skills | N/A (externals are out-of-registry by definition) |
| Install / mount | js_eyes_install_skill → js-eyes skills approve <id> → js-eyes skills enable <id> | js-eyes skills link <abs-path> (auto-enables on first discovery) |
| Upgrade to the latest registry version | js-eyes skills update <id> (preserves skillsEnabled, honors minParentVersion); js-eyes skills update --all for every primary-source skill; rerunning curl … | JS_EYES_SKILL=<id> bash works too | N/A — externals are managed in their source tree; pull/rebuild there |
| Temporarily stop without removing | js-eyes skills disable <id> — runtime.dispose() fires, tools stop responding, files stay on disk | js-eyes skills disable <id> (works the same; the link path stays in extraSkillDirs) |
| Re-enable | js-eyes skills enable <id> | js-eyes skills enable <id> |
| Replace / edit in place | Edit {baseDir}/skills/<id>/skill.contract.js and save — picked up by the skill-dir watcher, or call js-eyes skills reload | Same, against the external directory |
| Verify integrity | js-eyes skills verify [<id>] — checks .integrity.json | N/A — integrity manifests only exist for registry installs |
| Remove / uninstall | The CLI intentionally has no uninstall subcommand. Soft-remove: js-eyes skills disable <id> (recommended). Hard-remove: after disable, delete {baseDir}/skills/<id>/ manually, then optionally clear the skillsEnabled.<id> key from ~/.js-eyes/config/config.json to keep it tidy. | js-eyes skills unlink <abs-path> — removes the path from extraSkillDirs and hot-unloads every skill that was sourced from it |
| Force a re-scan | js-eyes skills reload or js_eyes_reload_skills (agent tool) | Same |
The agent SHOULD execute these commands directly when it has shell access. If it does not (read-only / "ask" mode), it SHOULD print the exact command for the user to run and then re-verify via js-eyes skills list or js_eyes_reload_skills afterwards.
Do not instruct the user to register child-skill plugin paths manually. Child skills no longer ship their own openclaw-plugin wrappers.
Prefer the built-in install flow over manual zip extraction when the user wants additional JS Eyes capabilities.
Troubleshooting
Cannot find module 'ws'
Run npm install in {baseDir}. The bundle expects dependencies to be installed from the skill root.
js_eyes_* tools do not appear
Check:
plugins.load.pathspoints to{baseDir}/openclaw-plugin.plugins.entries["js-eyes"].enabledistrue.tools.alsoAllowortools.allowincludesjs-eyes.- For items 1-3 (OpenClaw-level plugin config), OpenClaw has been restarted or refreshed since the config change — the plugin module itself is loaded once per OpenClaw process. Skill-level changes (
skillsEnabled.<id>,extraSkillDirs, edits to askill.contract.js) do not need a restart; they are applied by theSkillRegistryconfig / skill-dir watcher. - If this is an extension skill, confirm it is not disabled in the JS Eyes host config (
skillsEnabled.<id>: true) and that legacy OpenClaw child-plugin entries are removed.
Custom Extension Skill Not Picked Up
For a custom external skill mounted via js-eyes skills link <abs-path> where no tools appear and the log has no Loaded local skill "<id>" / Hot-loaded skill "<id>" line, check in order:
- Path actually landed in config:
~/.js-eyes/config/config.json→extraSkillDirscontains the path;skillsEnabled["<id>"]istrue. - Skill is self-contained:
cd <abs-path> && ls skill.contract.js package.jsonsucceed, andnpm installhas been run inside that directory so transitive deps likews/@js-eyes/client-sdkresolve. - Contract actually loads: the gateway log contains no
Failed to load skill "<id>"entry; if it does, the error message identifies the offendingrequireor syntax issue. Fix in place and calljs-eyes skills reload(orjs_eyes_reload_skills) — no OpenClaw restart needed. - Id conflict with primary: look for
Skipping extra skill ... same id already loaded from primary. Primary wins by design; rename the custom skill'sidin itspackage.json/skill.contract.jsor move it into primary. - Brand-new tool name refused by host: if
js_eyes_reload_skillsreturns a non-emptyfailedDispatchers, OpenClaw refused to register a tool name post-boot. This is the one case that needs a one-shot OpenClaw restart; after the restart the skill loads normally. - Plugin is running an outdated version (after a
git pull/ upgrade but before restart): the running plugin may predateSkillRegistry. In the gateway log look for[js-eyes] Watching host config: ...— if it is missing, restart OpenClaw once to pick up the new plugin code; subsequent skill changes stay zero-restart.
Browser Extension Stays Disconnected
Check:
openclaw js-eyes statusserverHost/serverPortin plugin config- The extension popup server URL
- Whether
autoStartServeristrue - (2.2.0+) The popup Server Token field matches
js-eyes server token show --reveal. On 2.4.0+ installs, prefer re-running the popup's Sync Token From Host button (powered by the Native Messaging host — seedocs/native-messaging.md). Taillogs/audit.logviajs-eyes audit tail—conn.rejectwithreason: tokenorreason: originpoints to token/Origin mismatches. Sync Token From Hostreportstoken-missing: the manifest is registered but~/.js-eyes/runtime/server.tokendoesn't exist yet. Either runjs-eyes server token init(idempotent), start OpenClaw /js-eyes server startonce soensureToken()creates it, or re-run the local launcherbin/js-eyes-native-host-install.sh/.ps1(2.6.3+ seeds the token automatically). Tail~/.js-eyes/logs/native-host.logto confirm — aget-config: token-missingline on every popup click is the smoking gun.Sync Token From Hostdoes nothing / errors withCould not establish connection: the manifest isn't actually registered for this browser, or the browser was open beforenative-host installran. Re-runnode apps/cli/bin/js-eyes.js native-host statusto confirm the manifest + launcher exist for the right browser, then fully restart the browser (not just reload the extension) so it re-scans the NativeMessagingHosts directory.
Sensitive Tool Calls Hang Without Output (2.2.0+)
execute_script*, get_cookies*, upload_file*, inject_css, and install_skill default to the confirm policy and wait for operator approval.
js-eyes consent listto see pending requests.js-eyes consent approve <id>orjs-eyes consent deny <id>to resolve.- To disable the gate for a specific tool, set
security.toolPolicies.<tool>=allowinconfig.json(logs an audit event).
Open URL or automation tools fail with egress / policy messages (2.3.0+)
If js_eyes_open_url or other browser tools return text mentioning pending-egress, 出站策略, or POLICY_SOFT_BLOCK, the server applied the policy engine before the extension ran the action (navigation may never reach the browser).
- Run
js-eyes security showand inspectegressAllowlistandtaskOrigin(hosts must be in static allowlist, session scope, or task-origin scope — seeSECURITY.mdPolicy Engine). - Run
js-eyes egress list; usejs-eyes egress approve <id>for a queued host orjs-eyes egress allow <domain>to append tosecurity.egressAllowlist. - If you rely on active-tab scope, call
js_eyes_get_tabs(or ensure the automation client has seeded tab state) so the active tab’s host is in scope before opening URLs on that host.
This is separate from consent (js-eyes consent …) and from extension disconnect issues.
Skill Fails to Load With Integrity Error (2.2.0+)
The main plugin refuses to register skills whose files no longer match .integrity.json.
js-eyes skills verify <id>to see which files drifted.- Re-install:
js-eyes skills install <id>→js-eyes skills approve <id>→js-eyes skills enable <id>. - If the drift was expected (manual patch), re-generate the manifest by reinstalling; do not edit
.integrity.jsonby hand.
Custom OpenClaw Config Location
Always resolve OPENCLAW_CONFIG_PATH, OPENCLAW_STATE_DIR, and OPENCLAW_HOME before editing config or telling the user where to look.
Notes For The Agent
- Prefer performing the setup steps for the user instead of only explaining them.
- Modify existing OpenClaw config carefully; preserve unrelated plugin entries.
- For plugin setup, edit JSON directly rather than asking the user to do it manually unless you are blocked by permissions.
- Once setup is complete, switch from installation guidance to normal use of
js_eyes_*tools.