Install
openclaw skills install @dlazyai/asale-buy-opencodeSwitch opencode between buying from the asale market and using its own subscription, and see which running sessions are still on the old config. 在「从 asale 市场买」和「用它自己的订阅」之间切换 opencode,并查看哪些正在运行的会话还用着旧配置。
openclaw skills install @dlazyai/asale-buy-opencodeSwitch opencode between buying from the asale market and using its own subscription, and see which running sessions are still on the old config.
Two separate things have to be true.
1. The local daemon must be running, and you need its token. asaled writes
~/.asale/daemon.token (mode 0600) on first run and requires it on every /rpc
call, loopback included.
asale status # is the daemon up, and on which port
asale start # start it if it is not
# Every /rpc call carries the daemon token — loopback included.
TOKEN=$(cat ~/.asale/daemon.token)
call() {
body=$2; [ -n "$body" ] || body='{}'
# --noproxy is not optional: a machine with HTTP_PROXY set sends even
# 127.0.0.1 through it, and the proxy answers 502 with an empty body,
# which reads exactly like a broken daemon.
curl -sS --noproxy 127.0.0.1 -X POST "http://127.0.0.1:9700/rpc/$1" \
-H 'content-type: application/json' -H "x-asale-token: $TOKEN" -d "$body"
}
A connection refused means the daemon is not running — say so and stop. Starting it is the user's call.
~/.asale/asaled.bind holds the port the last asale start used; fall back to
127.0.0.1:9700. A bind on 0.0.0.0 is not a destination — keep the port and
dial loopback.
2. Selling and buying need a signed-in asale account. The daemon mints the
consumer key and registers the seller against that session. There is no CLI
login: open the app (asale open) and sign in there. Without it you get
errors.session.signInToSell / errors.session.signInToBuy, which is not
something to work around.
curl -fsSL https://asale.ai/dl/install.sh | sh (Windows:
irm https://asale.ai/dl/install.ps1 | iex). asale update re-runs the same
installer.~/.asale/ — the token, the SQLite store and the daemon's logs.
$ASALE_DATA_DIR moves all of it.This skill talks to a daemon on your own machine. Nothing here reaches the asale servers directly; the daemon does that, over its own authenticated session. The token file is the only credential this skill reads, and it never leaves the loopback interface.
Buying points one locally installed AI CLI at asale's local proxy instead of at the vendor. The switch rewrites that tool's own config file — and only that tool's, so the switches are fully independent — and injects an asale key the tool's config never has to hold in plain sight. Every file it touches is snapshotted first, so switching off restores the original byte-for-byte.
Requests then go to the proxy, which decides per request: if your own subscription window has room and the mode allows it, it goes straight upstream and costs nothing; otherwise it is bought from the market.
Two things about opencode's config are worth knowing, because both were found by asking the binary rather than the docs:
opencode debug paths answers C:\Users\<u>\.config\opencode, not
%APPDATA%.opencode.json and opencode.jsonc, and .jsonc
wins when both exist — and opencode writes a .jsonc stub of its own the
first time it runs. So on any machine that has ever started opencode, that is
the live file. asale edits whichever one is already there and creates the
.jsonc when there is none.The switch refuses to rewrite a config it cannot round-trip: .jsonc allows
comments and the writer emits plain JSON, so a commented file is left alone with
an error rather than silently stripped.
opencode is addressed under its own /opencode/v1 prefix, for the same reason
OpenClaw is — the dialect cannot tell them apart.
call buy_tools
call market_models # the catalog, to pick model ids from
The opencode row carries installed, enabled (the switch), in_effect (its live
config really points at the proxy), models and config_paths.
This tool needs a model selection. It offers exactly the models its config
names, so an empty models leaves it with an asale provider and nothing to
pick. Turning the switch on without one is not a working setup — say so rather
than reporting it as done.
call set_buy_tool '{"tool":"opencode","enabled":true,"models":["claude-fable-5"]}'
models omitted leaves the current selection alone; [] clears it. Config
rewritten: ~/.config/opencode/opencode.jsonc (or opencode.json when that is the live file) — a provider.asale block plus the top-level model.
call tool_processes
opencode only reads its config at startup, so a session that is already running
keeps the old one. running.opencode lists the ones still on it, by pid, with the
command line they were started with.
scanned: false means the machine's process table could not be read — that is
"we do not know", not "nothing is running", and must not be reported as the
latter.
call set_buy_tool '{"tool":"opencode","enabled":false}'
The original config comes back byte-for-byte.
enabled is true but in_effect is falseSomething else rewrote the config after asale did (another switcher, an editor,
an installer). Re-running step 2 repairs it; open_config_path opens the file
for the user to look at.
call open_config_path '{"path":"<one of config_paths>"}'
CRITICAL INSTRUCTION FOR AGENT:
buy_tools before every set_buy_tool. The switch and the model
selection are independent, and writing from a stale reading silently
overwrites what the user chose.tool_processes as things to kill, and never kill
one. A CLI is attached to a terminal nothing outside it can hand a new
process to; killing one throws away whatever it was doing. Report them and
let the user restart what they choose.errors.session.signInToBuy, stop and say so.| Code | Error Type | Example Message |
|---|---|---|
| — | Daemon not running | curl: (7) Failed to connect to 127.0.0.1 port 9700 |
| 401 | Bad or missing token | {"key":"errors.daemon.unauthorized","message":"unauthorized (missing or bad X-Asale-Token)"} |
| 400 | Not signed in (selling) | {"key":"errors.session.signInToSell","message":"sign in before selling"} |
| 400 | Not signed in (buying) | {"key":"errors.session.signInToBuy","message":"sign in before buying"} |
| 400 | Unknown account | {"message":"unknown account"} |
| 400 | Bad tool id | {"message":"unknown tool: <id>"} |
AGENT CRITICAL INSTRUCTION:
- Errors carry a
keyas well as amessage. Thekeyis a stable translation id — quote it, do not paraphrase the message.- On
errors.session.signInToSell/errors.session.signInToBuy, tell the user to sign in from the app (asale open) and stop. Do not retry, and do not look for another route to the same effect.- On a connection failure, report that the daemon is down and let the user decide whether to start it. Do not run
asale startunasked.
Visit https://asale.ai for more information. The same switches have a UI in the
desktop app and in any browser — asale open opens it, and anything this skill
does can be checked there.