revert: Switch back to tsc for compiling.

This commit is contained in:
cpojer
2026-01-31 18:31:49 +09:00
parent e25fedf932
commit 76361ae3ab
36 changed files with 527 additions and 843 deletions

View File

@@ -94,7 +94,7 @@ jobs:
command: pnpm canvas:a2ui:bundle && bunx vitest run
- runtime: bun
task: build
command: bunx tsdown
command: bunx tsc -p tsconfig.json --noEmit false
steps:
- name: Checkout
uses: actions/checkout@v4

View File

@@ -36,4 +36,4 @@ ENV NODE_ENV=production
# This reduces the attack surface by preventing container escape via root privileges
USER node
CMD ["node", "dist/index.mjs"]
CMD ["node", "dist/index.js"]

View File

@@ -5,7 +5,7 @@ enum CommandResolver {
private static let helperName = "openclaw"
static func gatewayEntrypoint(in root: URL) -> String? {
let distEntry = root.appendingPathComponent("dist/index.mjs").path
let distEntry = root.appendingPathComponent("dist/index.js").path
if FileManager().isReadableFile(atPath: distEntry) { return distEntry }
let openclawEntry = root.appendingPathComponent("openclaw.mjs").path
if FileManager().isReadableFile(atPath: openclawEntry) { return openclawEntry }
@@ -271,7 +271,7 @@ enum CommandResolver {
}
let missingEntry = """
openclaw entrypoint missing (looked for dist/index.mjs or openclaw.mjs); run pnpm build.
openclaw entrypoint missing (looked for dist/index.js or openclaw.mjs); run pnpm build.
"""
return self.errorCommand(with: missingEntry)
@@ -360,10 +360,10 @@ enum CommandResolver {
if command -v openclaw >/dev/null 2>&1; then
CLI="$(command -v openclaw)"
openclaw \(quotedArgs);
elif [ -n "${PRJ:-}" ] && [ -f "$PRJ/dist/index.mjs" ]; then
elif [ -n "${PRJ:-}" ] && [ -f "$PRJ/dist/index.js" ]; then
if command -v node >/dev/null 2>&1; then
CLI="node $PRJ/dist/index.mjs"
node "$PRJ/dist/index.mjs" \(quotedArgs);
CLI="node $PRJ/dist/index.js"
node "$PRJ/dist/index.js" \(quotedArgs);
else
echo "Node >=22 required on remote host"; exit 127;
fi

View File

@@ -45,7 +45,7 @@ import Testing
@Test func gatewayEntrypointPrefersDistOverBin() throws {
let tmp = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
.appendingPathComponent(UUID().uuidString, isDirectory: true)
let dist = tmp.appendingPathComponent("dist/index.mjs")
let dist = tmp.appendingPathComponent("dist/index.js")
let bin = tmp.appendingPathComponent("bin/openclaw.js")
try FileManager().createDirectory(at: dist.deletingLastPathComponent(), withIntermediateDirectories: true)
try FileManager().createDirectory(at: bin.deletingLastPathComponent(), withIntermediateDirectories: true)

View File

@@ -12,19 +12,19 @@ services:
- ${OPENCLAW_CONFIG_DIR}:/home/node/.openclaw
- ${OPENCLAW_WORKSPACE_DIR}:/home/node/.openclaw/workspace
ports:
- "${OPENCLAW_GATEWAY_PORT:-18789}:18789"
- "${OPENCLAW_BRIDGE_PORT:-18790}:18790"
- '${OPENCLAW_GATEWAY_PORT:-18789}:18789'
- '${OPENCLAW_BRIDGE_PORT:-18790}:18790'
init: true
restart: unless-stopped
command:
[
"node",
"dist/index.mjs",
"gateway",
"--bind",
"${OPENCLAW_GATEWAY_BIND:-lan}",
"--port",
"${OPENCLAW_GATEWAY_PORT:-18789}"
'node',
'dist/index.js',
'gateway',
'--bind',
'${OPENCLAW_GATEWAY_BIND:-lan}',
'--port',
'${OPENCLAW_GATEWAY_PORT:-18789}',
]
openclaw-cli:
@@ -42,4 +42,4 @@ services:
stdin_open: true
tty: true
init: true
entrypoint: ["node", "dist/index.mjs"]
entrypoint: ['node', 'dist/index.js']

View File

@@ -211,4 +211,4 @@ echo "Token: $OPENCLAW_GATEWAY_TOKEN"
echo ""
echo "Commands:"
echo " ${COMPOSE_HINT} logs -f openclaw-gateway"
echo " ${COMPOSE_HINT} exec openclaw-gateway node dist/index.mjs health --token \"$OPENCLAW_GATEWAY_TOKEN\""
echo " ${COMPOSE_HINT} exec openclaw-gateway node dist/index.js health --token \"$OPENCLAW_GATEWAY_TOKEN\""

View File

@@ -1,5 +1,5 @@
---
summary: "Optional Docker-based setup and onboarding for OpenClaw"
summary: 'Optional Docker-based setup and onboarding for OpenClaw'
read_when:
- You want a containerized gateway instead of local installs
- You are validating the Docker flow
@@ -16,6 +16,7 @@ Docker is **optional**. Use it only if you want a containerized gateway or to va
- **Sandboxing note**: agent sandboxing uses Docker too, but it does **not** require the full gateway to run in Docker. See [Sandboxing](/gateway/sandboxing).
This guide covers:
- Containerized Gateway (full OpenClaw in Docker)
- Per-session Agent Sandbox (host gateway + Docker-isolated agent tools)
@@ -37,6 +38,7 @@ From repo root:
```
This script:
- builds the gateway image
- runs the onboarding wizard
- prints optional provider setup hints
@@ -44,15 +46,18 @@ This script:
- generates a gateway token and writes it to `.env`
Optional env vars:
- `OPENCLAW_DOCKER_APT_PACKAGES` — install extra apt packages during build
- `OPENCLAW_EXTRA_MOUNTS` — add extra host bind mounts
- `OPENCLAW_HOME_VOLUME` — persist `/home/node` in a named volume
After it finishes:
- Open `http://127.0.0.1:18789/` in your browser.
- Paste the token into the Control UI (Settings → token).
It writes config/workspace on the host:
- `~/.openclaw/`
- `~/.openclaw/workspace`
@@ -81,6 +86,7 @@ export OPENCLAW_EXTRA_MOUNTS="$HOME/.codex:/home/node/.codex:ro,$HOME/github:/ho
```
Notes:
- Paths must be shared with Docker Desktop on macOS/Windows.
- If you edit `OPENCLAW_EXTRA_MOUNTS`, rerun `docker-setup.sh` to regenerate the
extra compose file.
@@ -110,6 +116,7 @@ export OPENCLAW_EXTRA_MOUNTS="$HOME/.codex:/home/node/.codex:ro,$HOME/github:/ho
```
Notes:
- If you change `OPENCLAW_HOME_VOLUME`, rerun `docker-setup.sh` to regenerate the
extra compose file.
- The named volume persists until removed with `docker volume rm <name>`.
@@ -129,6 +136,7 @@ export OPENCLAW_DOCKER_APT_PACKAGES="ffmpeg build-essential"
```
Notes:
- This accepts a space-separated list of apt package names.
- If you change `OPENCLAW_DOCKER_APT_PACKAGES`, rerun `docker-setup.sh` to rebuild
the image.
@@ -163,7 +171,7 @@ RUN pnpm ui:build
ENV NODE_ENV=production
CMD ["node","dist/index.mjs"]
CMD ["node","dist/index.js"]
```
### Channel setup (optional)
@@ -171,16 +179,19 @@ CMD ["node","dist/index.mjs"]
Use the CLI container to configure channels, then restart the gateway if needed.
WhatsApp (QR):
```bash
docker compose run --rm openclaw-cli channels login
```
Telegram (bot token):
```bash
docker compose run --rm openclaw-cli channels add --channel telegram --token "<token>"
```
Discord (bot token):
```bash
docker compose run --rm openclaw-cli channels add --channel discord --token "<token>"
```
@@ -190,7 +201,7 @@ Docs: [WhatsApp](/channels/whatsapp), [Telegram](/channels/telegram), [Discord](
### Health check
```bash
docker compose exec openclaw-gateway node dist/index.mjs health --token "$OPENCLAW_GATEWAY_TOKEN"
docker compose exec openclaw-gateway node dist/index.js health --token "$OPENCLAW_GATEWAY_TOKEN"
```
### E2E smoke test (Docker)
@@ -218,6 +229,7 @@ Deep dive: [Sandboxing](/gateway/sandboxing)
When `agents.defaults.sandbox` is enabled, **non-main sessions** run tools inside a Docker
container. The gateway stays on your host, but the tool execution is isolated:
- scope: `"agent"` by default (one container + workspace per agent)
- scope: `"session"` for per-session isolation
- per-scope workspace folder mounted at `/workspace`
@@ -233,6 +245,7 @@ one container and one workspace.
If you use multi-agent routing, each agent can override sandbox + tool settings:
`agents.list[].sandbox` and `agents.list[].tools` (plus `agents.list[].tools.sandbox.tools`). This lets you run
mixed access levels in one gateway:
- Full access (personal agent)
- Read-only tools + read-only workspace (family/work agent)
- No filesystem/shell tools (public agent)
@@ -255,60 +268,72 @@ precedence, and troubleshooting.
### Enable sandboxing
If you plan to install packages in `setupCommand`, note:
- Default `docker.network` is `"none"` (no egress).
- `readOnlyRoot: true` blocks package installs.
- `user` must be root for `apt-get` (omit `user` or set `user: "0:0"`).
OpenClaw auto-recreates containers when `setupCommand` (or docker config) changes
unless the container was **recently used** (within ~5 minutes). Hot containers
log a warning with the exact `openclaw sandbox recreate ...` command.
OpenClaw auto-recreates containers when `setupCommand` (or docker config) changes
unless the container was **recently used** (within ~5 minutes). Hot containers
log a warning with the exact `openclaw sandbox recreate ...` command.
```json5
{
agents: {
defaults: {
sandbox: {
mode: "non-main", // off | non-main | all
scope: "agent", // session | agent | shared (agent is default)
workspaceAccess: "none", // none | ro | rw
workspaceRoot: "~/.openclaw/sandboxes",
mode: 'non-main', // off | non-main | all
scope: 'agent', // session | agent | shared (agent is default)
workspaceAccess: 'none', // none | ro | rw
workspaceRoot: '~/.openclaw/sandboxes',
docker: {
image: "openclaw-sandbox:bookworm-slim",
workdir: "/workspace",
image: 'openclaw-sandbox:bookworm-slim',
workdir: '/workspace',
readOnlyRoot: true,
tmpfs: ["/tmp", "/var/tmp", "/run"],
network: "none",
user: "1000:1000",
capDrop: ["ALL"],
env: { LANG: "C.UTF-8" },
setupCommand: "apt-get update && apt-get install -y git curl jq",
tmpfs: ['/tmp', '/var/tmp', '/run'],
network: 'none',
user: '1000:1000',
capDrop: ['ALL'],
env: { LANG: 'C.UTF-8' },
setupCommand: 'apt-get update && apt-get install -y git curl jq',
pidsLimit: 256,
memory: "1g",
memorySwap: "2g",
memory: '1g',
memorySwap: '2g',
cpus: 1,
ulimits: {
nofile: { soft: 1024, hard: 2048 },
nproc: 256
nproc: 256,
},
seccompProfile: "/path/to/seccomp.json",
apparmorProfile: "openclaw-sandbox",
dns: ["1.1.1.1", "8.8.8.8"],
extraHosts: ["internal.service:10.0.0.5"]
seccompProfile: '/path/to/seccomp.json',
apparmorProfile: 'openclaw-sandbox',
dns: ['1.1.1.1', '8.8.8.8'],
extraHosts: ['internal.service:10.0.0.5'],
},
prune: {
idleHours: 24, // 0 disables idle pruning
maxAgeDays: 7 // 0 disables max-age pruning
}
}
}
maxAgeDays: 7, // 0 disables max-age pruning
},
},
},
},
tools: {
sandbox: {
tools: {
allow: ["exec", "process", "read", "write", "edit", "sessions_list", "sessions_history", "sessions_send", "sessions_spawn", "session_status"],
deny: ["browser", "canvas", "nodes", "cron", "discord", "gateway"]
}
}
}
allow: [
'exec',
'process',
'read',
'write',
'edit',
'sessions_list',
'sessions_history',
'sessions_send',
'sessions_spawn',
'session_status',
],
deny: ['browser', 'canvas', 'nodes', 'cron', 'discord', 'gateway'],
},
},
},
}
```
@@ -328,6 +353,7 @@ scripts/sandbox-setup.sh
This builds `openclaw-sandbox:bookworm-slim` using `Dockerfile.sandbox`.
### Sandbox common image (optional)
If you want a sandbox image with common build tooling (Node, Go, Rust, etc.), build the common image:
```bash
@@ -338,7 +364,11 @@ This builds `openclaw-sandbox-common:bookworm-slim`. To use it:
```json5
{
agents: { defaults: { sandbox: { docker: { image: "openclaw-sandbox-common:bookworm-slim" } } } }
agents: {
defaults: {
sandbox: { docker: { image: 'openclaw-sandbox-common:bookworm-slim' } },
},
},
}
```
@@ -355,6 +385,7 @@ This builds `openclaw-sandbox-browser:bookworm-slim` using
an optional noVNC observer (headful via Xvfb).
Notes:
- Headful (Xvfb) reduces bot blocking vs headless.
- Headless can still be used by setting `agents.defaults.sandbox.browser.headless=true`.
- No full desktop environment (GNOME) is needed; Xvfb provides the display.
@@ -366,10 +397,10 @@ Use config:
agents: {
defaults: {
sandbox: {
browser: { enabled: true }
}
}
}
browser: { enabled: true },
},
},
},
}
```
@@ -379,13 +410,14 @@ Custom browser image:
{
agents: {
defaults: {
sandbox: { browser: { image: "my-openclaw-browser" } }
}
}
sandbox: { browser: { image: 'my-openclaw-browser' } },
},
},
}
```
When enabled, the agent receives:
- a sandbox browser control URL (for the `browser` tool)
- a noVNC URL (if enabled and headless=false)
@@ -405,9 +437,9 @@ docker build -t my-openclaw-sbx -f Dockerfile.sandbox .
{
agents: {
defaults: {
sandbox: { docker: { image: "my-openclaw-sbx" } }
}
}
sandbox: { docker: { image: 'my-openclaw-sbx' } },
},
},
}
```
@@ -420,10 +452,12 @@ docker build -t my-openclaw-sbx -f Dockerfile.sandbox .
### Pruning strategy
Two knobs:
- `prune.idleHours`: remove containers not used in X hours (0 = disable)
- `prune.maxAgeDays`: remove containers older than X days (0 = disable)
Example:
- Keep busy sessions but cap lifetime:
`idleHours: 24`, `maxAgeDays: 7`
- Never prune:
@@ -431,8 +465,8 @@ Example:
### Security notes
- Hard wall only applies to **tools** (exec/read/write/edit/apply_patch).
- Host-only tools like browser/camera/canvas are blocked by default.
- Hard wall only applies to **tools** (exec/read/write/edit/apply_patch).
- Host-only tools like browser/camera/canvas are blocked by default.
- Allowing `browser` in sandbox **breaks isolation** (browser runs on host).
## Troubleshooting

View File

@@ -57,7 +57,7 @@ primary_region = "iad"
NODE_OPTIONS = "--max-old-space-size=1536"
[processes]
app = "node dist/index.mjs gateway --allow-unconfigured --port 3000 --bind lan"
app = "node dist/index.js gateway --allow-unconfigured --port 3000 --bind lan"
[http_service]
internal_port = 3000
@@ -78,13 +78,13 @@ primary_region = "iad"
**Key settings:**
| Setting | Why |
|---------|-----|
| `--bind lan` | Binds to `0.0.0.0` so Fly's proxy can reach the gateway |
| `--allow-unconfigured` | Starts without a config file (you'll create one after) |
| `internal_port = 3000` | Must match `--port 3000` (or `OPENCLAW_GATEWAY_PORT`) for Fly health checks |
| `memory = "2048mb"` | 512MB is too small; 2GB recommended |
| `OPENCLAW_STATE_DIR = "/data"` | Persists state on the volume |
| Setting | Why |
| ------------------------------ | --------------------------------------------------------------------------- |
| `--bind lan` | Binds to `0.0.0.0` so Fly's proxy can reach the gateway |
| `--allow-unconfigured` | Starts without a config file (you'll create one after) |
| `internal_port = 3000` | Must match `--port 3000` (or `OPENCLAW_GATEWAY_PORT`) for Fly health checks |
| `memory = "2048mb"` | 512MB is too small; 2GB recommended |
| `OPENCLAW_STATE_DIR = "/data"` | Persists state on the volume |
## 3) Set secrets
@@ -104,6 +104,7 @@ fly secrets set DISCORD_BOT_TOKEN=MTQ...
```
**Notes:**
- Non-loopback binds (`--bind lan`) require `OPENCLAW_GATEWAY_TOKEN` for security.
- Treat these tokens like passwords.
- **Prefer env vars over config file** for all API keys and tokens. This keeps secrets out of `openclaw.json` where they could be accidentally exposed or logged.
@@ -117,12 +118,14 @@ fly deploy
First deploy builds the Docker image (~2-3 minutes). Subsequent deploys are faster.
After deployment, verify:
```bash
fly status
fly logs
```
You should see:
```
[gateway] listening on ws://0.0.0.0:3000 (PID xxx)
[discord] logged in to discord as xxx
@@ -137,6 +140,7 @@ fly ssh console
```
Create the config directory and file:
```bash
mkdir -p /data
cat > /data/openclaw.json << 'EOF'
@@ -194,12 +198,14 @@ EOF
**Note:** With `OPENCLAW_STATE_DIR=/data`, the config path is `/data/openclaw.json`.
**Note:** The Discord token can come from either:
- Environment variable: `DISCORD_BOT_TOKEN` (recommended for secrets)
- Config file: `channels.discord.token`
If using env var, no need to add token to config. The gateway reads `DISCORD_BOT_TOKEN` automatically.
Restart to apply:
```bash
exit
fly machine restart <machine-id>
@@ -210,6 +216,7 @@ fly machine restart <machine-id>
### Control UI
Open in browser:
```bash
fly open
```
@@ -250,12 +257,14 @@ Fly can't reach the gateway on the configured port.
Container keeps restarting or getting killed. Signs: `SIGABRT`, `v8::internal::Runtime_AllocateInYoungGeneration`, or silent restarts.
**Fix:** Increase memory in `fly.toml`:
```toml
[[vm]]
memory = "2048mb"
```
Or update an existing machine:
```bash
fly machine update <machine-id> --vm-memory 2048 -y
```
@@ -269,6 +278,7 @@ Gateway refuses to start with "already running" errors.
This happens when the container restarts but the PID lock file persists on the volume.
**Fix:** Delete the lock file:
```bash
fly ssh console --command "rm -f /data/gateway.*.lock"
fly machine restart <machine-id>
@@ -281,6 +291,7 @@ The lock file is at `/data/gateway.*.lock` (not in a subdirectory).
If using `--allow-unconfigured`, the gateway creates a minimal config. Your custom config at `/data/openclaw.json` should be read on restart.
Verify the config exists:
```bash
fly ssh console --command "cat /data/openclaw.json"
```
@@ -299,6 +310,7 @@ fly sftp shell
```
**Note:** `fly sftp` may fail if the file already exists. Delete first:
```bash
fly ssh console --command "rm /data/openclaw.json"
```
@@ -332,10 +344,10 @@ If you need to change the startup command without a full redeploy:
fly machines list
# Update command
fly machine update <machine-id> --command "node dist/index.mjs gateway --port 3000 --bind lan" -y
fly machine update <machine-id> --command "node dist/index.js gateway --port 3000 --bind lan" -y
# Or with memory increase
fly machine update <machine-id> --vm-memory 2048 --command "node dist/index.mjs gateway --port 3000 --bind lan" -y
fly machine update <machine-id> --vm-memory 2048 --command "node dist/index.js gateway --port 3000 --bind lan" -y
```
**Note:** After `fly deploy`, the machine command may reset to what's in `fly.toml`. If you made manual changes, re-apply them after deploy.
@@ -381,6 +393,7 @@ fly ips allocate-v6 --private -a my-openclaw
```
After this, `fly ips list` should show only a `private` type IP:
```
VERSION IP TYPE REGION
v6 fdaa:x:x:x:x::x private global
@@ -391,6 +404,7 @@ v6 fdaa:x:x:x:x::x private global
Since there's no public URL, use one of these methods:
**Option 1: Local proxy (simplest)**
```bash
# Forward local port 3000 to the app
fly proxy 3000:3000 -a my-openclaw
@@ -399,6 +413,7 @@ fly proxy 3000:3000 -a my-openclaw
```
**Option 2: WireGuard VPN**
```bash
# Create WireGuard config (one-time)
fly wireguard create
@@ -408,6 +423,7 @@ fly wireguard create
```
**Option 3: SSH only**
```bash
fly ssh console -a my-openclaw
```
@@ -421,6 +437,7 @@ If you need webhook callbacks (Twilio, Telnyx, etc.) without public exposure:
3. **Outbound-only** - Some providers (Twilio) work fine for outbound calls without webhooks
Example voice-call config with ngrok:
```json
{
"plugins": {
@@ -441,12 +458,12 @@ The ngrok tunnel runs inside the container and provides a public webhook URL wit
### Security benefits
| Aspect | Public | Private |
|--------|--------|---------|
| Internet scanners | Discoverable | Hidden |
| Direct attacks | Possible | Blocked |
| Control UI access | Browser | Proxy/VPN |
| Webhook delivery | Direct | Via tunnel |
| Aspect | Public | Private |
| ----------------- | ------------ | ---------- |
| Internet scanners | Discoverable | Hidden |
| Direct attacks | Possible | Blocked |
| Control UI access | Browser | Proxy/VPN |
| Webhook delivery | Direct | Via tunnel |
## Notes
@@ -459,6 +476,7 @@ The ngrok tunnel runs inside the container and provides a public webhook URL wit
## Cost
With the recommended config (`shared-cpu-2x`, 2GB RAM):
- ~$10-15/month depending on usage
- Free tier includes some allowance

View File

@@ -1,5 +1,5 @@
---
summary: "Run OpenClaw Gateway 24/7 on a GCP Compute Engine VM (Docker) with durable state"
summary: 'Run OpenClaw Gateway 24/7 on a GCP Compute Engine VM (Docker) with durable state'
read_when:
- You want OpenClaw running 24/7 on GCP
- You want a production-grade, always-on Gateway on your own VM
@@ -25,6 +25,7 @@ Pricing varies by machine type and region; pick the smallest VM that fits your w
- Access the Control UI from your laptop via an SSH tunnel
The Gateway can be accessed via:
- SSH port forwarding from your laptop
- Direct port exposure if you manage firewalling and tokens yourself
@@ -36,14 +37,14 @@ For the generic Docker flow, see [Docker](/install/docker).
## Quick path (experienced operators)
1) Create GCP project + enable Compute Engine API
2) Create Compute Engine VM (e2-small, Debian 12, 20GB)
3) SSH into the VM
4) Install Docker
5) Clone OpenClaw repository
6) Create persistent host directories
7) Configure `.env` and `docker-compose.yml`
8) Bake required binaries, build, and launch
1. Create GCP project + enable Compute Engine API
2. Create Compute Engine VM (e2-small, Debian 12, 20GB)
3. SSH into the VM
4. Install Docker
5. Clone OpenClaw repository
6. Create persistent host directories
7. Configure `.env` and `docker-compose.yml`
8. Bake required binaries, build, and launch
---
@@ -112,9 +113,9 @@ gcloud services enable compute.googleapis.com
**Machine types:**
| Type | Specs | Cost | Notes |
|------|-------|------|-------|
| e2-small | 2 vCPU, 2GB RAM | ~$12/mo | Recommended |
| Type | Specs | Cost | Notes |
| -------- | ------------------------ | ------------------ | ------------------ |
| e2-small | 2 vCPU, 2GB RAM | ~$12/mo | Recommended |
| e2-micro | 2 vCPU (shared), 1GB RAM | Free tier eligible | May OOM under load |
**CLI:**
@@ -263,20 +264,20 @@ services:
ports:
# Recommended: keep the Gateway loopback-only on the VM; access via SSH tunnel.
# To expose it publicly, remove the `127.0.0.1:` prefix and firewall accordingly.
- "127.0.0.1:${OPENCLAW_GATEWAY_PORT}:18789"
- '127.0.0.1:${OPENCLAW_GATEWAY_PORT}:18789'
# Optional: only if you run iOS/Android nodes against this VM and need Canvas host.
# If you expose this publicly, read /gateway/security and firewall accordingly.
# - "18793:18793"
command:
[
"node",
"dist/index.mjs",
"gateway",
"--bind",
"${OPENCLAW_GATEWAY_BIND}",
"--port",
"${OPENCLAW_GATEWAY_PORT}"
'node',
'dist/index.js',
'gateway',
'--bind',
'${OPENCLAW_GATEWAY_BIND}',
'--port',
'${OPENCLAW_GATEWAY_PORT}',
]
```
@@ -290,6 +291,7 @@ Anything installed at runtime will be lost on restart.
All external binaries required by skills must be installed at image build time.
The examples below show three common binaries only:
- `gog` for Gmail access
- `goplaces` for Google Places
- `wacli` for WhatsApp
@@ -298,6 +300,7 @@ These are examples, not a complete list.
You may install as many binaries as needed using the same pattern.
If you add new skills later that depend on additional binaries, you must:
1. Update the Dockerfile
2. Rebuild the image
3. Restart the containers
@@ -338,7 +341,7 @@ RUN pnpm ui:build
ENV NODE_ENV=production
CMD ["node","dist/index.mjs"]
CMD ["node","dist/index.js"]
```
---
@@ -403,18 +406,18 @@ Paste your gateway token.
OpenClaw runs in Docker, but Docker is not the source of truth.
All long-lived state must survive restarts, rebuilds, and reboots.
| Component | Location | Persistence mechanism | Notes |
|---|---|---|---|
| Gateway config | `/home/node/.openclaw/` | Host volume mount | Includes `openclaw.json`, tokens |
| Model auth profiles | `/home/node/.openclaw/` | Host volume mount | OAuth tokens, API keys |
| Skill configs | `/home/node/.openclaw/skills/` | Host volume mount | Skill-level state |
| Agent workspace | `/home/node/.openclaw/workspace/` | Host volume mount | Code and agent artifacts |
| WhatsApp session | `/home/node/.openclaw/` | Host volume mount | Preserves QR login |
| Gmail keyring | `/home/node/.openclaw/` | Host volume + password | Requires `GOG_KEYRING_PASSWORD` |
| External binaries | `/usr/local/bin/` | Docker image | Must be baked at build time |
| Node runtime | Container filesystem | Docker image | Rebuilt every image build |
| OS packages | Container filesystem | Docker image | Do not install at runtime |
| Docker container | Ephemeral | Restartable | Safe to destroy |
| Component | Location | Persistence mechanism | Notes |
| ------------------- | --------------------------------- | ---------------------- | -------------------------------- |
| Gateway config | `/home/node/.openclaw/` | Host volume mount | Includes `openclaw.json`, tokens |
| Model auth profiles | `/home/node/.openclaw/` | Host volume mount | OAuth tokens, API keys |
| Skill configs | `/home/node/.openclaw/skills/` | Host volume mount | Skill-level state |
| Agent workspace | `/home/node/.openclaw/workspace/` | Host volume mount | Code and agent artifacts |
| WhatsApp session | `/home/node/.openclaw/` | Host volume mount | Preserves QR login |
| Gmail keyring | `/home/node/.openclaw/` | Host volume + password | Requires `GOG_KEYRING_PASSWORD` |
| External binaries | `/usr/local/bin/` | Docker image | Must be baked at build time |
| Node runtime | Container filesystem | Docker image | Rebuilt every image build |
| OS packages | Container filesystem | Docker image | Do not install at runtime |
| Docker container | Ephemeral | Restartable | Safe to destroy |
---
@@ -473,6 +476,7 @@ For personal use, your default user account works fine.
For automation or CI/CD pipelines, create a dedicated service account with minimal permissions:
1. Create a service account:
```bash
gcloud iam service-accounts create openclaw-deploy \
--display-name="OpenClaw Deployment"

View File

@@ -1,5 +1,5 @@
---
summary: "Run OpenClaw Gateway 24/7 on a cheap Hetzner VPS (Docker) with durable state and baked-in binaries"
summary: 'Run OpenClaw Gateway 24/7 on a cheap Hetzner VPS (Docker) with durable state and baked-in binaries'
read_when:
- You want OpenClaw running 24/7 on a cloud VPS (not your laptop)
- You want a production-grade, always-on Gateway on your own VPS
@@ -10,6 +10,7 @@ read_when:
# OpenClaw on Hetzner (Docker, Production VPS Guide)
## Goal
Run a persistent OpenClaw Gateway on a Hetzner VPS using Docker, with durable state, baked-in binaries, and safe restart behavior.
If you want “OpenClaw 24/7 for ~$5”, this is the simplest reliable setup.
@@ -24,6 +25,7 @@ Hetzner pricing changes; pick the smallest Debian/Ubuntu VPS and scale up if you
- Access the Control UI from your laptop via an SSH tunnel
The Gateway can be accessed via:
- SSH port forwarding from your laptop
- Direct port exposure if you manage firewalling and tokens yourself
@@ -35,29 +37,29 @@ For the generic Docker flow, see [Docker](/install/docker).
## Quick path (experienced operators)
1) Provision Hetzner VPS
2) Install Docker
3) Clone OpenClaw repository
4) Create persistent host directories
5) Configure `.env` and `docker-compose.yml`
6) Bake required binaries into the image
7) `docker compose up -d`
8) Verify persistence and Gateway access
1. Provision Hetzner VPS
2. Install Docker
3. Clone OpenClaw repository
4. Create persistent host directories
5. Configure `.env` and `docker-compose.yml`
6. Bake required binaries into the image
7. `docker compose up -d`
8. Verify persistence and Gateway access
---
## What you need
- Hetzner VPS with root access
- SSH access from your laptop
- Basic comfort with SSH + copy/paste
- ~20 minutes
- Docker and Docker Compose
- Model auth credentials
- Optional provider credentials
- WhatsApp QR
- Telegram bot token
- Gmail OAuth
- Hetzner VPS with root access
- SSH access from your laptop
- Basic comfort with SSH + copy/paste
- ~20 minutes
- Docker and Docker Compose
- Model auth credentials
- Optional provider credentials
- WhatsApp QR
- Telegram bot token
- Gmail OAuth
---
@@ -175,20 +177,20 @@ services:
ports:
# Recommended: keep the Gateway loopback-only on the VPS; access via SSH tunnel.
# To expose it publicly, remove the `127.0.0.1:` prefix and firewall accordingly.
- "127.0.0.1:${OPENCLAW_GATEWAY_PORT}:18789"
- '127.0.0.1:${OPENCLAW_GATEWAY_PORT}:18789'
# Optional: only if you run iOS/Android nodes against this VPS and need Canvas host.
# If you expose this publicly, read /gateway/security and firewall accordingly.
# - "18793:18793"
command:
[
"node",
"dist/index.mjs",
"gateway",
"--bind",
"${OPENCLAW_GATEWAY_BIND}",
"--port",
"${OPENCLAW_GATEWAY_PORT}"
'node',
'dist/index.js',
'gateway',
'--bind',
'${OPENCLAW_GATEWAY_BIND}',
'--port',
'${OPENCLAW_GATEWAY_PORT}',
]
```
@@ -202,6 +204,7 @@ Anything installed at runtime will be lost on restart.
All external binaries required by skills must be installed at image build time.
The examples below show three common binaries only:
- `gog` for Gmail access
- `goplaces` for Google Places
- `wacli` for WhatsApp
@@ -210,6 +213,7 @@ These are examples, not a complete list.
You may install as many binaries as needed using the same pattern.
If you add new skills later that depend on additional binaries, you must:
1. Update the Dockerfile
2. Rebuild the image
3. Restart the containers
@@ -250,7 +254,7 @@ RUN pnpm ui:build
ENV NODE_ENV=production
CMD ["node","dist/index.mjs"]
CMD ["node","dist/index.js"]
```
---
@@ -311,15 +315,15 @@ Paste your gateway token.
OpenClaw runs in Docker, but Docker is not the source of truth.
All long-lived state must survive restarts, rebuilds, and reboots.
| Component | Location | Persistence mechanism | Notes |
|---|---|---|---|
| Gateway config | `/home/node/.openclaw/` | Host volume mount | Includes `openclaw.json`, tokens |
| Model auth profiles | `/home/node/.openclaw/` | Host volume mount | OAuth tokens, API keys |
| Skill configs | `/home/node/.openclaw/skills/` | Host volume mount | Skill-level state |
| Agent workspace | `/home/node/.openclaw/workspace/` | Host volume mount | Code and agent artifacts |
| WhatsApp session | `/home/node/.openclaw/` | Host volume mount | Preserves QR login |
| Gmail keyring | `/home/node/.openclaw/` | Host volume + password | Requires `GOG_KEYRING_PASSWORD` |
| External binaries | `/usr/local/bin/` | Docker image | Must be baked at build time |
| Node runtime | Container filesystem | Docker image | Rebuilt every image build |
| OS packages | Container filesystem | Docker image | Do not install at runtime |
| Docker container | Ephemeral | Restartable | Safe to destroy |
| Component | Location | Persistence mechanism | Notes |
| ------------------- | --------------------------------- | ---------------------- | -------------------------------- |
| Gateway config | `/home/node/.openclaw/` | Host volume mount | Includes `openclaw.json`, tokens |
| Model auth profiles | `/home/node/.openclaw/` | Host volume mount | OAuth tokens, API keys |
| Skill configs | `/home/node/.openclaw/skills/` | Host volume mount | Skill-level state |
| Agent workspace | `/home/node/.openclaw/workspace/` | Host volume mount | Code and agent artifacts |
| WhatsApp session | `/home/node/.openclaw/` | Host volume mount | Preserves QR login |
| Gmail keyring | `/home/node/.openclaw/` | Host volume + password | Requires `GOG_KEYRING_PASSWORD` |
| External binaries | `/usr/local/bin/` | Docker image | Must be baked at build time |
| Node runtime | Container filesystem | Docker image | Rebuilt every image build |
| OS packages | Container filesystem | Docker image | Do not install at runtime |
| Docker container | Ephemeral | Restartable | Safe to destroy |

