5.0 KiB
Running the connector as a 7x24 service
dws dev connect keeps a DingTalk robot wired to a local agent over a
Stream long-connection. By default it runs in the foreground and dies when the
terminal closes. For an unattended "digital employee" you have two options.
Security: prefer
--unified-app-id <uappid>over--robot-client-id/--robot-client-secret. With--unified-app-idthe CLI resolves clientId/clientSecret at runtime throughdev app credentials get, so the secret never appears inps/ journald / shell history. Pasting--robot-client-secretonto argv lets any local user read your AppSecret withps -ef; the CLI will warn you when you do that.
Option A: built-in daemon (recommended for a quick start)
# Detach into a background supervisor that restarts the connector if it crashes.
dws dev connect --daemon \
--channel claudecode \
--unified-app-id <unifiedAppId>
# Inspect / stop / restart it (locate the daemon by unifiedAppId).
dws dev connect status --unified-app-id <unifiedAppId>
dws dev connect stop --unified-app-id <unifiedAppId>
dws dev connect restart --unified-app-id <unifiedAppId>
- The parent prints the daemon pid and the log path, then exits.
- A supervisor process (POSIX
setsid, detached from the terminal) keeps a worker connector alive, restarting it with exponential backoff (1s..60s, up to 10 consecutive fast failures) when it exits abnormally. - The single-instance lock (one connector per robot per machine) is reused, so a duplicate daemon refuses to start.
- Logs go to
~/.dws/connect/<key>/daemon.logwith size-based rotation (5 MB x 2 backups), and the pid file lives at~/.dws/connect/<key>/daemon.pid. - The daemon does NOT survive a reboot. For that, use Option B.
Windows:
--daemonis not supported (nosetsid/ POSIX signal stop). Use a Windows service wrapper around the foreground command instead.
Option B: OS service manager (survives reboot)
Use the foreground command (NOT --daemon) and let the OS supervise and
restart it. This is the most robust way to get boot-time auto-start.
macOS — launchd
Save as ~/Library/LaunchAgents/com.dingtalk.dws.connect.plist, edit the paths
and REPLACE_UNIFIED_APP_ID, then launchctl load -w <path>.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.dingtalk.dws.connect</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/dws</string>
<string>dev</string>
<string>connect</string>
<string>--channel</string>
<string>claudecode</string>
<string>--unified-app-id</string>
<string>REPLACE_UNIFIED_APP_ID</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>ThrottleInterval</key>
<integer>10</integer>
<key>StandardOutPath</key>
<string>/tmp/dws-connect.out.log</string>
<key>StandardErrorPath</key>
<string>/tmp/dws-connect.err.log</string>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/usr/local/bin:/usr/bin:/bin</string>
</dict>
</dict>
</plist>
KeepAlive=true makes launchd restart the connector if it exits; the connector
itself relies on the single-instance lock to avoid duplicates.
Linux — systemd (user service)
Save as ~/.config/systemd/user/dws-connect.service, edit paths and
REPLACE_UNIFIED_APP_ID, then:
systemctl --user daemon-reload
systemctl --user enable --now dws-connect.service
# allow it to keep running after logout:
loginctl enable-linger "$USER"
[Unit]
Description=DWS DingTalk robot connector
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/dws dev connect \
--channel claudecode \
--unified-app-id REPLACE_UNIFIED_APP_ID
Restart=always
RestartSec=5
# Optional hardening:
# NoNewPrivileges=true
# PrivateTmp=true
[Install]
WantedBy=default.target
Restart=always + RestartSec gives crash recovery; systemd captures stdout/
stderr into the journal (journalctl --user -u dws-connect -f).
Legacy: passing clientId/clientSecret directly (not recommended)
If you truly must pass credentials on the command line (e.g. one-off local
debugging without a unifiedAppId), the CLI still accepts
--robot-client-id <id> --robot-client-secret <secret> and will print a
security warning to stderr. This form:
- exposes
clientSecretto every user on the box viaps -ef; - gets baked into launchd
ProgramArguments/ systemdExecStart, which makes rotation harder; - means
dws dev connect restartcannot re-fetch credentials — you must re-run the full command yourself.
Prefer --unified-app-id. Only fall back to the pair when you understand the
trade-off.
Which to choose
- Just need it to outlive the terminal and self-heal on crash →
--daemon. - Need it to come back after a reboot, with the OS owning the lifecycle → use launchd / systemd with the foreground command.