View File

@@ -1,9 +1,10 @@
---
summary: "OpenClaw plugins/extensions: discovery, config, and safety"
summary: 'OpenClaw plugins/extensions: discovery, config, and safety'
read_when:
- Adding or modifying plugins/extensions
- Documenting plugin install or load rules
---
# Plugins (Extensions)
## Quick start (new to plugins?)
@@ -17,19 +18,19 @@ install).
Fast path:
1) See whats already loaded:
1. See whats already loaded:
```bash
openclaw plugins list
```
2) Install an official plugin (example: Voice Call):
2. Install an official plugin (example: Voice Call):
```bash
openclaw plugins install @openclaw/voice-call
```
3) Restart the Gateway, then configure under `plugins.entries.<id>.config`.
3. Restart the Gateway, then configure under `plugins.entries.<id>.config`.
See [Voice Call](/plugins/voice-call) for a concrete example plugin.
@@ -73,12 +74,13 @@ Plugins can access selected core helpers via `api.runtime`. For telephony TTS:
```ts
const result = await api.runtime.tts.textToSpeechTelephony({
text: "Hello from OpenClaw",
text: 'Hello from OpenClaw',
cfg: api.config,
});
```
Notes:
- Uses core `messages.tts` configuration (OpenAI or ElevenLabs).
- Returns PCM audio buffer + sample rate. Plugins must resample/encode for providers.
- Edge TTS is not supported for telephony.
@@ -87,18 +89,22 @@ Notes:
OpenClaw scans, in order:
1) Config paths
1. Config paths
- `plugins.load.paths` (file or directory)
2) Workspace extensions
2. Workspace extensions
- `<workspace>/.openclaw/extensions/*.ts`
- `<workspace>/.openclaw/extensions/*/index.ts`
3) Global extensions
3. Global extensions
- `~/.openclaw/extensions/*.ts`
- `~/.openclaw/extensions/*/index.ts`
4) Bundled extensions (shipped with OpenClaw, **disabled by default**)
4. Bundled extensions (shipped with OpenClaw, **disabled by default**)
- `<openclaw>/extensions/*`
Bundled plugins must be enabled explicitly via `plugins.entries.<id>.enabled`
@@ -164,6 +170,7 @@ Example:
OpenClaw can also merge **external channel catalogs** (for example, an MPM
registry export). Drop a JSON file at one of:
- `~/.openclaw/mpm/plugins.json`
- `~/.openclaw/mpm/catalog.json`
- `~/.openclaw/plugins/catalog.json`
@@ -188,17 +195,18 @@ configured id.
{
plugins: {
enabled: true,
allow: ["voice-call"],
deny: ["untrusted-plugin"],
load: { paths: ["~/Projects/oss/voice-call-extension"] },
allow: ['voice-call'],
deny: ['untrusted-plugin'],
load: { paths: ['~/Projects/oss/voice-call-extension'] },
entries: {
"voice-call": { enabled: true, config: { provider: "twilio" } }
}
}
'voice-call': { enabled: true, config: { provider: 'twilio' } },
},
},
}
```
Fields:
- `enabled`: master toggle (default: true)
- `allow`: allowlist (optional)
- `deny`: denylist (optional; deny wins)
@@ -208,6 +216,7 @@ Fields:
Config changes **require a gateway restart**.
Validation rules (strict):
- Unknown plugin ids in `entries`, `allow`, `deny`, or `slots` are **errors**.
- Unknown `channels.<id>` keys are **errors** unless a plugin manifest declares
the channel id.
@@ -224,9 +233,9 @@ Some plugin categories are **exclusive** (only one active at a time). Use
{
plugins: {
slots: {
memory: "memory-core" // or "none" to disable memory plugins
}
}
memory: 'memory-core', // or "none" to disable memory plugins
},
},
}
```
@@ -311,6 +320,7 @@ export default function register(api) {
```
Notes:
- Hook directories follow the normal hook structure (`HOOK.md` + `handler.ts`).
- Hook eligibility rules still apply (OS/bins/env/config requirements).
- Plugin-managed hooks show up in `openclaw hooks list` with `plugin:<id>`.
@@ -330,29 +340,29 @@ Example:
```ts
api.registerProvider({
id: "acme",
label: "AcmeAI",
id: 'acme',
label: 'AcmeAI',
auth: [
{
id: "oauth",
label: "OAuth",
kind: "oauth",
id: 'oauth',
label: 'OAuth',
kind: 'oauth',
run: async (ctx) => {
// Run OAuth flow and return auth profiles.
return {
profiles: [
{
profileId: "acme:default",
profileId: 'acme:default',
credential: {
type: "oauth",
provider: "acme",
access: "...",
refresh: "...",
type: 'oauth',
provider: 'acme',
access: '...',
refresh: '...',
expires: Date.now() + 3600 * 1000,
},
},
],
defaultModel: "acme/opus-1",
defaultModel: 'acme/opus-1',
};
},
},
@@ -361,6 +371,7 @@ api.registerProvider({
```
Notes:
- `run` receives a `ProviderAuthContext` with `prompter`, `runtime`,
`openUrl`, and `oauth.createVpsAwareHandlers` helpers.
- Return `configPatch` when you need to add default models or provider config.
@@ -374,23 +385,26 @@ validated by your channel plugin code.
```ts
const myChannel = {
id: "acmechat",
id: 'acmechat',
meta: {
id: "acmechat",
label: "AcmeChat",
selectionLabel: "AcmeChat (API)",
docsPath: "/channels/acmechat",
blurb: "demo channel plugin.",
aliases: ["acme"],
id: 'acmechat',
label: 'AcmeChat',
selectionLabel: 'AcmeChat (API)',
docsPath: '/channels/acmechat',
blurb: 'demo channel plugin.',
aliases: ['acme'],
},
capabilities: { chatTypes: ["direct"] },
capabilities: { chatTypes: ['direct'] },
config: {
listAccountIds: (cfg) => Object.keys(cfg.channels?.acmechat?.accounts ?? {}),
listAccountIds: (cfg) =>
Object.keys(cfg.channels?.acmechat?.accounts ?? {}),
resolveAccount: (cfg, accountId) =>
(cfg.channels?.acmechat?.accounts?.[accountId ?? "default"] ?? { accountId }),
cfg.channels?.acmechat?.accounts?.[accountId ?? 'default'] ?? {
accountId,
},
},
outbound: {
deliveryMode: "direct",
deliveryMode: 'direct',
sendText: async () => ({ ok: true }),
},
};
@@ -401,6 +415,7 @@ export default function (api) {
```
Notes:
- Put config under `channels.<id>` (not `plugins.entries`).
- `meta.label` is used for labels in CLI/UI lists.
- `meta.aliases` adds alternate ids for normalization and CLI inputs.
@@ -412,27 +427,32 @@ Notes:
Use this when you want a **new chat surface** (a “messaging channel”), not a model provider.
Model provider docs live under `/providers/*`.
1) Pick an id + config shape
1. Pick an id + config shape
- All channel config lives under `channels.<id>`.
- Prefer `channels.<id>.accounts.<accountId>` for multiaccount setups.
2) Define the channel metadata
2. Define the channel metadata
- `meta.label`, `meta.selectionLabel`, `meta.docsPath`, `meta.blurb` control CLI/UI lists.
- `meta.docsPath` should point at a docs page like `/channels/<id>`.
- `meta.preferOver` lets a plugin replace another channel (auto-enable prefers it).
- `meta.detailLabel` and `meta.systemImage` are used by UIs for detail text/icons.
3) Implement the required adapters
3. Implement the required adapters
- `config.listAccountIds` + `config.resolveAccount`
- `capabilities` (chat types, media, threads, etc.)
- `outbound.deliveryMode` + `outbound.sendText` (for basic send)
4) Add optional adapters as needed
4. Add optional adapters as needed
- `setup` (wizard), `security` (DM policy), `status` (health/diagnostics)
- `gateway` (start/stop/login), `mentions`, `threading`, `streaming`
- `actions` (message actions), `commands` (native command behavior)
5) Register the channel in your plugin
5. Register the channel in your plugin
- `api.registerChannel({ plugin })`
Minimal config example:
@@ -442,10 +462,10 @@ Minimal config example:
channels: {
acmechat: {
accounts: {
default: { token: "ACME_TOKEN", enabled: true }
}
}
}
default: { token: 'ACME_TOKEN', enabled: true },
},
},
},
}
```
@@ -453,23 +473,26 @@ Minimal channel plugin (outboundonly):
```ts
const plugin = {
id: "acmechat",
id: 'acmechat',
meta: {
id: "acmechat",
label: "AcmeChat",
selectionLabel: "AcmeChat (API)",
docsPath: "/channels/acmechat",
blurb: "AcmeChat messaging channel.",
aliases: ["acme"],
id: 'acmechat',
label: 'AcmeChat',
selectionLabel: 'AcmeChat (API)',
docsPath: '/channels/acmechat',
blurb: 'AcmeChat messaging channel.',
aliases: ['acme'],
},
capabilities: { chatTypes: ["direct"] },
capabilities: { chatTypes: ['direct'] },
config: {
listAccountIds: (cfg) => Object.keys(cfg.channels?.acmechat?.accounts ?? {}),
listAccountIds: (cfg) =>
Object.keys(cfg.channels?.acmechat?.accounts ?? {}),
resolveAccount: (cfg, accountId) =>
(cfg.channels?.acmechat?.accounts?.[accountId ?? "default"] ?? { accountId }),
cfg.channels?.acmechat?.accounts?.[accountId ?? 'default'] ?? {
accountId,
},
},
outbound: {
deliveryMode: "direct",
deliveryMode: 'direct',
sendText: async ({ text }) => {
// deliver `text` to your channel here
return { ok: true };
@@ -493,7 +516,7 @@ See the dedicated guide: [Plugin agent tools](/plugins/agent-tools).
```ts
export default function (api) {
api.registerGatewayMethod("myplugin.status", ({ respond }) => {
api.registerGatewayMethod('myplugin.status', ({ respond }) => {
respond(true, { ok: true });
});
}
@@ -503,11 +526,14 @@ export default function (api) {
```ts
export default function (api) {
api.registerCli(({ program }) => {
program.command("mycmd").action(() => {
console.log("Hello");
});
}, { commands: ["mycmd"] });
api.registerCli(
({ program }) => {
program.command('mycmd').action(() => {
console.log('Hello');
});
},
{ commands: ['mycmd'] },
);
}
```
@@ -520,8 +546,8 @@ that don't need LLM processing.
```ts
export default function (api) {
api.registerCommand({
name: "mystatus",
description: "Show plugin status",
name: 'mystatus',
description: 'Show plugin status',
handler: (ctx) => ({
text: `Plugin is running! Channel: ${ctx.channel}`,
}),
@@ -550,12 +576,12 @@ Example with authorization and arguments:
```ts
api.registerCommand({
name: "setmode",
description: "Set plugin mode",
name: 'setmode',
description: 'Set plugin mode',
acceptsArgs: true,
requireAuth: true,
handler: async (ctx) => {
const mode = ctx.args?.trim() || "default";
const mode = ctx.args?.trim() || 'default';
await saveMode(mode);
return { text: `Mode set to: ${mode}` };
},
@@ -563,6 +589,7 @@ api.registerCommand({
```
Notes:
- Plugin commands are processed **before** built-in commands and the AI agent
- Commands are registered globally and work across all channels
- Command names are case-insensitive (`/MyStatus` matches `/mystatus`)
@@ -575,9 +602,9 @@ Notes:
```ts
export default function (api) {
api.registerService({
id: "my-service",
start: () => api.logger.info("ready"),
stop: () => api.logger.info("bye"),
id: 'my-service',
start: () => api.logger.info('ready'),
stop: () => api.logger.info('bye'),
});
}
```
@@ -635,4 +662,4 @@ Plugins run in-process with the Gateway. Treat them as trusted code:
Plugins can (and should) ship tests:
- In-repo plugins can keep Vitest tests under `src/**` (example: `src/plugins/voice-call.plugin.test.ts`).
- Separately published plugins should run their own CI (lint/build/test) and validate `openclaw.extensions` points at the built entrypoint (`dist/index.mjs`).
- Separately published plugins should run their own CI (lint/build/test) and validate `openclaw.extensions` points at the built entrypoint (`dist/index.js`).

View File

@@ -9,20 +9,20 @@
#
# See https://fly.io/docs/reference/configuration/
app = "my-openclaw" # change to your app name
primary_region = "iad" # change to your closest region
app = "my-openclaw" # change to your app name
primary_region = "iad" # change to your closest region
[build]
dockerfile = "Dockerfile"
dockerfile = "Dockerfile"
[env]
NODE_ENV = "production"
OPENCLAW_PREFER_PNPM = "1"
OPENCLAW_STATE_DIR = "/data"
NODE_OPTIONS = "--max-old-space-size=1536"
NODE_ENV = "production"
OPENCLAW_PREFER_PNPM = "1"
OPENCLAW_STATE_DIR = "/data"
NODE_OPTIONS = "--max-old-space-size=1536"
[processes]
app = "node dist/index.mjs gateway --allow-unconfigured --port 3000 --bind lan"
app = "node dist/index.js gateway --allow-unconfigured --port 3000 --bind lan"
# NOTE: No [http_service] block = no public ingress allocated.
# The gateway will only be accessible via:
@@ -31,9 +31,9 @@ primary_region = "iad" # change to your closest region
# - fly ssh console
[[vm]]
size = "shared-cpu-2x"
memory = "2048mb"
size = "shared-cpu-2x"
memory = "2048mb"
[mounts]
source = "openclaw_data"
destination = "/data"
source = "openclaw_data"
destination = "/data"

View File

@@ -2,33 +2,33 @@
# See https://fly.io/docs/reference/configuration/
app = "openclaw"
primary_region = "iad" # change to your closest region
primary_region = "iad" # change to your closest region
[build]
dockerfile = "Dockerfile"
dockerfile = "Dockerfile"
[env]
NODE_ENV = "production"
# Fly uses x86, but keep this for consistency
OPENCLAW_PREFER_PNPM = "1"
OPENCLAW_STATE_DIR = "/data"
NODE_OPTIONS = "--max-old-space-size=1536"
NODE_ENV = "production"
# Fly uses x86, but keep this for consistency
OPENCLAW_PREFER_PNPM = "1"
OPENCLAW_STATE_DIR = "/data"
NODE_OPTIONS = "--max-old-space-size=1536"
[processes]
app = "node dist/index.mjs gateway --allow-unconfigured --port 3000 --bind lan"
app = "node dist/index.js gateway --allow-unconfigured --port 3000 --bind lan"
[http_service]
internal_port = 3000
force_https = true
auto_stop_machines = false # Keep running for persistent connections
auto_start_machines = true
min_machines_running = 1
processes = ["app"]
internal_port = 3000
force_https = true
auto_stop_machines = false # Keep running for persistent connections
auto_start_machines = true
min_machines_running = 1
processes = ["app"]
[[vm]]
size = "shared-cpu-2x"
memory = "2048mb"
size = "shared-cpu-2x"
memory = "2048mb"
[mounts]
source = "openclaw_data"
destination = "/data"
source = "openclaw_data"
destination = "/data"

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env node
import module from "node:module";
import module from 'node:module';
// https://nodejs.org/api/module.html#module-compile-cache
if (module.enableCompileCache && !process.env.NODE_DISABLE_COMPILE_CACHE) {
@@ -11,4 +11,4 @@ if (module.enableCompileCache && !process.env.NODE_DISABLE_COMPILE_CACHE) {
}
}
await import("./dist/entry.mjs");
await import('./dist/entry.js');

View File

@@ -3,9 +3,9 @@
"version": "2026.1.29",
"description": "WhatsApp gateway CLI (Baileys web) with Pi RPC agent",
"type": "module",
"main": "dist/index.mjs",
"main": "dist/index.js",
"exports": {
".": "./dist/index.mjs",
".": "./dist/index.js",
"./plugin-sdk": "./dist/plugin-sdk/index.js",
"./plugin-sdk/*": "./dist/plugin-sdk/*",
"./cli-entry": "./openclaw.mjs"
@@ -85,7 +85,7 @@
"docs:bin": "node scripts/build-docs-list.mjs",
"docs:dev": "cd docs && mint dev",
"docs:build": "cd docs && pnpm dlx --reporter append-only mint broken-links",
"build": "pnpm canvas:a2ui:bundle && tsdown && node --import tsx scripts/canvas-a2ui-copy.ts && node --import tsx scripts/copy-hook-metadata.ts && node --import tsx scripts/write-build-info.ts",
"build": "pnpm canvas:a2ui:bundle && tsc -p tsconfig.json --noEmit false && node --import tsx scripts/canvas-a2ui-copy.ts && node --import tsx scripts/copy-hook-metadata.ts && node --import tsx scripts/write-build-info.ts",
"plugins:sync": "node --import tsx scripts/sync-plugin-versions.ts",
"release:check": "node --import tsx scripts/release-check.ts",
"ui:install": "node scripts/ui.js install",
@@ -227,7 +227,6 @@
"oxlint": "^1.42.0",
"oxlint-tsgolint": "^0.11.4",
"rolldown": "1.0.0-rc.2",
"tsdown": "^0.20.1",
"tsx": "^4.21.0",
"typescript": "^5.9.3",
"vitest": "^4.0.18"

424
pnpm-lock.yaml generated
View File

@@ -220,9 +220,6 @@ importers:
rolldown:
specifier: 1.0.0-rc.2
version: 1.0.0-rc.2
tsdown:
specifier: ^0.20.1
version: 0.20.1(@typescript/native-preview@7.0.0-dev.20260130.1)(typescript@5.9.3)
tsx:
specifier: ^4.21.0
version: 4.21.0
@@ -663,36 +660,19 @@ packages:
resolution: {integrity: sha512-XTmhdItcBckcVVTy65Xp+42xG4LX5GK+9AqAsXPXk4IqUNv+LyQo5TMwNjuFYBfAB2GTG9iSQGk+QLc03vhf3w==}
engines: {node: '>=16'}
'@babel/generator@8.0.0-beta.4':
resolution: {integrity: sha512-5xRfRZk6wx1BRu2XnTE8cTh2mx1ixrZ3/vpn7p/RCJpgctL6pexVVHE3eqtwlYvHhPAuOYCAlnsAyXpBdmfh5Q==}
engines: {node: ^20.19.0 || >=22.12.0}
'@babel/helper-string-parser@7.27.1':
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
engines: {node: '>=6.9.0'}
'@babel/helper-string-parser@8.0.0-beta.4':
resolution: {integrity: sha512-FGwbdQ/I2nJXXfyxa7dT0Fr/zPWwgX7m+hNVj0HrIHYJtyLxSQeQY1Kd8QkAYviQJV3OWFlRLuGd5epF03bdQg==}
engines: {node: ^20.19.0 || >=22.12.0}
'@babel/helper-validator-identifier@7.28.5':
resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
engines: {node: '>=6.9.0'}
'@babel/helper-validator-identifier@8.0.0-beta.4':
resolution: {integrity: sha512-6t0IaUEzlinbLmsGIvBZIHEJGjuchx+cMj+FbS78zL17tucYervgbwO07V5/CgBenVraontpmyMCTVyqCfxhFQ==}
engines: {node: ^20.19.0 || >=22.12.0}
'@babel/parser@7.28.6':
resolution: {integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==}
engines: {node: '>=6.0.0'}
hasBin: true
'@babel/parser@8.0.0-beta.4':
resolution: {integrity: sha512-fBcUqUN3eenLyg25QFkOwY1lmV6L0RdG92g6gxyS2CVCY8kHdibkQz1+zV3bLzxcvNnfHoi3i9n5Dci+g93acg==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
'@babel/runtime@7.28.6':
resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==}
engines: {node: '>=6.9.0'}
@@ -701,10 +681,6 @@ packages:
resolution: {integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==}
engines: {node: '>=6.9.0'}
'@babel/types@8.0.0-beta.4':
resolution: {integrity: sha512-xjk2xqYp25ePzAs0I08hN2lrbUDDQFfCjwq6MIEa8HwHa0WK8NfNtdvtXod8Ku2CbE1iui7qwWojGvjQiyrQeA==}
engines: {node: ^20.19.0 || >=22.12.0}
'@bcoe/v8-coverage@1.0.2':
resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==}
engines: {node: '>=18'}
@@ -1145,9 +1121,6 @@ packages:
resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
engines: {node: '>=18.0.0'}
'@jridgewell/gen-mapping@0.3.13':
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
'@jridgewell/resolve-uri@3.1.2':
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
engines: {node: '>=6.0.0'}
@@ -1822,9 +1795,6 @@ packages:
resolution: {integrity: sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==}
engines: {node: '>=14'}
'@oxc-project/types@0.110.0':
resolution: {integrity: sha512-6Ct21OIlrEnFEJk5LT4e63pk3btsI6/TusD/GStLi7wYlGJNOl1GI9qvXAnRAxQU9zqA2Oz+UwhfTOU2rPZVow==}
'@oxc-project/types@0.111.0':
resolution: {integrity: sha512-bh54LJMafgRGl2cPQ/QM+tI5rWaShm/wK9KywEj/w36MhiPKXYM67H2y3q+9pr4YO7ufwg2AKdBAZkhHBD8ClA==}
@@ -1978,9 +1948,6 @@ packages:
'@protobufjs/utf8@1.1.0':
resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
'@quansync/fs@1.0.0':
resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==}
'@reflink/reflink-darwin-arm64@0.1.19':
resolution: {integrity: sha512-ruy44Lpepdk1FqDz38vExBY/PVUsjxZA+chd9wozjUH9JjuDT/HEaQYA6wYN9mf041l0yLVar6BCZuWABJvHSA==}
engines: {node: '>= 10'}
@@ -2033,163 +2000,83 @@ packages:
resolution: {integrity: sha512-DmCG8GzysnCZ15bres3N5AHCmwBwYgp0As6xjhQ47rAUTUXxJiK+lLUxaGsX3hd/30qUpVElh05PbGuxRPgJwA==}
engines: {node: '>= 10'}
'@rolldown/binding-android-arm64@1.0.0-rc.1':
resolution: {integrity: sha512-He6ZoCfv5D7dlRbrhNBkuMVIHd0GDnjJwbICE1OWpG7G3S2gmJ+eXkcNLJjzjNDpeI2aRy56ou39AJM9AD8YFA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [android]
'@rolldown/binding-android-arm64@1.0.0-rc.2':
resolution: {integrity: sha512-AGV80viZ4Hil4C16GFH+PSwq10jclV9oyRFhD+5HdowPOCJ+G+99N5AClQvMkUMIahTY8cX0SQpKEEWcCg6fSA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [android]
'@rolldown/binding-darwin-arm64@1.0.0-rc.1':
resolution: {integrity: sha512-YzJdn08kSOXnj85ghHauH2iHpOJ6eSmstdRTLyaziDcUxe9SyQJgGyx/5jDIhDvtOcNvMm2Ju7m19+S/Rm1jFg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [darwin]
'@rolldown/binding-darwin-arm64@1.0.0-rc.2':
resolution: {integrity: sha512-PYR+PQu1mMmQiiKHN2JiOctvH32Xc/Mf+Su2RSmWtC9BbIqlqsVWjbulnShk0imjRim0IsbkMMCN5vYQwiuqaA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [darwin]
'@rolldown/binding-darwin-x64@1.0.0-rc.1':
resolution: {integrity: sha512-cIvAbqM+ZVV6lBSKSBtlNqH5iCiW933t1q8j0H66B3sjbe8AxIRetVqfGgcHcJtMzBIkIALlL9fcDrElWLJQcQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [darwin]
'@rolldown/binding-darwin-x64@1.0.0-rc.2':
resolution: {integrity: sha512-X2G36Z6oh5ynoYpE2JAyG+uQ4kO/3N7XydM/I98FNk8VVgDKjajFF+v7TXJ2FMq6xa7Xm0UIUKHW2MRQroqoUA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [darwin]
'@rolldown/binding-freebsd-x64@1.0.0-rc.1':
resolution: {integrity: sha512-rVt+B1B/qmKwCl1XD02wKfgh3vQPXRXdB/TicV2w6g7RVAM1+cZcpigwhLarqiVCxDObFZ7UgXCxPC7tpDoRog==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [freebsd]
'@rolldown/binding-freebsd-x64@1.0.0-rc.2':
resolution: {integrity: sha512-XpiFTsl9qjiDfrmJF6CE3dgj1nmSbxUIT+p2HIbXV6WOj/32btO8FKkWSsOphUwVinEt3R8HVkVrcLtFNruMMQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [freebsd]
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.1':
resolution: {integrity: sha512-69YKwJJBOFprQa1GktPgbuBOfnn+EGxu8sBJ1TjPER+zhSpYeaU4N07uqmyBiksOLGXsMegymuecLobfz03h8Q==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.2':
resolution: {integrity: sha512-zjYZ99e47Wlygs4hW+sQ+kshlO8ake9OoY2ecnJ9cwpDGiiIB9rQ3LgP3kt8j6IeVyMSksu//VEhc8Mrd1lRIw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
'@rolldown/binding-linux-arm64-gnu@1.0.0-rc.1':
resolution: {integrity: sha512-9JDhHUf3WcLfnViFWm+TyorqUtnSAHaCzlSNmMOq824prVuuzDOK91K0Hl8DUcEb9M5x2O+d2/jmBMsetRIn3g==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
'@rolldown/binding-linux-arm64-gnu@1.0.0-rc.2':
resolution: {integrity: sha512-Piso04EZ9IHV1aZSsLQVMOPTiCq4Ps2UPL3pchjNXHGJGFiB9U42s22LubPaEBFS+i6tCawS5EarIwex1zC4BA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
'@rolldown/binding-linux-arm64-musl@1.0.0-rc.1':
resolution: {integrity: sha512-UvApLEGholmxw/HIwmUnLq3CwdydbhaHHllvWiCTNbyGom7wTwOtz5OAQbAKZYyiEOeIXZNPkM7nA4Dtng7CLw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
'@rolldown/binding-linux-arm64-musl@1.0.0-rc.2':
resolution: {integrity: sha512-OwJCeMZlmjKsN9pfJfTmqYpe3JC+L6RO87+hu9ajRLr1Lh6cM2FRQ8e48DLRyRDww8Ti695XQvqEANEMmsuzLw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
'@rolldown/binding-linux-x64-gnu@1.0.0-rc.1':
resolution: {integrity: sha512-uVctNgZHiGnJx5Fij7wHLhgw4uyZBVi6mykeWKOqE7bVy9Hcxn0fM/IuqdMwk6hXlaf9fFShDTFz2+YejP+x0A==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
'@rolldown/binding-linux-x64-gnu@1.0.0-rc.2':
resolution: {integrity: sha512-uQqBmA8dTWbKvfqbeSsXNUssRGfdgQCc0hkGfhQN7Pf85wG2h0Fd/z2d+ykyT4YbcsjQdgEGxBNsg3v4ekOuEA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
'@rolldown/binding-linux-x64-musl@1.0.0-rc.1':
resolution: {integrity: sha512-T6Eg0xWwcxd/MzBcuv4Z37YVbUbJxy5cMNnbIt/Yr99wFwli30O4BPlY8hKeGyn6lWNtU0QioBS46lVzDN38bg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
'@rolldown/binding-linux-x64-musl@1.0.0-rc.2':
resolution: {integrity: sha512-ItZabVsICCYWHbP+jcAgNzjPAYg5GIVQp/NpqT6iOgWctaMYtobClc5m0kNtxwqfNrLXoyt998xUey4AvcxnGQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
'@rolldown/binding-openharmony-arm64@1.0.0-rc.1':
resolution: {integrity: sha512-PuGZVS2xNJyLADeh2F04b+Cz4NwvpglbtWACgrDOa5YDTEHKwmiTDjoD5eZ9/ptXtcpeFrMqD2H4Zn33KAh1Eg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [openharmony]
'@rolldown/binding-openharmony-arm64@1.0.0-rc.2':
resolution: {integrity: sha512-U4UYANwafcMXSUC0VqdrqTAgCo2v8T7SiuTYwVFXgia0KOl8jiv3okwCFqeZNuw/G6EWDiqhT8kK1DLgyLsxow==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [openharmony]
'@rolldown/binding-wasm32-wasi@1.0.0-rc.1':
resolution: {integrity: sha512-2mOxY562ihHlz9lEXuaGEIDCZ1vI+zyFdtsoa3M62xsEunDXQE+DVPO4S4x5MPK9tKulG/aFcA/IH5eVN257Cw==}
engines: {node: '>=14.0.0'}
cpu: [wasm32]
'@rolldown/binding-wasm32-wasi@1.0.0-rc.2':
resolution: {integrity: sha512-ZIWCjQsMon4tqRoao0Vzowjwx0cmFT3kublh2nNlgeasIJMWlIGHtr0d4fPypm57Rqx4o1h4L8SweoK2q6sMGA==}
engines: {node: '>=14.0.0'}
cpu: [wasm32]
'@rolldown/binding-win32-arm64-msvc@1.0.0-rc.1':
resolution: {integrity: sha512-oQVOP5cfAWZwRD0Q3nGn/cA9FW3KhMMuQ0NIndALAe6obqjLhqYVYDiGGRGrxvnjJsVbpLwR14gIUYnpIcHR1g==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [win32]
'@rolldown/binding-win32-arm64-msvc@1.0.0-rc.2':
resolution: {integrity: sha512-NIo7vwRUPEzZ4MuZGr5YbDdjJ84xdiG+YYf8ZBfTgvIsk9wM0sZamJPEXvaLkzVIHpOw5uqEHXS85Gqqb7aaqQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [win32]
'@rolldown/binding-win32-x64-msvc@1.0.0-rc.1':
resolution: {integrity: sha512-Ydsxxx++FNOuov3wCBPaYjZrEvKOOGq3k+BF4BPridhg2pENfitSRD2TEuQ8i33bp5VptuNdC9IzxRKU031z5A==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [win32]
'@rolldown/binding-win32-x64-msvc@1.0.0-rc.2':
resolution: {integrity: sha512-bLKzyLFbvngeNPZocuLo3LILrKwCrkyMxmRXs6fZYDrvh7cyZRw9v56maDL9ipPas0OOmQK1kAKYwvTs30G21Q==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [win32]
'@rolldown/pluginutils@1.0.0-rc.1':
resolution: {integrity: sha512-UTBjtTxVOhodhzFVp/ayITaTETRHPUPYZPXQe0WU0wOgxghMojXxYjOiPOauKIYNWJAWS2fd7gJgGQK8GU8vDA==}
'@rolldown/pluginutils@1.0.0-rc.2':
resolution: {integrity: sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==}
@@ -2649,9 +2536,6 @@ packages:
'@types/http-errors@2.0.5':
resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==}
'@types/jsesc@2.5.1':
resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==}
'@types/jsonwebtoken@9.0.10':
resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==}
@@ -2928,10 +2812,6 @@ packages:
resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
engines: {node: '>=12'}
ansis@4.2.0:
resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==}
engines: {node: '>=14'}
any-promise@1.3.0:
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
@@ -2972,10 +2852,6 @@ packages:
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
engines: {node: '>=12'}
ast-kit@3.0.0-beta.1:
resolution: {integrity: sha512-trmleAnZ2PxN/loHWVhhx1qeOHSRXq4TDsBBxq3GqeJitfk3+jTQ+v/C1km/KYq9M7wKqCewMh+/NAvVH7m+bw==}
engines: {node: '>=20.19.0'}
ast-types@0.13.4:
resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==}
engines: {node: '>=4'}
@@ -3041,9 +2917,6 @@ packages:
bignumber.js@9.3.1:
resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==}
birpc@4.0.0:
resolution: {integrity: sha512-LShSxJP0KTmd101b6DRyGBj57LZxSDYWKitQNW/mi8GRMvZb078Uf9+pveax1DrVL89vm7mWe+TovdI/UDOuPw==}
bluebird@3.7.2:
resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
@@ -3083,10 +2956,6 @@ packages:
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
engines: {node: '>= 0.8'}
cac@6.7.14:
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
engines: {node: '>=8'}
cacheable@2.3.2:
resolution: {integrity: sha512-w+ZuRNmex9c1TR9RcsxbfTKCjSL0rh1WA5SABbrWprIHeNBdmyQLSYonlDy9gpD+63XT8DgZ/wNh1Smvc9WnJA==}
@@ -3287,9 +3156,6 @@ packages:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
engines: {node: '>=0.10.0'}
defu@6.1.4:
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
degenerator@5.0.1:
resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==}
engines: {node: '>= 14'}
@@ -3343,15 +3209,6 @@ packages:
resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==}
engines: {node: '>=12'}
dts-resolver@2.1.3:
resolution: {integrity: sha512-bihc7jPC90VrosXNzK0LTE2cuLP6jr0Ro8jk+kMugHReJVLIpHz/xadeq3MhuwyO4TD4OA3L1Q8pBBFRc08Tsw==}
engines: {node: '>=20.19.0'}
peerDependencies:
oxc-resolver: '>=11.0.0'
peerDependenciesMeta:
oxc-resolver:
optional: true
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
@@ -3377,10 +3234,6 @@ packages:
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
empathic@2.0.0:
resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==}
engines: {node: '>=14'}
encodeurl@2.0.0:
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
engines: {node: '>= 0.8'}
@@ -3719,9 +3572,6 @@ packages:
resolution: {integrity: sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==}
engines: {node: '>=16.9.0'}
hookable@6.0.1:
resolution: {integrity: sha512-uKGyY8BuzN/a5gvzvA+3FVWo0+wUjgtfSdnmjtrOVwQCZPHpHDH2WRO3VZSOeluYrHoDCiXFffZXs8Dj1ULWtw==}
hookified@1.15.0:
resolution: {integrity: sha512-51w+ZZGt7Zw5q7rM3nC4t3aLn/xvKDETsXqMczndvwyVQhAHfUmUuFBRFcos8Iyebtk7OAE9dL26wFNzZVVOkw==}
@@ -3781,10 +3631,6 @@ packages:
import-in-the-middle@2.0.6:
resolution: {integrity: sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==}
import-without-cache@0.2.5:
resolution: {integrity: sha512-B6Lc2s6yApwnD2/pMzFh/d5AVjdsDXjgkeJ766FmFuJELIGHNycKRj+l3A39yZPM4CchqNCB4RITEAYB1KUM6A==}
engines: {node: '>=20.19.0'}
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
@@ -3892,11 +3738,6 @@ packages:
jsbn@0.1.1:
resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==}
jsesc@3.1.0:
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
engines: {node: '>=6'}
hasBin: true
json-bigint@1.0.0:
resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==}
@@ -4698,9 +4539,6 @@ packages:
resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==}
engines: {node: '>=0.6'}
quansync@1.0.0:
resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==}
quick-format-unescaped@4.0.4:
resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==}
@@ -4787,30 +4625,6 @@ packages:
resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==}
hasBin: true
rolldown-plugin-dts@0.21.8:
resolution: {integrity: sha512-czOQoe6eZpRKCv9P+ijO/v4A2TwQjASAV7qezUxRZSua06Yb2REPIZv/mbfXiZDP1ZfI7Ez7re7qfK9F9u0Epw==}
engines: {node: '>=20.19.0'}
peerDependencies:
'@ts-macro/tsc': ^0.3.6
'@typescript/native-preview': '>=7.0.0-dev.20250601.1'
rolldown: ^1.0.0-beta.57
typescript: ^5.0.0
vue-tsc: ~3.2.0
peerDependenciesMeta:
'@ts-macro/tsc':
optional: true
'@typescript/native-preview':
optional: true
typescript:
optional: true
vue-tsc:
optional: true
rolldown@1.0.0-rc.1:
resolution: {integrity: sha512-M3AeZjYE6UclblEf531Hch0WfVC/NOL43Cc+WdF3J50kk5/fvouHhDumSGTh0oRjbZ8C4faaVr5r6Nx1xMqDGg==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
rolldown@1.0.0-rc.2:
resolution: {integrity: sha512-1g/8Us9J8sgJGn3hZfBecX1z4U3y5KO7V/aV2U1M/9UUzLNqHA8RfFQ/NPT7HLxOIldyIgrcjaYTRvA81KhJIg==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -5134,38 +4948,9 @@ packages:
tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
tree-kill@1.2.2:
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
hasBin: true
ts-algebra@2.0.0:
resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==}
tsdown@0.20.1:
resolution: {integrity: sha512-Wo1BzqNQVZ6SFQV8rjQBwMmNubO+yV3F+vp2WNTjEaS4S5CT1C1dHtUbeFMrCEasZpGy5w6TshpehNnfTe8QBQ==}
engines: {node: '>=20.19.0'}
hasBin: true
peerDependencies:
'@arethetypeswrong/core': ^0.18.1
'@vitejs/devtools': '*'
publint: ^0.3.0
typescript: ^5.0.0
unplugin-lightningcss: ^0.4.0
unplugin-unused: ^0.5.0
peerDependenciesMeta:
'@arethetypeswrong/core':
optional: true
'@vitejs/devtools':
optional: true
publint:
optional: true
typescript:
optional: true
unplugin-lightningcss:
optional: true
unplugin-unused:
optional: true
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
@@ -5219,9 +5004,6 @@ packages:
resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==}
engines: {node: '>=18'}
unconfig-core@7.4.2:
resolution: {integrity: sha512-VgPCvLWugINbXvMQDf8Jh0mlbvNjNC6eSUziHsBCMpxR05OPrNrvDnyatdMjRgcHaaNsCqz+wjNXxNw1kRLHUg==}
undici-types@6.21.0:
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
@@ -5246,16 +5028,6 @@ packages:
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
engines: {node: '>= 0.8'}
unrun@0.2.26:
resolution: {integrity: sha512-A3DQLBcDyTui4Hlaoojkldg+8x+CIR+tcSHY0wzW+CgB4X/DNyH58jJpXp1B/EkE+yG6tU8iH1mWsLtwFU3IQg==}
engines: {node: '>=20.19.0'}
hasBin: true
peerDependencies:
synckit: ^0.11.11
peerDependenciesMeta:
synckit:
optional: true
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
@@ -5979,31 +5751,14 @@ snapshots:
jsonwebtoken: 9.0.3
uuid: 8.3.2
'@babel/generator@8.0.0-beta.4':
dependencies:
'@babel/parser': 8.0.0-beta.4
'@babel/types': 8.0.0-beta.4
'@jridgewell/gen-mapping': 0.3.13
'@jridgewell/trace-mapping': 0.3.31
'@types/jsesc': 2.5.1
jsesc: 3.1.0
'@babel/helper-string-parser@7.27.1': {}
'@babel/helper-string-parser@8.0.0-beta.4': {}
'@babel/helper-validator-identifier@7.28.5': {}
'@babel/helper-validator-identifier@8.0.0-beta.4': {}
'@babel/parser@7.28.6':
dependencies:
'@babel/types': 7.28.6
'@babel/parser@8.0.0-beta.4':
dependencies:
'@babel/types': 8.0.0-beta.4
'@babel/runtime@7.28.6': {}
'@babel/types@7.28.6':
@@ -6011,11 +5766,6 @@ snapshots:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.28.5
'@babel/types@8.0.0-beta.4':
dependencies:
'@babel/helper-string-parser': 8.0.0-beta.4
'@babel/helper-validator-identifier': 8.0.0-beta.4
'@bcoe/v8-coverage@1.0.2': {}
'@borewit/text-codec@0.2.1': {}
@@ -6403,11 +6153,6 @@ snapshots:
dependencies:
minipass: 7.1.2
'@jridgewell/gen-mapping@0.3.13':
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
'@jridgewell/trace-mapping': 0.3.31
'@jridgewell/resolve-uri@3.1.2': {}
'@jridgewell/sourcemap-codec@1.5.5': {}
@@ -6473,7 +6218,7 @@ snapshots:
dependencies:
'@types/node': 24.10.9
optionalDependencies:
axios: 1.13.4
axios: 1.13.4(debug@4.4.3)
transitivePeerDependencies:
- debug
@@ -6670,7 +6415,7 @@ snapshots:
'@azure/core-auth': 1.10.1
'@azure/msal-node': 3.8.6
'@microsoft/agents-activity': 1.2.3
axios: 1.13.4
axios: 1.13.4(debug@4.4.3)
jsonwebtoken: 9.0.3
jwks-rsa: 3.2.2
object-path: 0.11.8
@@ -7171,8 +6916,6 @@ snapshots:
'@opentelemetry/semantic-conventions@1.39.0': {}
'@oxc-project/types@0.110.0': {}
'@oxc-project/types@0.111.0': {}
'@oxfmt/darwin-arm64@0.27.0':
@@ -7271,10 +7014,6 @@ snapshots:
'@protobufjs/utf8@1.1.0': {}
'@quansync/fs@1.0.0':
dependencies:
quansync: 1.0.0
'@reflink/reflink-darwin-arm64@0.1.19':
optional: true
@@ -7311,90 +7050,47 @@ snapshots:
'@reflink/reflink-win32-x64-msvc': 0.1.19
optional: true
'@rolldown/binding-android-arm64@1.0.0-rc.1':
optional: true
'@rolldown/binding-android-arm64@1.0.0-rc.2':
optional: true
'@rolldown/binding-darwin-arm64@1.0.0-rc.1':
optional: true
'@rolldown/binding-darwin-arm64@1.0.0-rc.2':
optional: true
'@rolldown/binding-darwin-x64@1.0.0-rc.1':
optional: true
'@rolldown/binding-darwin-x64@1.0.0-rc.2':
optional: true
'@rolldown/binding-freebsd-x64@1.0.0-rc.1':
optional: true
'@rolldown/binding-freebsd-x64@1.0.0-rc.2':
optional: true
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.1':
optional: true
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.2':
optional: true
'@rolldown/binding-linux-arm64-gnu@1.0.0-rc.1':
optional: true
'@rolldown/binding-linux-arm64-gnu@1.0.0-rc.2':
optional: true
'@rolldown/binding-linux-arm64-musl@1.0.0-rc.1':
optional: true
'@rolldown/binding-linux-arm64-musl@1.0.0-rc.2':
optional: true
'@rolldown/binding-linux-x64-gnu@1.0.0-rc.1':
optional: true
'@rolldown/binding-linux-x64-gnu@1.0.0-rc.2':
optional: true
'@rolldown/binding-linux-x64-musl@1.0.0-rc.1':
optional: true
'@rolldown/binding-linux-x64-musl@1.0.0-rc.2':
optional: true
'@rolldown/binding-openharmony-arm64@1.0.0-rc.1':
optional: true
'@rolldown/binding-openharmony-arm64@1.0.0-rc.2':
optional: true
'@rolldown/binding-wasm32-wasi@1.0.0-rc.1':
dependencies:
'@napi-rs/wasm-runtime': 1.1.1
optional: true
'@rolldown/binding-wasm32-wasi@1.0.0-rc.2':
dependencies:
'@napi-rs/wasm-runtime': 1.1.1
optional: true
'@rolldown/binding-win32-arm64-msvc@1.0.0-rc.1':
optional: true
'@rolldown/binding-win32-arm64-msvc@1.0.0-rc.2':
optional: true
'@rolldown/binding-win32-x64-msvc@1.0.0-rc.1':
optional: true
'@rolldown/binding-win32-x64-msvc@1.0.0-rc.2':
optional: true
'@rolldown/pluginutils@1.0.0-rc.1': {}
'@rolldown/pluginutils@1.0.0-rc.2': {}
'@rollup/rollup-android-arm-eabi@4.57.1':
@@ -7502,7 +7198,7 @@ snapshots:
'@slack/types': 2.19.0
'@slack/web-api': 7.13.0
'@types/express': 5.0.6
axios: 1.13.4
axios: 1.13.4(debug@4.4.3)
express: 5.2.1
path-to-regexp: 8.3.0
raw-body: 3.0.2
@@ -7548,7 +7244,7 @@ snapshots:
'@slack/types': 2.19.0
'@types/node': 25.1.0
'@types/retry': 0.12.0
axios: 1.13.4
axios: 1.13.4(debug@4.4.3)
eventemitter3: 5.0.4
form-data: 4.0.5
is-electron: 2.2.2
@@ -8006,8 +7702,6 @@ snapshots:
'@types/http-errors@2.0.5': {}
'@types/jsesc@2.5.1': {}
'@types/jsonwebtoken@9.0.10':
dependencies:
'@types/ms': 2.1.0
@@ -8351,8 +8045,6 @@ snapshots:
ansi-styles@6.2.3: {}
ansis@4.2.0: {}
any-promise@1.3.0: {}
apache-arrow@18.1.0:
@@ -8390,12 +8082,6 @@ snapshots:
assertion-error@2.0.1: {}
ast-kit@3.0.0-beta.1:
dependencies:
'@babel/parser': 8.0.0-beta.4
estree-walker: 3.0.3
pathe: 2.0.3
ast-types@0.13.4:
dependencies:
tslib: 2.8.1
@@ -8442,14 +8128,6 @@ snapshots:
aws4@1.13.2: {}
axios@1.13.4:
dependencies:
follow-redirects: 1.15.11
form-data: 4.0.5
proxy-from-env: 1.1.0
transitivePeerDependencies:
- debug
axios@1.13.4(debug@4.4.3):
dependencies:
follow-redirects: 1.15.11(debug@4.4.3)
@@ -8476,8 +8154,6 @@ snapshots:
bignumber.js@9.3.1: {}
birpc@4.0.0: {}
bluebird@3.7.2: {}
body-parser@1.20.4:
@@ -8534,8 +8210,6 @@ snapshots:
bytes@3.1.2: {}
cac@6.7.14: {}
cacheable@2.3.2:
dependencies:
'@cacheable/memory': 2.0.7
@@ -8722,8 +8396,6 @@ snapshots:
deepmerge@4.3.1: {}
defu@6.1.4: {}
degenerator@5.0.1:
dependencies:
ast-types: 0.13.4
@@ -8770,8 +8442,6 @@ snapshots:
dotenv@17.2.3: {}
dts-resolver@2.1.3: {}
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
@@ -8797,8 +8467,6 @@ snapshots:
emoji-regex@9.2.2: {}
empathic@2.0.0: {}
encodeurl@2.0.0: {}
entities@4.5.0: {}
@@ -9025,8 +8693,6 @@ snapshots:
flatbuffers@24.12.23: {}
follow-redirects@1.15.11: {}
follow-redirects@1.15.11(debug@4.4.3):
optionalDependencies:
debug: 4.4.3
@@ -9241,8 +8907,6 @@ snapshots:
hono@4.11.4:
optional: true
hookable@6.0.1: {}
hookified@1.15.0: {}
html-escaper@2.0.2: {}
@@ -9322,8 +8986,6 @@ snapshots:
cjs-module-lexer: 2.2.0
module-details-from-path: 1.0.4
import-without-cache@0.2.5: {}
inherits@2.0.4: {}
ini@1.3.8: {}
@@ -9432,8 +9094,6 @@ snapshots:
jsbn@0.1.1: {}
jsesc@3.1.0: {}
json-bigint@1.0.0:
dependencies:
bignumber.js: 9.3.1
@@ -10283,8 +9943,6 @@ snapshots:
qs@6.5.3: {}
quansync@1.0.0: {}
quick-format-unescaped@4.0.4: {}
range-parser@1.2.1: {}
@@ -10394,42 +10052,6 @@ snapshots:
dependencies:
glob: 10.5.0
rolldown-plugin-dts@0.21.8(@typescript/native-preview@7.0.0-dev.20260130.1)(rolldown@1.0.0-rc.1)(typescript@5.9.3):
dependencies:
'@babel/generator': 8.0.0-beta.4
'@babel/parser': 8.0.0-beta.4
'@babel/types': 8.0.0-beta.4
ast-kit: 3.0.0-beta.1
birpc: 4.0.0
dts-resolver: 2.1.3
get-tsconfig: 4.13.1
obug: 2.1.1
rolldown: 1.0.0-rc.1
optionalDependencies:
'@typescript/native-preview': 7.0.0-dev.20260130.1
typescript: 5.9.3
transitivePeerDependencies:
- oxc-resolver
rolldown@1.0.0-rc.1:
dependencies:
'@oxc-project/types': 0.110.0
'@rolldown/pluginutils': 1.0.0-rc.1
optionalDependencies:
'@rolldown/binding-android-arm64': 1.0.0-rc.1
'@rolldown/binding-darwin-arm64': 1.0.0-rc.1
'@rolldown/binding-darwin-x64': 1.0.0-rc.1
'@rolldown/binding-freebsd-x64': 1.0.0-rc.1
'@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.1
'@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.1
'@rolldown/binding-linux-arm64-musl': 1.0.0-rc.1
'@rolldown/binding-linux-x64-gnu': 1.0.0-rc.1
'@rolldown/binding-linux-x64-musl': 1.0.0-rc.1
'@rolldown/binding-openharmony-arm64': 1.0.0-rc.1
'@rolldown/binding-wasm32-wasi': 1.0.0-rc.1
'@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.1
'@rolldown/binding-win32-x64-msvc': 1.0.0-rc.1
rolldown@1.0.0-rc.2:
dependencies:
'@oxc-project/types': 0.111.0
@@ -10865,37 +10487,8 @@ snapshots:
tr46@0.0.3: {}
tree-kill@1.2.2: {}
ts-algebra@2.0.0: {}
tsdown@0.20.1(@typescript/native-preview@7.0.0-dev.20260130.1)(typescript@5.9.3):
dependencies:
ansis: 4.2.0
cac: 6.7.14
defu: 6.1.4
empathic: 2.0.0
hookable: 6.0.1
import-without-cache: 0.2.5
obug: 2.1.1
picomatch: 4.0.3
rolldown: 1.0.0-rc.1
rolldown-plugin-dts: 0.21.8(@typescript/native-preview@7.0.0-dev.20260130.1)(rolldown@1.0.0-rc.1)(typescript@5.9.3)
semver: 7.7.3
tinyexec: 1.0.2
tinyglobby: 0.2.15
tree-kill: 1.2.2
unconfig-core: 7.4.2
unrun: 0.2.26
optionalDependencies:
typescript: 5.9.3
transitivePeerDependencies:
- '@ts-macro/tsc'
- '@typescript/native-preview'
- oxc-resolver
- synckit
- vue-tsc
tslib@2.8.1: {}
tslog@4.10.2: {}
@@ -10938,11 +10531,6 @@ snapshots:
uint8array-extras@1.5.0: {}
unconfig-core@7.4.2:
dependencies:
'@quansync/fs': 1.0.0
quansync: 1.0.0
undici-types@6.21.0: {}
undici-types@7.16.0: {}
@@ -10957,10 +10545,6 @@ snapshots:
unpipe@1.0.0: {}
unrun@0.2.26:
dependencies:
rolldown: 1.0.0-rc.1
uri-js@4.4.1:
dependencies:
punycode: 2.3.1

View File

@@ -31,7 +31,7 @@ echo "Starting gateway container..."
-e "OPENCLAW_SKIP_CRON=1" \
-e "OPENCLAW_SKIP_CANVAS_HOST=1" \
"$IMAGE_NAME" \
bash -lc "node dist/index.mjs gateway --port $PORT --bind lan --allow-unconfigured > /tmp/gateway-net-e2e.log 2>&1"
bash -lc "node dist/index.js gateway --port $PORT --bind lan --allow-unconfigured > /tmp/gateway-net-e2e.log 2>&1"
echo "Waiting for gateway to come up..."
for _ in $(seq 1 20); do

View File

@@ -83,7 +83,7 @@ TRASH
}
start_gateway() {
node dist/index.mjs gateway --port 18789 --bind loopback --allow-unconfigured > /tmp/gateway-e2e.log 2>&1 &
node dist/index.js gateway --port 18789 --bind loopback --allow-unconfigured > /tmp/gateway-e2e.log 2>&1 &
GATEWAY_PID="$!"
}
@@ -185,7 +185,7 @@ TRASH
local validate_fn="${4:-}"
# Default onboarding command wrapper.
run_wizard_cmd "$case_name" "$home_dir" "node dist/index.mjs onboard $ONBOARD_FLAGS" "$send_fn" true "$validate_fn"
run_wizard_cmd "$case_name" "$home_dir" "node dist/index.js onboard $ONBOARD_FLAGS" "$send_fn" true "$validate_fn"
}
make_home() {
@@ -268,7 +268,7 @@ TRASH
home_dir="$(make_home local-basic)"
export HOME="$home_dir"
mkdir -p "$HOME"
node dist/index.mjs onboard \
node dist/index.js onboard \
--non-interactive \
--accept-risk \
--flow quickstart \
@@ -345,7 +345,7 @@ NODE
export HOME="$home_dir"
mkdir -p "$HOME"
# Smoke test non-interactive remote config write.
node dist/index.mjs onboard --non-interactive --accept-risk \
node dist/index.js onboard --non-interactive --accept-risk \
--mode remote \
--remote-url ws://gateway.local:18789 \
--remote-token remote-token \
@@ -398,7 +398,7 @@ NODE
}
JSON
node dist/index.mjs onboard \
node dist/index.js onboard \
--non-interactive \
--accept-risk \
--flow quickstart \
@@ -441,7 +441,7 @@ NODE
local home_dir
home_dir="$(make_home channels)"
# Channels-only configure flow.
run_wizard_cmd channels "$home_dir" "node dist/index.mjs configure --section channels" send_channels_flow
run_wizard_cmd channels "$home_dir" "node dist/index.js configure --section channels" send_channels_flow
config_path="$HOME/.openclaw/openclaw.json"
assert_file "$config_path"
@@ -492,7 +492,7 @@ NODE
}
JSON
run_wizard_cmd skills "$home_dir" "node dist/index.mjs configure --section skills" send_skills_flow
run_wizard_cmd skills "$home_dir" "node dist/index.js configure --section skills" send_skills_flow
config_path="$HOME/.openclaw/openclaw.json"
assert_file "$config_path"

View File

@@ -29,7 +29,7 @@ module.exports = {
};
JS
node dist/index.mjs plugins list --json > /tmp/plugins.json
node dist/index.js plugins list --json > /tmp/plugins.json
node - <<'"'"'NODE'"'"'
const fs = require("node:fs");
@@ -81,8 +81,8 @@ module.exports = {
JS
tar -czf /tmp/demo-plugin-tgz.tgz -C "$pack_dir" package
node dist/index.mjs plugins install /tmp/demo-plugin-tgz.tgz
node dist/index.mjs plugins list --json > /tmp/plugins2.json
node dist/index.js plugins install /tmp/demo-plugin-tgz.tgz
node dist/index.js plugins list --json > /tmp/plugins2.json
node - <<'"'"'NODE'"'"'
const fs = require("node:fs");
@@ -118,8 +118,8 @@ module.exports = {
};
JS
node dist/index.mjs plugins install "$dir_plugin"
node dist/index.mjs plugins list --json > /tmp/plugins3.json
node dist/index.js plugins install "$dir_plugin"
node dist/index.js plugins list --json > /tmp/plugins3.json
node - <<'"'"'NODE'"'"'
const fs = require("node:fs");
@@ -156,8 +156,8 @@ module.exports = {
};
JS
node dist/index.mjs plugins install "file:$file_pack_dir/package"
node dist/index.mjs plugins list --json > /tmp/plugins4.json
node dist/index.js plugins install "file:$file_pack_dir/package"
node dist/index.js plugins list --json > /tmp/plugins4.json
node - <<'"'"'NODE'"'"'
const fs = require("node:fs");

View File

@@ -110,8 +110,8 @@ merge_framework_machos() {
echo "📦 Ensuring deps (pnpm install)"
(cd "$ROOT_DIR" && pnpm install --no-frozen-lockfile --config.node-linker=hoisted)
if [[ "${SKIP_TSC:-0}" != "1" ]]; then
echo "📦 Building JS (pnpm tsdown)"
(cd "$ROOT_DIR" && pnpm tsdown)
echo "📦 Building JS (pnpm tsc)"
(cd "$ROOT_DIR" && pnpm tsc -p tsconfig.json --noEmit false)
else
echo "📦 Skipping TS build (SKIP_TSC=1)"
fi

View File

@@ -1,34 +1,36 @@
import fs from "node:fs";
import path from "node:path";
import { spawnSync } from "node:child_process";
import { fileURLToPath } from "node:url";
import { setupGitHooks } from "./setup-git-hooks.js";
import fs from 'node:fs';
import path from 'node:path';
import { spawnSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { setupGitHooks } from './setup-git-hooks.js';
function detectPackageManager(ua = process.env.npm_config_user_agent ?? "") {
function detectPackageManager(ua = process.env.npm_config_user_agent ?? '') {
// Examples:
// - "pnpm/10.23.0 npm/? node/v22.21.1 darwin arm64"
// - "npm/10.9.4 node/v22.12.0 linux x64"
// - "bun/1.2.2"
const normalized = String(ua).trim();
if (normalized.startsWith("pnpm/")) return "pnpm";
if (normalized.startsWith("bun/")) return "bun";
if (normalized.startsWith("npm/")) return "npm";
if (normalized.startsWith("yarn/")) return "yarn";
return "unknown";
if (normalized.startsWith('pnpm/')) return 'pnpm';
if (normalized.startsWith('bun/')) return 'bun';
if (normalized.startsWith('npm/')) return 'npm';
if (normalized.startsWith('yarn/')) return 'yarn';
return 'unknown';
}
function shouldApplyPnpmPatchedDependenciesFallback(pm = detectPackageManager()) {
function shouldApplyPnpmPatchedDependenciesFallback(
pm = detectPackageManager(),
) {
// pnpm already applies pnpm.patchedDependencies itself; re-applying would fail.
return pm !== "pnpm";
return pm !== 'pnpm';
}
function getRepoRoot() {
const here = path.dirname(fileURLToPath(import.meta.url));
return path.resolve(here, "..");
return path.resolve(here, '..');
}
function ensureExecutable(targetPath) {
if (process.platform === "win32") return;
if (process.platform === 'win32') return;
if (!fs.existsSync(targetPath)) return;
try {
const mode = fs.statSync(targetPath).mode & 0o777;
@@ -40,29 +42,32 @@ function ensureExecutable(targetPath) {
}
function hasGit(repoRoot) {
const result = spawnSync("git", ["--version"], { cwd: repoRoot, stdio: "ignore" });
const result = spawnSync('git', ['--version'], {
cwd: repoRoot,
stdio: 'ignore',
});
return result.status === 0;
}
function extractPackageName(key) {
if (key.startsWith("@")) {
const idx = key.indexOf("@", 1);
if (key.startsWith('@')) {
const idx = key.indexOf('@', 1);
if (idx === -1) return key;
return key.slice(0, idx);
}
const idx = key.lastIndexOf("@");
const idx = key.lastIndexOf('@');
if (idx <= 0) return key;
return key.slice(0, idx);
}
function stripPrefix(p) {
if (p.startsWith("a/") || p.startsWith("b/")) return p.slice(2);
if (p.startsWith('a/') || p.startsWith('b/')) return p.slice(2);
return p;
}
function parseRange(segment) {
// segment: "-12,5" or "+7"
const [startRaw, countRaw] = segment.slice(1).split(",");
const [startRaw, countRaw] = segment.slice(1).split(',');
const start = Number.parseInt(startRaw, 10);
const count = countRaw ? Number.parseInt(countRaw, 10) : 1;
if (Number.isNaN(start) || Number.isNaN(count)) {
@@ -72,12 +77,12 @@ function parseRange(segment) {
}
function parsePatch(patchText) {
const lines = patchText.split("\n");
const lines = patchText.split('\n');
const files = [];
let i = 0;
while (i < lines.length) {
if (!lines[i].startsWith("diff --git ")) {
if (!lines[i].startsWith('diff --git ')) {
i += 1;
continue;
}
@@ -86,20 +91,22 @@ function parsePatch(patchText) {
i += 1;
// Skip index line(s)
while (i < lines.length && lines[i].startsWith("index ")) i += 1;
while (i < lines.length && lines[i].startsWith('index ')) i += 1;
if (i < lines.length && lines[i].startsWith("--- ")) {
if (i < lines.length && lines[i].startsWith('--- ')) {
file.oldPath = stripPrefix(lines[i].slice(4).trim());
i += 1;
}
if (i < lines.length && lines[i].startsWith("+++ ")) {
if (i < lines.length && lines[i].startsWith('+++ ')) {
file.newPath = stripPrefix(lines[i].slice(4).trim());
i += 1;
}
while (i < lines.length && lines[i].startsWith("@@")) {
while (i < lines.length && lines[i].startsWith('@@')) {
const header = lines[i];
const match = /^@@\s+(-\d+(?:,\d+)?)\s+(\+\d+(?:,\d+)?)\s+@@/.exec(header);
const match = /^@@\s+(-\d+(?:,\d+)?)\s+(\+\d+(?:,\d+)?)\s+@@/.exec(
header,
);
if (!match) throw new Error(`invalid hunk header: ${header}`);
const oldRange = parseRange(match[1]);
const newRange = parseRange(match[2]);
@@ -108,12 +115,12 @@ function parsePatch(patchText) {
const hunkLines = [];
while (i < lines.length) {
const line = lines[i];
if (line.startsWith("@@") || line.startsWith("diff --git ")) break;
if (line === "") {
if (line.startsWith('@@') || line.startsWith('diff --git ')) break;
if (line === '') {
i += 1;
continue;
}
if (line.startsWith("\\ No newline at end of file")) {
if (line.startsWith('\\ No newline at end of file')) {
i += 1;
continue;
}
@@ -142,16 +149,16 @@ function readFileLines(targetPath) {
if (!fs.existsSync(targetPath)) {
throw new Error(`target file missing: ${targetPath}`);
}
const raw = fs.readFileSync(targetPath, "utf-8");
const hasTrailingNewline = raw.endsWith("\n");
const parts = raw.split("\n");
const raw = fs.readFileSync(targetPath, 'utf-8');
const hasTrailingNewline = raw.endsWith('\n');
const parts = raw.split('\n');
if (hasTrailingNewline) parts.pop();
return { lines: parts, hasTrailingNewline };
}
function writeFileLines(targetPath, lines, hadTrailingNewline) {
const content = lines.join("\n") + (hadTrailingNewline ? "\n" : "");
fs.writeFileSync(targetPath, content, "utf-8");
const content = lines.join('\n') + (hadTrailingNewline ? '\n' : '');
fs.writeFileSync(targetPath, content, 'utf-8');
}
function applyHunk(lines, hunk, offset) {
@@ -159,7 +166,7 @@ function applyHunk(lines, hunk, offset) {
const expected = [];
for (const raw of hunk.lines) {
const marker = raw[0];
if (marker === " " || marker === "+") {
if (marker === ' ' || marker === '+') {
expected.push(raw.slice(1));
}
}
@@ -180,21 +187,21 @@ function applyHunk(lines, hunk, offset) {
for (const raw of hunk.lines) {
const marker = raw[0];
const text = raw.slice(1);
if (marker === " ") {
if (marker === ' ') {
if (lines[cursor] !== text) {
throw new Error(
`context mismatch at line ${cursor + 1}: expected "${text}", found "${lines[cursor] ?? "<eof>"}"`,
`context mismatch at line ${cursor + 1}: expected "${text}", found "${lines[cursor] ?? '<eof>'}"`,
);
}
cursor += 1;
} else if (marker === "-") {
} else if (marker === '-') {
if (lines[cursor] !== text) {
throw new Error(
`delete mismatch at line ${cursor + 1}: expected "${text}", found "${lines[cursor] ?? "<eof>"}"`,
`delete mismatch at line ${cursor + 1}: expected "${text}", found "${lines[cursor] ?? '<eof>'}"`,
);
}
lines.splice(cursor, 1);
} else if (marker === "+") {
} else if (marker === '+') {
lines.splice(cursor, 0, text);
cursor += 1;
} else {
@@ -207,11 +214,11 @@ function applyHunk(lines, hunk, offset) {
}
function applyPatchToFile(targetDir, filePatch) {
if (filePatch.newPath === "/dev/null") {
if (filePatch.newPath === '/dev/null') {
// deletion not needed for our patches
return;
}
const relPath = stripPrefix(filePatch.newPath ?? filePatch.oldPath ?? "");
const relPath = stripPrefix(filePatch.newPath ?? filePatch.oldPath ?? '');
const targetPath = path.join(targetDir, relPath);
const { lines, hasTrailingNewline } = readFileLines(targetPath);
@@ -225,7 +232,10 @@ function applyPatchToFile(targetDir, filePatch) {
function applyPatchSet({ patchText, targetDir }) {
let resolvedTarget = path.resolve(targetDir);
if (!fs.existsSync(resolvedTarget) || !fs.statSync(resolvedTarget).isDirectory()) {
if (
!fs.existsSync(resolvedTarget) ||
!fs.statSync(resolvedTarget).isDirectory()
) {
console.warn(`[postinstall] skip missing target: ${resolvedTarget}`);
return;
}
@@ -244,28 +254,28 @@ function applyPatchFile({ patchPath, targetDir }) {
if (!fs.existsSync(absPatchPath)) {
throw new Error(`missing patch: ${absPatchPath}`);
}
const patchText = fs.readFileSync(absPatchPath, "utf-8");
const patchText = fs.readFileSync(absPatchPath, 'utf-8');
applyPatchSet({ patchText, targetDir });
}
function trySetupCompletion(repoRoot) {
// Skip in CI or if explicitly disabled
if (process.env.CI || process.env.OPENCLAW_SKIP_COMPLETION_SETUP) return;
const binPath = path.join(repoRoot, "openclaw.mjs");
const binPath = path.join(repoRoot, 'openclaw.mjs');
if (!fs.existsSync(binPath)) return;
// In development, dist might not exist yet during postinstall
const distEntry = path.join(repoRoot, "dist", "index.js");
const distEntry = path.join(repoRoot, 'dist', 'index.js');
if (!fs.existsSync(distEntry)) return;
try {
// Run with OPENCLAW_SKIP_POSTINSTALL to avoid any weird recursion,
// though distinct from this script.
spawnSync(process.execPath, [binPath, "completion", "--install", "--yes"], {
spawnSync(process.execPath, [binPath, 'completion', '--install', '--yes'], {
cwd: repoRoot,
stdio: "inherit",
env: { ...process.env, OPENCLAW_SKIP_POSTINSTALL: "1" },
stdio: 'inherit',
env: { ...process.env, OPENCLAW_SKIP_POSTINSTALL: '1' },
});
} catch (err) {
// Ignore errors to not break install
@@ -276,7 +286,7 @@ function main() {
const repoRoot = getRepoRoot();
process.chdir(repoRoot);
ensureExecutable(path.join(repoRoot, "dist", "entry.mjs"));
ensureExecutable(path.join(repoRoot, 'dist', '/entry.js'));
setupGitHooks({ repoRoot });
trySetupCompletion(repoRoot);
@@ -284,18 +294,18 @@ function main() {
return;
}
const pkgPath = path.join(repoRoot, "package.json");
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
const pkgPath = path.join(repoRoot, 'package.json');
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
const patched = pkg?.pnpm?.patchedDependencies ?? {};
// Bun does not support pnpm.patchedDependencies. Apply these patch files to
// node_modules packages as a best-effort compatibility layer.
for (const [key, relPatchPath] of Object.entries(patched)) {
if (typeof relPatchPath !== "string" || !relPatchPath.trim()) continue;
if (typeof relPatchPath !== 'string' || !relPatchPath.trim()) continue;
const pkgName = extractPackageName(String(key));
if (!pkgName) continue;
applyPatchFile({
targetDir: path.join("node_modules", ...pkgName.split("/")),
targetDir: path.join('node_modules', ...pkgName.split('/')),
patchPath: relPatchPath,
});
}
@@ -303,10 +313,10 @@ function main() {
try {
const skip =
process.env.OPENCLAW_SKIP_POSTINSTALL === "1" ||
process.env.CLAWDBOT_SKIP_POSTINSTALL === "1" ||
process.env.VITEST === "true" ||
process.env.NODE_ENV === "test";
process.env.OPENCLAW_SKIP_POSTINSTALL === '1' ||
process.env.CLAWDBOT_SKIP_POSTINSTALL === '1' ||
process.env.VITEST === 'true' ||
process.env.NODE_ENV === 'test';
if (!skip) {
main();

View File

@@ -1,21 +1,24 @@
#!/usr/bin/env node
import { spawn } from "node:child_process";
import fs from "node:fs";
import path from "node:path";
import process from "node:process";
import { spawn } from 'node:child_process';
import fs from 'node:fs';
import path from 'node:path';
import process from 'node:process';
const args = process.argv.slice(2);
const env = { ...process.env };
const cwd = process.cwd();
const compilerOverride = env.OPENCLAW_TS_COMPILER ?? env.CLAWDBOT_TS_COMPILER;
const compiler = compilerOverride === "tsc" ? "tsc" : "tsgo";
const projectArgs = ["--project", "tsconfig.json"];
const compiler = compilerOverride === 'tsc' ? 'tsc' : 'tsgo';
const projectArgs = ['--project', 'tsconfig.json'];
const distRoot = path.join(cwd, "dist");
const distEntry = path.join(distRoot, "entry.mjs");
const buildStampPath = path.join(distRoot, ".buildstamp");
const srcRoot = path.join(cwd, "src");
const configFiles = [path.join(cwd, "tsconfig.json"), path.join(cwd, "package.json")];
const distRoot = path.join(cwd, 'dist');
const distEntry = path.join(distRoot, '/entry.js');
const buildStampPath = path.join(distRoot, '.buildstamp');
const srcRoot = path.join(cwd, 'src');
const configFiles = [
path.join(cwd, 'tsconfig.json'),
path.join(cwd, 'package.json'),
];
const statMtime = (filePath) => {
try {
@@ -27,10 +30,10 @@ const statMtime = (filePath) => {
const isExcludedSource = (filePath) => {
const relativePath = path.relative(srcRoot, filePath);
if (relativePath.startsWith("..")) return false;
if (relativePath.startsWith('..')) return false;
return (
relativePath.endsWith(".test.ts") ||
relativePath.endsWith(".test.tsx") ||
relativePath.endsWith('.test.ts') ||
relativePath.endsWith('.test.tsx') ||
relativePath.endsWith(`test-helpers.ts`)
);
};
@@ -66,7 +69,7 @@ const findLatestMtime = (dirPath, shouldSkip) => {
};
const shouldBuild = () => {
if (env.OPENCLAW_FORCE_BUILD === "1") return true;
if (env.OPENCLAW_FORCE_BUILD === '1') return true;
const stampMtime = statMtime(buildStampPath);
if (stampMtime == null) return true;
if (statMtime(distEntry) == null) return true;
@@ -82,18 +85,18 @@ const shouldBuild = () => {
};
const logRunner = (message) => {
if (env.OPENCLAW_RUNNER_LOG === "0") return;
if (env.OPENCLAW_RUNNER_LOG === '0') return;
process.stderr.write(`[openclaw] ${message}\n`);
};
const runNode = () => {
const nodeProcess = spawn(process.execPath, ["openclaw.mjs", ...args], {
const nodeProcess = spawn(process.execPath, ['openclaw.mjs', ...args], {
cwd,
env,
stdio: "inherit",
stdio: 'inherit',
});
nodeProcess.on("exit", (exitCode, exitSignal) => {
nodeProcess.on('exit', (exitCode, exitSignal) => {
if (exitSignal) {
process.exit(1);
}
@@ -107,25 +110,29 @@ const writeBuildStamp = () => {
fs.writeFileSync(buildStampPath, `${Date.now()}\n`);
} catch (error) {
// Best-effort stamp; still allow the runner to start.
logRunner(`Failed to write build stamp: ${error?.message ?? "unknown error"}`);
logRunner(
`Failed to write build stamp: ${error?.message ?? 'unknown error'}`,
);
}
};
if (!shouldBuild()) {
runNode();
} else {
logRunner("Building TypeScript (dist is stale).");
const pnpmArgs = ["exec", compiler, ...projectArgs];
const buildCmd = process.platform === "win32" ? "cmd.exe" : "pnpm";
logRunner('Building TypeScript (dist is stale).');
const pnpmArgs = ['exec', compiler, ...projectArgs];
const buildCmd = process.platform === 'win32' ? 'cmd.exe' : 'pnpm';
const buildArgs =
process.platform === "win32" ? ["/d", "/s", "/c", "pnpm", ...pnpmArgs] : pnpmArgs;
process.platform === 'win32'
? ['/d', '/s', '/c', 'pnpm', ...pnpmArgs]
: pnpmArgs;
const build = spawn(buildCmd, buildArgs, {
cwd,
env,
stdio: "inherit",
stdio: 'inherit',
});
build.on("exit", (code, signal) => {
build.on('exit', (code, signal) => {
if (signal) {
process.exit(1);
}

View File

@@ -16,7 +16,7 @@ if (initialBuild.status !== 0) {
process.exit(initialBuild.status ?? 1);
}
const compilerProcess = spawn("pnpm", ["tsdown", '--watch', 'src/'], {
const compilerProcess = spawn("pnpm", ["tsc", '-p', 'tsconfig.json', '--noEmit', 'false', '--watch'], {
cwd,
env,
stdio: "inherit",

View File

@@ -24,7 +24,7 @@ describe("ensureSkillsWatcher", () => {
expect(ignored.some((re) => re.test("/tmp/workspace/skills/node_modules/pkg/index.js"))).toBe(
true,
);
expect(ignored.some((re) => re.test("/tmp/workspace/skills/dist/index.mjs"))).toBe(true);
expect(ignored.some((re) => re.test("/tmp/workspace/skills/dist/index.js"))).toBe(true);
expect(ignored.some((re) => re.test("/tmp/workspace/skills/.git/config"))).toBe(true);
expect(ignored.some((re) => re.test("/tmp/.hidden/skills/index.md"))).toBe(false);
});

View File

@@ -43,7 +43,7 @@ describe("browser CLI --browser-profile flag", () => {
});
it("does not conflict with global --profile flag", () => {
// The global --profile flag is handled by entry.mjs before Commander
// The global --profile flag is handled by /entry.js before Commander
// This test verifies --browser-profile is a separate option
const program = new Command();
program.name("test");

View File

@@ -4,22 +4,22 @@ import { rewriteUpdateFlagArgv } from "./run-main.js";
describe("rewriteUpdateFlagArgv", () => {
it("leaves argv unchanged when --update is absent", () => {
const argv = ["node", "entry.mjs", "status"];
const argv = ["node", "entry.js", "status"];
expect(rewriteUpdateFlagArgv(argv)).toBe(argv);
});
it("rewrites --update into the update command", () => {
expect(rewriteUpdateFlagArgv(["node", "entry.mjs", "--update"])).toEqual([
expect(rewriteUpdateFlagArgv(["node", "entry.js", "--update"])).toEqual([
"node",
"entry.mjs",
"entry.js",
"update",
]);
});
it("preserves global flags that appear before --update", () => {
expect(rewriteUpdateFlagArgv(["node", "entry.mjs", "--profile", "p", "--update"])).toEqual([
expect(rewriteUpdateFlagArgv(["node", "entry.js", "--profile", "p", "--update"])).toEqual([
"node",
"entry.mjs",
"entry.js",
"--profile",
"p",
"update",
@@ -27,9 +27,9 @@ describe("rewriteUpdateFlagArgv", () => {
});
it("keeps update options after the rewritten command", () => {
expect(rewriteUpdateFlagArgv(["node", "entry.mjs", "--update", "--json"])).toEqual([
expect(rewriteUpdateFlagArgv(["node", "entry.js", "--update", "--json"])).toEqual([
"node",
"entry.mjs",
"entry.js",
"update",
"--json",
]);

View File

@@ -747,7 +747,7 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
let afterVersion = beforeVersion;
if (pkgRoot) {
afterVersion = await readPackageVersion(pkgRoot);
const entryPath = path.join(pkgRoot, "dist", "entry.mjs");
const entryPath = path.join(pkgRoot, "dist", "entry.js");
if (await pathExists(entryPath)) {
const doctorStep = await runUpdateStep({
name: `${CLI_NAME} doctor`,
@@ -969,7 +969,9 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
try {
const { doctorCommand } = await import("../commands/doctor.js");
const interactiveDoctor = Boolean(process.stdin.isTTY) && !opts.json && opts.yes !== true;
await doctorCommand(defaultRuntime, { nonInteractive: !interactiveDoctor });
await doctorCommand(defaultRuntime, {
nonInteractive: !interactiveDoctor,
});
} catch (err) {
defaultRuntime.log(theme.warn(`Doctor failed: ${String(err)}`));
} finally {
@@ -1220,7 +1222,9 @@ ${theme.muted("Docs:")} ${formatDocsLink("/cli/update", "docs.openclaw.ai/cli/up
)
.action(async (opts) => {
try {
await updateWizardCommand({ timeout: opts.timeout as string | undefined });
await updateWizardCommand({
timeout: opts.timeout as string | undefined,
});
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);

View File

@@ -103,7 +103,12 @@ vi.mock("../memory/manager.js", () => ({
sourceCounts: [{ source: "memory", files: 2, chunks: 3 }],
cache: { enabled: true, entries: 10, maxEntries: 500 },
fts: { enabled: true, available: true },
vector: { enabled: true, available: true, extensionPath: "/opt/vec0.dylib", dims: 1024 },
vector: {
enabled: true,
available: true,
extensionPath: "/opt/vec0.dylib",
dims: 1024,
},
}),
close: vi.fn(async () => {}),
__agentId: agentId,
@@ -254,7 +259,7 @@ vi.mock("../daemon/service.js", () => ({
isLoaded: async () => true,
readRuntime: async () => ({ status: "running", pid: 1234 }),
readCommand: async () => ({
programArguments: ["node", "dist/entry.mjs", "gateway"],
programArguments: ["node", "dist/entry.js", "gateway"],
sourcePath: "/tmp/Library/LaunchAgents/bot.molt.gateway.plist",
}),
}),
@@ -267,7 +272,7 @@ vi.mock("../daemon/node-service.js", () => ({
isLoaded: async () => true,
readRuntime: async () => ({ status: "running", pid: 4321 }),
readCommand: async () => ({
programArguments: ["node", "dist/entry.mjs", "node-host"],
programArguments: ["node", "dist/entry.js", "node-host"],
sourcePath: "/tmp/Library/LaunchAgents/bot.molt.node.plist",
}),
}),

View File

@@ -24,7 +24,7 @@ afterEach(() => {
describe("resolveGatewayProgramArguments", () => {
it("uses realpath-resolved dist entry when running via npx shim", async () => {
const argv1 = path.resolve("/tmp/.npm/_npx/63c3/node_modules/.bin/openclaw");
const entryPath = path.resolve("/tmp/.npm/_npx/63c3/node_modules/openclaw/dist/entry.mjs");
const entryPath = path.resolve("/tmp/.npm/_npx/63c3/node_modules/openclaw/dist/entry.js");
process.argv = ["node", argv1];
fsMocks.realpath.mockResolvedValue(entryPath);
fsMocks.access.mockImplementation(async (target: string) => {
@@ -49,10 +49,10 @@ describe("resolveGatewayProgramArguments", () => {
// Simulates pnpm global install where node_modules/openclaw is a symlink
// to .pnpm/openclaw@X.Y.Z/node_modules/openclaw
const symlinkPath = path.resolve(
"/Users/test/Library/pnpm/global/5/node_modules/openclaw/dist/entry.mjs",
"/Users/test/Library/pnpm/global/5/node_modules/openclaw/dist/entry.js",
);
const realpathResolved = path.resolve(
"/Users/test/Library/pnpm/global/5/node_modules/.pnpm/openclaw@2026.1.21-2/node_modules/openclaw/dist/entry.mjs",
"/Users/test/Library/pnpm/global/5/node_modules/.pnpm/openclaw@2026.1.21-2/node_modules/openclaw/dist/entry.js",
);
process.argv = ["node", symlinkPath];
fsMocks.realpath.mockResolvedValue(realpathResolved);
@@ -67,7 +67,7 @@ describe("resolveGatewayProgramArguments", () => {
it("falls back to node_modules package dist when .bin path is not resolved", async () => {
const argv1 = path.resolve("/tmp/.npm/_npx/63c3/node_modules/.bin/openclaw");
const indexPath = path.resolve("/tmp/.npm/_npx/63c3/node_modules/openclaw/dist/index.mjs");
const indexPath = path.resolve("/tmp/.npm/_npx/63c3/node_modules/openclaw/dist/index.js");
process.argv = ["node", argv1];
fsMocks.realpath.mockRejectedValue(new Error("no realpath"));
fsMocks.access.mockImplementation(async (target: string) => {

View File

@@ -47,7 +47,7 @@ export async function resolveControlUiDistIndexPath(
}
const normalized = path.resolve(argv1);
// Case 1: entrypoint is directly inside dist/ (e.g., dist/entry.mjs)
// Case 1: entrypoint is directly inside dist/ (e.g., dist/entry.js)
const distDir = path.dirname(normalized);
if (path.basename(distDir) === "dist") {
return path.join(distDir, "control-ui", "index.html");

View File

@@ -73,8 +73,8 @@ function isGatewayArgv(args: string[]): boolean {
}
const entryCandidates = [
"dist/index.mjs",
"dist/entry.mjs",
"dist/index.js",
"dist/entry.js",
"openclaw.mjs",
"scripts/run-node.mjs",
"src/index.ts",

View File

@@ -6,8 +6,8 @@ describe("isMainModule", () => {
it("returns true when argv[1] matches current file", () => {
expect(
isMainModule({
currentFile: "/repo/dist/index.mjs",
argv: ["node", "/repo/dist/index.mjs"],
currentFile: "/repo/dist/index.js",
argv: ["node", "/repo/dist/index.js"],
cwd: "/repo",
env: {},
}),
@@ -17,10 +17,10 @@ describe("isMainModule", () => {
it("returns true under PM2 when pm_exec_path matches current file", () => {
expect(
isMainModule({
currentFile: "/repo/dist/index.mjs",
currentFile: "/repo/dist/index.js",
argv: ["node", "/pm2/lib/ProcessContainerFork.js"],
cwd: "/repo",
env: { pm_exec_path: "/repo/dist/index.mjs", pm_id: "0" },
env: { pm_exec_path: "/repo/dist/index.js", pm_id: "0" },
}),
).toBe(true);
});
@@ -28,7 +28,7 @@ describe("isMainModule", () => {
it("returns false when running under PM2 but this module is imported", () => {
expect(
isMainModule({
currentFile: "/repo/node_modules/openclaw/dist/index.mjs",
currentFile: "/repo/node_modules/openclaw/dist/index.js",
argv: ["node", "/repo/app.js"],
cwd: "/repo",
env: { pm_exec_path: "/repo/app.js", pm_id: "0" },

View File

@@ -37,7 +37,7 @@ describe("ports helpers", () => {
expect(
classifyPortListener(
{
commandLine: "node /Users/me/Projects/openclaw/dist/entry.mjs gateway",
commandLine: "node /Users/me/Projects/openclaw/dist/entry.js gateway",
},
18789,
),

View File

@@ -102,11 +102,11 @@ describe("installPluginFromArchive", () => {
JSON.stringify({
name: "@openclaw/voice-call",
version: "0.0.1",
openclaw: { extensions: ["./dist/index.mjs"] },
openclaw: { extensions: ["./dist/index.js"] },
}),
"utf-8",
);
fs.writeFileSync(path.join(pkgDir, "dist", "index.mjs"), "export {};", "utf-8");
fs.writeFileSync(path.join(pkgDir, "dist", "index.js"), "export {};", "utf-8");
const archivePath = packToArchive({
pkgDir,
@@ -127,7 +127,7 @@ describe("installPluginFromArchive", () => {
expect(result.pluginId).toBe("voice-call");
expect(result.targetDir).toBe(path.join(stateDir, "extensions", "voice-call"));
expect(fs.existsSync(path.join(result.targetDir, "package.json"))).toBe(true);
expect(fs.existsSync(path.join(result.targetDir, "dist", "index.mjs"))).toBe(true);
expect(fs.existsSync(path.join(result.targetDir, "dist", "index.js"))).toBe(true);
});
it("rejects installing when plugin already exists", async () => {
@@ -140,7 +140,7 @@ describe("installPluginFromArchive", () => {
JSON.stringify({
name: "@openclaw/voice-call",
version: "0.0.1",
openclaw: { extensions: ["./dist/index.mjs"] },
openclaw: { extensions: ["./dist/index.js"] },
}),
"utf-8",
);
@@ -182,10 +182,10 @@ describe("installPluginFromArchive", () => {
JSON.stringify({
name: "@openclaw/zipper",
version: "0.0.1",
openclaw: { extensions: ["./dist/index.mjs"] },
openclaw: { extensions: ["./dist/index.js"] },
}),
);
zip.file("package/dist/index.mjs", "export {};");
zip.file("package/dist/index.js", "export {};");
const buffer = await zip.generateAsync({ type: "nodebuffer" });
fs.writeFileSync(archivePath, buffer);
@@ -203,7 +203,7 @@ describe("installPluginFromArchive", () => {
expect(result.pluginId).toBe("zipper");
expect(result.targetDir).toBe(path.join(stateDir, "extensions", "zipper"));
expect(fs.existsSync(path.join(result.targetDir, "package.json"))).toBe(true);
expect(fs.existsSync(path.join(result.targetDir, "dist", "index.mjs"))).toBe(true);
expect(fs.existsSync(path.join(result.targetDir, "dist", "index.js"))).toBe(true);
});
it("allows updates when mode is update", async () => {
@@ -216,7 +216,7 @@ describe("installPluginFromArchive", () => {
JSON.stringify({
name: "@openclaw/voice-call",
version: "0.0.1",
openclaw: { extensions: ["./dist/index.mjs"] },
openclaw: { extensions: ["./dist/index.js"] },
}),
"utf-8",
);
@@ -234,7 +234,7 @@ describe("installPluginFromArchive", () => {
JSON.stringify({
name: "@openclaw/voice-call",
version: "0.0.2",
openclaw: { extensions: ["./dist/index.mjs"] },
openclaw: { extensions: ["./dist/index.js"] },
}),
"utf-8",
);

View File

@@ -114,7 +114,7 @@ const spawnGatewayInstance = async (name: string): Promise<GatewayInstance> => {
child = spawn(
"node",
[
"dist/index.mjs",
"dist/index.js",
"gateway",
"--port",
String(port),
@@ -201,7 +201,7 @@ const stopGatewayInstance = async (inst: GatewayInstance) => {
const runCliJson = async (args: string[], env: NodeJS.ProcessEnv): Promise<unknown> => {
const stdout: string[] = [];
const stderr: string[] = [];
const child = spawn("node", ["dist/index.mjs", ...args], {
const child = spawn("node", ["dist/index.js", ...args], {
cwd: process.cwd(),
env: { ...process.env, ...env },
stdio: ["ignore", "pipe", "pipe"],

View File

@@ -1,12 +0,0 @@
import { defineConfig } from 'tsdown'
export default defineConfig([
{
entry: 'src/index.ts',
platform: 'node',
},
{
entry: 'src/entry.ts',
platform: 'node',
},
])