wtt-connect 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,295 @@
1
+ # wtt-connect
2
+
3
+ `wtt-connect` is the WTT-native connector daemon. It is **not** intended to be a reduced cc-connect clone: the goal is feature parity and then WTT-specific extensions while keeping surfaces such as TTS, agent adapters, task routing, chat routing, artifacts, and permission control as first-class modules.
4
+
5
+ Implemented production-oriented surfaces:
6
+
7
+ - Long-lived WTT WebSocket client
8
+ - WTT chat and task event intake
9
+ - Per-session mailbox/actor queue (`topic_id` / `task_id`) so busy acks are never treated as final replies
10
+ - Durable local state store for session/thread ids, dedupe, and artifact ledger
11
+ - Multi-adapter registry and routing, aligned with cc-connect's agent backend matrix without depending on a cc-connect process:
12
+ - `codex` via `codex exec --json`
13
+ - `claude-code` via `claude -p --output-format stream-json`
14
+ - `cursor` via Cursor Agent CLI (`agent --print --output-format stream-json`)
15
+ - `gemini` via Gemini CLI (`gemini --output-format stream-json`)
16
+ - `qoder` via `qodercli -f stream-json`
17
+ - `opencode` / `crush` via OpenCode-style `run --format json`
18
+ - `iflow` via iFlow CLI
19
+ - `kimi` via Kimi CLI (`kimi --print --output-format stream-json`)
20
+ - `pi` via Cursor Background Agent (`pi --mode json`)
21
+ - `acp` for any Agent Client Protocol compatible agent
22
+ - `devin` through ACP (`devin acp`)
23
+ - Normalized tool/progress event publishing
24
+ - TTS extension point:
25
+ - `none`
26
+ - `macos-say` using the macOS `say` command, emitted as WAV when upload is enabled
27
+ - HTTP task status update path
28
+ - Permission broker for Codex modes, including explicit opt-in for dangerous `yolo`
29
+ - Optional WTT media artifact upload path (`/media/sign` → direct upload → `/media/commit`)
30
+ - Attachment staging for WTT message media/files; image attachments are passed to Codex with `--image` (including Claude Code image fallback when the Claude provider cannot view images directly)
31
+ - STT extension point (`command` and OpenAI/Whisper providers) for audio attachments
32
+ - Setup/claim-code command, smoke-task/smoke-chat commands, start script, and macOS launchd installer
33
+ - npm-installable Node.js implementation (Node >= 22) with runtime dependencies installed by npm
34
+
35
+ ## Why this exists
36
+
37
+ cc-connect is a broad IM-to-agent bridge. WTT needs a connector that is native to WTT's identity, task, runner, artifact, permission, and observability model. `wtt-connect` should eventually cover cc-connect's useful capabilities instead of dropping them.
38
+
39
+ ## Quick start
40
+
41
+ Install once, then bind each WTT agent identity with one command:
42
+
43
+ ```bash
44
+ npm install -g wtt-connect
45
+ wtt-connect up codex agent-001 'wtt-tok-001'
46
+ wtt-connect up claude-code agent-002 'wtt-tok-002'
47
+ ```
48
+
49
+ `up` writes a profile, installs a background service, enables it for the current user, starts it immediately, and prints status. On Linux it installs a `systemd --user` service; on macOS it installs a LaunchAgent. The npm install step installs package dependencies such as `node-pty`; users should not need to run `npm install` inside a cloned source tree.
50
+
51
+ Common management commands:
52
+
53
+ ```bash
54
+ wtt-connect list
55
+ wtt-connect status all
56
+ wtt-connect restart all
57
+ wtt-connect logs agent-001-codex --lines 100
58
+ wtt-connect down agent-001-codex
59
+ ```
60
+
61
+ For source-tree development:
62
+
63
+ ```bash
64
+ cd tools/wtt-connect
65
+ node ./bin/wtt-connect.js setup --claim-code
66
+ npm run doctor
67
+ npm start
68
+ ```
69
+
70
+ Or run directly:
71
+
72
+ ```bash
73
+ node ./bin/wtt-connect.js doctor
74
+ node ./bin/wtt-connect.js start
75
+ ```
76
+
77
+ ## Configuration
78
+
79
+ Environment variables are loaded from `tools/wtt-connect/.env` and current working directory `.env`.
80
+ For multiple WTT agent identities, pass an explicit dotenv file with `--env-file` or `WTT_CONNECT_ENV_FILE`; that file is loaded before the default `.env` files, so it wins for that process.
81
+
82
+ Important settings:
83
+
84
+ - `WTT_BASE_URL`
85
+ - `WTT_AGENT_ID`
86
+ - `WTT_TOKEN`
87
+ - `WTT_CONNECT_ADAPTER=codex|claude-code|cursor|gemini|qoder|opencode|crush|iflow|kimi|pi|acp|devin`
88
+ - `WTT_CONNECT_ADAPTERS=codex,claude-code,cursor,gemini,qoder,opencode,iflow,kimi,pi,acp,devin`
89
+ - `WTT_CONNECT_WORKDIR`
90
+ - `WTT_CONNECT_MODE=full-auto|auto-edit|yolo|suggest`
91
+ - `WTT_CONNECT_AGENT_ALIASES=alias1,alias2` for extra @mention names in discussion/collaborative topics; `WTT_AGENT_ID` always works
92
+ - `WTT_CONNECT_ALLOW_YOLO=1` only when intentionally using `yolo`
93
+ - `WTT_CONNECT_STATE_DIR` / `WTT_CONNECT_STORE_FILE` for durable sessions
94
+ - `WTT_CONNECT_UPLOAD_ARTIFACTS=1` to upload generated summaries/TTS through WTT media
95
+ - `WTT_CONNECT_TTS_PROVIDER=none|macos-say`
96
+ - `WTT_CONNECT_STT_PROVIDER=none|command|openai|whisper` and `WTT_CONNECT_STT_COMMAND='...'`
97
+ - `WTT_CONNECT_STT_OPENAI_API_KEY`, `WTT_CONNECT_STT_OPENAI_BASE_URL`, `WTT_CONNECT_STT_OPENAI_MODEL`, `WTT_CONNECT_STT_OPENAI_TRANSPORT=auto|fetch|curl`, `WTT_CONNECT_STT_LANGUAGE` for OpenAI-compatible speech-to-text
98
+ - `WTT_CONNECT_ENABLE_SHELL=1` exposes an interactive agent-side Terminal from wtt-web for claimed online agents
99
+ - `WTT_CONNECT_SHELL_MODE=unsafe|readonly|off`; default `unsafe` runs one-shot shell commands without command filtering. The interactive Terminal always opens a real PTY shell on the agent host.
100
+ - `WTT_CONNECT_SHELL_TIMEOUT_SECONDS` and `WTT_CONNECT_SHELL_MAX_OUTPUT_CHARS` only bound legacy one-shot command execution and returned output
101
+
102
+ A JSON config may also be supplied:
103
+
104
+ ```bash
105
+ node ./bin/wtt-connect.js start --config ./config.json
106
+ ```
107
+
108
+ ### One-command service binding
109
+
110
+ The recommended install path for users is npm + `up`:
111
+
112
+ ```bash
113
+ npm install -g wtt-connect
114
+ wtt-connect up <agenttype> <agent_id> <token>
115
+ ```
116
+
117
+ Examples:
118
+
119
+ ```bash
120
+ wtt-connect up codex agent-alice 'wtt-tok-alice'
121
+ wtt-connect up codex agent-bob 'wtt-tok-bob'
122
+ wtt-connect up claude-code agent-claude 'wtt-tok-claude'
123
+ ```
124
+
125
+ Each call creates an independent profile and service:
126
+
127
+ ```text
128
+ ~/.config/wtt-connect/profiles/agent-alice-codex.env
129
+ ~/.local/state/wtt-connect/agent-alice-codex/state.json
130
+ ~/.config/systemd/user/wtt-connect@agent-alice-codex.service
131
+ ```
132
+
133
+ On macOS the same profile and state paths are used, while the service file is:
134
+
135
+ ```text
136
+ ~/Library/LaunchAgents/com.wtt.connect.agent-alice-codex.plist
137
+ ```
138
+
139
+ A profile contains exactly one WTT identity: `WTT_AGENT_ID`, `WTT_TOKEN`, adapter, workdir, permission mode, and state directory. Multiple profiles may point at the same local `codex` or `claude` CLI binary; they must not share `WTT_AGENT_ID`, `WTT_TOKEN`, or `WTT_CONNECT_STATE_DIR`.
140
+
141
+ Useful flags:
142
+
143
+ ```bash
144
+ wtt-connect up codex agent-alice 'wtt-tok-alice' \
145
+ --mode yolo \
146
+ --allow-yolo \
147
+ --workdir /data/workspaces/alice \
148
+ --base-url https://www.waxbyte.com \
149
+ --enable-linger
150
+ ```
151
+
152
+ `--enable-linger` asks systemd to start the Linux user service at boot before an interactive login. Without it, the service starts when the user session starts. On macOS LaunchAgents start at user login; `--enable-linger` is ignored with a warning.
153
+
154
+ Start an already-created profile in the foreground:
155
+
156
+ ```bash
157
+ wtt-connect start --profile agent-alice-codex
158
+ wtt-connect doctor --profile agent-alice-codex
159
+ ```
160
+
161
+ Service names are platform-specific:
162
+
163
+ ```text
164
+ Linux: wtt-connect@agent-alice-codex.service
165
+ macOS: com.wtt.connect.agent-alice-codex
166
+ ```
167
+
168
+ Logs are read with the same command on both platforms:
169
+
170
+ ```bash
171
+ wtt-connect logs agent-alice-codex --lines 100
172
+ ```
173
+
174
+ On Linux this reads `journalctl --user`; on macOS it tails files under the profile state directory.
175
+
176
+ ### Multiple WTT agent identities
177
+
178
+ Use one `wtt-connect` process per WTT agent identity. `wtt-connect up` is the preferred way to create those processes. Each process should have its own `WTT_AGENT_ID`, `WTT_TOKEN`, adapter default, and state directory:
179
+
180
+ ```dotenv
181
+ # .env.codex
182
+ WTT_AGENT_ID=agent-codex
183
+ WTT_TOKEN=***
184
+ WTT_CONNECT_ADAPTER=codex
185
+ WTT_CONNECT_ADAPTERS=codex
186
+ WTT_CONNECT_STATE_DIR=.wtt-connect-codex
187
+ ```
188
+
189
+ ```dotenv
190
+ # .env.claude
191
+ WTT_AGENT_ID=agent-claude
192
+ WTT_TOKEN=***
193
+ WTT_CONNECT_ADAPTER=claude-code
194
+ WTT_CONNECT_ADAPTERS=claude-code
195
+ WTT_CONNECT_STATE_DIR=.wtt-connect-claude
196
+ ```
197
+
198
+ Start them separately:
199
+
200
+ ```bash
201
+ ./start.sh --env-file .env.codex
202
+ ./start.sh --env-file .env.claude
203
+ ```
204
+
205
+ Install them as separate macOS LaunchAgents:
206
+
207
+ ```bash
208
+ ./scripts/install-launchd.sh --name codex --env-file .env.codex
209
+ ./scripts/install-launchd.sh --name claude --env-file .env.claude
210
+ ```
211
+
212
+ Install them as separate Linux `systemd --user` services from a source checkout if you are not using npm:
213
+
214
+ ```bash
215
+ ./scripts/install-agent.sh codex agent-codex '***'
216
+ ./scripts/install-agent.sh claude-code agent-claude '***'
217
+ ```
218
+
219
+ The npm equivalent is preferred for end users:
220
+
221
+ ```bash
222
+ wtt-connect up codex agent-codex '***'
223
+ wtt-connect up claude-code agent-claude '***'
224
+ ```
225
+
226
+ Use named options when you need a custom service name, permission mode, or boot-before-login behavior:
227
+
228
+ ```bash
229
+ ./scripts/install-systemd-user.sh \
230
+ --name alice-codex \
231
+ --agent-id agent-codex \
232
+ --token '***' \
233
+ --adapter codex \
234
+ --mode yolo \
235
+ --allow-yolo \
236
+ --publish-progress \
237
+ --enable-linger
238
+
239
+ ./scripts/install-systemd-user.sh \
240
+ --name alice-claude \
241
+ --agent-id agent-claude \
242
+ --token '***' \
243
+ --adapter claude-code \
244
+ --mode yolo \
245
+ --allow-yolo \
246
+ --publish-progress \
247
+ --enable-linger
248
+ ```
249
+
250
+ Each invocation writes a separate `.env.<name>`, `.wtt-connect-<name>` state directory, and `wtt-connect-<name>.service`. Multiple WTT agent identities may share the same local `codex` or `claude` CLI binary; only the WTT identity and state directory need to be unique.
251
+
252
+ Uninstall one identity with:
253
+
254
+ ```bash
255
+ ./scripts/uninstall-launchd.sh --name codex
256
+ ./scripts/uninstall-systemd-user.sh --name alice-codex
257
+ ```
258
+
259
+ Discussion/collaborative topics are mention-gated: `wtt-connect` only replies when the message targets its `WTT_AGENT_ID`, one of `WTT_CONNECT_AGENT_ALIASES`, backend-resolved `runner_agent_id`, or a multi-mention `mention_target_agent_ids` entry. P2P and task topics still run normally.
260
+
261
+ Adapter routing can be explicit through task fields such as `exec_mode=gemini`, or through message mentions such as `@codex`, `@claude`, `@cursor`, `@gemini`, `@qoder`, `@opencode`, `@iflow`, `@kimi`, `@pi`, `@acp`, or `@devin` when those adapters are enabled.
262
+
263
+ OpenClaw is deliberately not part of the default `wtt-connect` adapter set because WTT already has the first-class `wtt-plugin` for OpenClaw.
264
+
265
+ ## Direction
266
+
267
+ Near-term parity targets with cc-connect:
268
+
269
+ 1. Rich attachment rendering in WTT Web and task artifact schema persistence
270
+ 2. Fine-grained permission broker with per-task policy, not just env-level modes
271
+ 3. TTS/STT providers beyond macOS `say`
272
+ 4. Multiple concurrent local adapters under one claimed WTT agent
273
+ 5. Durable session store expanded across all native adapters
274
+ 6. Tool/progress event normalization for WTT Web observability
275
+ 7. Installer/claim flow integrated with wtt-web
276
+ 8. Artifact schema in task rows, not only topic messages
277
+
278
+ ## Production helpers
279
+
280
+ ```bash
281
+ ./start.sh
282
+ ./scripts/install-launchd.sh
283
+ ./scripts/uninstall-launchd.sh
284
+ ```
285
+
286
+ ## Smoke tests
287
+
288
+ Run the connector in one terminal, then:
289
+
290
+ ```bash
291
+ node ./bin/wtt-connect.js smoke-task --expected WTT_CONNECT_TASK_READY
292
+ node ./bin/wtt-connect.js smoke-chat --expected WTT_CONNECT_CHAT_READY
293
+ ```
294
+
295
+ `smoke-chat` requires a second sender agent via `WTT_SMOKE_SENDER_AGENT_ID` and `WTT_SMOKE_SENDER_TOKEN`.
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { main } from '../src/main.js';
3
+
4
+ main(process.argv.slice(2)).catch((err) => {
5
+ console.error(err?.stack || err?.message || String(err));
6
+ process.exitCode = 1;
7
+ });
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "wtt-connect",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "description": "WTT-native connector daemon for Codex, Claude Code, Cursor, Gemini, ACP, and other coding agent surfaces.",
6
+ "type": "module",
7
+ "bin": {
8
+ "wtt-connect": "bin/wtt-connect.js"
9
+ },
10
+ "scripts": {
11
+ "check": "node --check bin/wtt-connect.js && node --check src/*.js && node --check src/adapters/*.js",
12
+ "doctor": "node ./bin/wtt-connect.js doctor",
13
+ "setup": "node ./bin/wtt-connect.js setup",
14
+ "smoke:task": "node ./bin/wtt-connect.js smoke-task",
15
+ "smoke:chat": "node ./bin/wtt-connect.js smoke-chat",
16
+ "start": "node ./bin/wtt-connect.js start"
17
+ },
18
+ "engines": {
19
+ "node": ">=22"
20
+ },
21
+ "dependencies": {
22
+ "node-pty": "^1.1.0"
23
+ },
24
+ "files": [
25
+ "bin",
26
+ "src",
27
+ "scripts",
28
+ "systemd",
29
+ "README.md",
30
+ "package.json"
31
+ ]
32
+ }
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
4
+ exec "$ROOT/scripts/install-systemd-user.sh" "$@"
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
4
+ NAME=""
5
+ ENV_FILE=""
6
+ while [[ $# -gt 0 ]]; do
7
+ case "$1" in
8
+ --name)
9
+ NAME="$2"; shift 2 ;;
10
+ --env-file)
11
+ ENV_FILE="$2"; shift 2 ;;
12
+ *)
13
+ echo "unknown argument: $1" >&2
14
+ echo "usage: $0 [--name codex] [--env-file .env.codex]" >&2
15
+ exit 2 ;;
16
+ esac
17
+ done
18
+ if [[ -z "$NAME" && -n "$ENV_FILE" ]]; then
19
+ base="$(basename "$ENV_FILE")"
20
+ NAME="${base#.env.}"
21
+ NAME="${NAME#.env}"
22
+ fi
23
+ LABEL="com.wtt.connect${NAME:+.$NAME}"
24
+ PLIST="$HOME/Library/LaunchAgents/$LABEL.plist"
25
+ mkdir -p "$HOME/Library/LaunchAgents" "$ROOT/logs"
26
+ PROGRAM_ARGS="<string>$ROOT/start.sh</string>"
27
+ if [[ -n "$ENV_FILE" ]]; then
28
+ PROGRAM_ARGS="$PROGRAM_ARGS<string>--env-file</string><string>$ENV_FILE</string>"
29
+ fi
30
+ NODE_BIN="$(command -v node || true)"
31
+ NODE_DIR="$(dirname "${NODE_BIN:-/usr/bin/node}")"
32
+ LAUNCH_PATH="$NODE_DIR:$PATH"
33
+ cat > "$PLIST" <<PLIST
34
+ <?xml version="1.0" encoding="UTF-8"?>
35
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
36
+ <plist version="1.0"><dict>
37
+ <key>Label</key><string>$LABEL</string>
38
+ <key>ProgramArguments</key><array>$PROGRAM_ARGS</array>
39
+ <key>WorkingDirectory</key><string>$ROOT</string>
40
+ <key>EnvironmentVariables</key><dict>
41
+ <key>PATH</key><string>$LAUNCH_PATH</string>
42
+ </dict>
43
+ <key>RunAtLoad</key><true/>
44
+ <key>KeepAlive</key><true/>
45
+ <key>StandardOutPath</key><string>$ROOT/logs/${LABEL}.out.log</string>
46
+ <key>StandardErrorPath</key><string>$ROOT/logs/${LABEL}.err.log</string>
47
+ </dict></plist>
48
+ PLIST
49
+ launchctl unload "$PLIST" >/dev/null 2>&1 || true
50
+ launchctl load "$PLIST"
51
+ echo "installed $PLIST"
@@ -0,0 +1,189 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
5
+
6
+ usage() {
7
+ local code="${1:-2}"
8
+ cat >&2 <<'USAGE'
9
+ Usage:
10
+ install-systemd-user.sh <agenttype> <agent_id> <token> [options]
11
+ install-systemd-user.sh --name <name> --agent-id <agent_id> --token <token> --adapter <adapter> [options]
12
+
13
+ Minimal:
14
+ <agenttype> codex or claude-code
15
+ <agent_id> WTT agent id
16
+ <token> WTT agent token
17
+
18
+ Named options:
19
+ --name <name> Local service/env suffix, e.g. alice-codex.
20
+ Defaults to <agent_id>-<adapter>.
21
+ --agent-id <agent_id> WTT agent id
22
+ --token <token> WTT agent token
23
+ --adapter <adapter> codex, claude-code, cursor, gemini, qoder, opencode, iflow, kimi, pi, acp, devin.
24
+ Defaults to codex.
25
+
26
+ Options:
27
+ --base-url <url> WTT base URL (default: https://www.waxbyte.com)
28
+ --workdir <dir> Agent working directory (default: wtt-connect root)
29
+ --env-file <file> Env file to write (default: .env.<name>)
30
+ --state-dir <dir> State directory (default: .wtt-connect-<name>)
31
+ --mode <mode> full-auto, auto-edit, suggest, yolo (default: full-auto)
32
+ --allow-yolo Required when --mode yolo is used
33
+ --publish-progress Publish agent progress/status messages
34
+ --timeout <seconds> Per-run timeout (default: 3600)
35
+ --node-bin <path> node binary (default: command -v node)
36
+ --codex-bin <path> codex binary shared by any number of services
37
+ --claude-bin <path> claude binary shared by any number of services
38
+ --enable-linger Enable systemd user linger so services start at boot before login
39
+ --no-start Enable service but do not start it now
40
+ USAGE
41
+ exit "$code"
42
+ }
43
+
44
+ NAME=""
45
+ AGENT_ID=""
46
+ TOKEN=""
47
+ ADAPTER=""
48
+ BASE_URL="https://www.waxbyte.com"
49
+ WORKDIR="$ROOT"
50
+ ENV_FILE=""
51
+ STATE_DIR=""
52
+ MODE="${WTT_CONNECT_MODE:-full-auto}"
53
+ ALLOW_YOLO=0
54
+ PUBLISH_PROGRESS=0
55
+ TIMEOUT_SECONDS=3600
56
+ NODE_BIN="${WTT_NODE_BIN:-$(command -v node || true)}"
57
+ CODEX_BIN="${WTT_CODEX_BIN:-$(command -v codex || true)}"
58
+ CLAUDE_BIN="${WTT_CLAUDE_BIN:-$(command -v claude || true)}"
59
+ ENABLE_LINGER=0
60
+ START_NOW=1
61
+ POSITIONAL=()
62
+
63
+ while [[ $# -gt 0 ]]; do
64
+ case "$1" in
65
+ --name) NAME="$2"; shift 2 ;;
66
+ --agent-id) AGENT_ID="$2"; shift 2 ;;
67
+ --token) TOKEN="$2"; shift 2 ;;
68
+ --adapter) ADAPTER="$2"; shift 2 ;;
69
+ --base-url) BASE_URL="$2"; shift 2 ;;
70
+ --workdir) WORKDIR="$2"; shift 2 ;;
71
+ --env-file) ENV_FILE="$2"; shift 2 ;;
72
+ --state-dir) STATE_DIR="$2"; shift 2 ;;
73
+ --mode) MODE="$2"; shift 2 ;;
74
+ --allow-yolo) ALLOW_YOLO=1; shift ;;
75
+ --publish-progress) PUBLISH_PROGRESS=1; shift ;;
76
+ --timeout) TIMEOUT_SECONDS="$2"; shift 2 ;;
77
+ --node-bin) NODE_BIN="$2"; shift 2 ;;
78
+ --codex-bin) CODEX_BIN="$2"; shift 2 ;;
79
+ --claude-bin) CLAUDE_BIN="$2"; shift 2 ;;
80
+ --enable-linger) ENABLE_LINGER=1; shift ;;
81
+ --no-start) START_NOW=0; shift ;;
82
+ -h|--help) usage 0 ;;
83
+ --*) echo "unknown argument: $1" >&2; usage ;;
84
+ *)
85
+ POSITIONAL+=("$1")
86
+ shift
87
+ ;;
88
+ esac
89
+ done
90
+
91
+ if [[ "${#POSITIONAL[@]}" -gt 0 ]]; then
92
+ [[ -z "$AGENT_ID" && -z "$TOKEN" ]] || { echo "do not mix positional agent/token with --agent-id/--token" >&2; exit 2; }
93
+ [[ -z "$ADAPTER" ]] || { echo "do not mix positional agenttype with --adapter" >&2; exit 2; }
94
+ [[ "${#POSITIONAL[@]}" -eq 3 ]] || { echo "positional usage: $0 <agenttype> <agent_id> <token>" >&2; usage; }
95
+ ADAPTER="${POSITIONAL[0]}"
96
+ AGENT_ID="${POSITIONAL[1]}"
97
+ TOKEN="${POSITIONAL[2]}"
98
+ fi
99
+
100
+ ADAPTER="${ADAPTER:-codex}"
101
+ if [[ -z "$NAME" && -n "$AGENT_ID" ]]; then
102
+ NAME="$AGENT_ID-$ADAPTER"
103
+ fi
104
+
105
+ [[ -n "$NAME" && -n "$AGENT_ID" && -n "$TOKEN" && -n "$ADAPTER" ]] || usage
106
+ [[ -n "$NODE_BIN" && -x "$NODE_BIN" ]] || { echo "node binary not found; pass --node-bin" >&2; exit 1; }
107
+
108
+ SAFE_NAME="$(printf '%s' "$NAME" | tr -cs 'A-Za-z0-9_.@-' '-' | sed 's/^-//; s/-$//')"
109
+ [[ -n "$SAFE_NAME" ]] || { echo "invalid --name" >&2; exit 1; }
110
+
111
+ case "$MODE" in
112
+ suggest|auto-edit|full-auto|yolo) ;;
113
+ *) echo "invalid --mode: $MODE" >&2; exit 1 ;;
114
+ esac
115
+ if [[ "$MODE" == "yolo" && "$ALLOW_YOLO" != "1" ]]; then
116
+ echo "--mode yolo requires --allow-yolo" >&2
117
+ exit 1
118
+ fi
119
+
120
+ ENV_FILE="${ENV_FILE:-$ROOT/.env.$SAFE_NAME}"
121
+ STATE_DIR="${STATE_DIR:-$ROOT/.wtt-connect-$SAFE_NAME}"
122
+ SERVICE_NAME="wtt-connect-$SAFE_NAME.service"
123
+ SYSTEMD_USER_DIR="$HOME/.config/systemd/user"
124
+ SERVICE_PATH="$SYSTEMD_USER_DIR/$SERVICE_NAME"
125
+
126
+ NODE_MAJOR="$("$NODE_BIN" -p 'Number(process.versions.node.split(".")[0])')"
127
+ NODE_WS_FLAG=()
128
+ if [[ "$NODE_MAJOR" -lt 22 ]]; then
129
+ NODE_WS_FLAG=(--experimental-websocket)
130
+ fi
131
+
132
+ mkdir -p "$(dirname "$ENV_FILE")" "$STATE_DIR" "$SYSTEMD_USER_DIR"
133
+
134
+ {
135
+ echo "# wtt-connect config for $NAME"
136
+ echo "# Keep local; contains agent secrets."
137
+ echo "WTT_BASE_URL=$BASE_URL"
138
+ echo "WTT_AGENT_ID=$AGENT_ID"
139
+ echo "WTT_TOKEN=$TOKEN"
140
+ echo "WTT_CONNECT_ADAPTERS=$ADAPTER"
141
+ echo "WTT_CONNECT_ADAPTER=$ADAPTER"
142
+ echo "WTT_CONNECT_WORKDIR=$WORKDIR"
143
+ echo "WTT_CONNECT_MODE=$MODE"
144
+ if [[ "$ALLOW_YOLO" == "1" ]]; then echo "WTT_CONNECT_ALLOW_YOLO=1"; fi
145
+ echo "WTT_CONNECT_ENABLE_CHAT=1"
146
+ echo "WTT_CONNECT_PUBLISH_PROGRESS=$PUBLISH_PROGRESS"
147
+ echo "WTT_CONNECT_TASK_TIMEOUT_SECONDS=$TIMEOUT_SECONDS"
148
+ echo "WTT_CONNECT_STATE_DIR=$STATE_DIR"
149
+ echo "WTT_REQUIRE_COMMIT_PUSH=1"
150
+ if [[ -n "$CODEX_BIN" ]]; then echo "WTT_CODEX_BIN=$CODEX_BIN"; fi
151
+ if [[ -n "$CLAUDE_BIN" ]]; then echo "WTT_CLAUDE_BIN=$CLAUDE_BIN"; fi
152
+ } > "$ENV_FILE"
153
+ chmod 600 "$ENV_FILE"
154
+
155
+ cat > "$SERVICE_PATH" <<SERVICE
156
+ [Unit]
157
+ Description=WTT Connect Agent ($NAME)
158
+ After=network-online.target
159
+ Wants=network-online.target
160
+
161
+ [Service]
162
+ Type=simple
163
+ WorkingDirectory=$ROOT
164
+ Environment=PATH=/home/cecwxf/.local/npm-global/bin:/usr/local/bin:/usr/bin:/bin
165
+ ExecStart=$NODE_BIN ${NODE_WS_FLAG[*]} $ROOT/bin/wtt-connect.js start --env-file $ENV_FILE
166
+ Restart=always
167
+ RestartSec=5
168
+
169
+ [Install]
170
+ WantedBy=default.target
171
+ SERVICE
172
+
173
+ systemctl --user daemon-reload
174
+ if [[ "$START_NOW" == "1" ]]; then
175
+ systemctl --user enable --now "$SERVICE_NAME"
176
+ else
177
+ systemctl --user enable "$SERVICE_NAME"
178
+ fi
179
+
180
+ if [[ "$ENABLE_LINGER" == "1" ]]; then
181
+ loginctl enable-linger "${USER}" || {
182
+ echo "warning: failed to enable linger; service will start when the user session starts" >&2
183
+ }
184
+ fi
185
+
186
+ echo "installed service: $SERVICE_NAME"
187
+ echo "env file: $ENV_FILE"
188
+ echo "state dir: $STATE_DIR"
189
+ systemctl --user --no-pager --full status "$SERVICE_NAME" || true
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ NAME=""
4
+ while [[ $# -gt 0 ]]; do
5
+ case "$1" in
6
+ --name)
7
+ NAME="$2"; shift 2 ;;
8
+ *)
9
+ echo "unknown argument: $1" >&2
10
+ echo "usage: $0 [--name codex]" >&2
11
+ exit 2 ;;
12
+ esac
13
+ done
14
+ LABEL="com.wtt.connect${NAME:+.$NAME}"
15
+ PLIST="$HOME/Library/LaunchAgents/$LABEL.plist"
16
+ launchctl unload "$PLIST" >/dev/null 2>&1 || true
17
+ rm -f "$PLIST"
18
+ echo "uninstalled $PLIST"
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
5
+
6
+ usage() {
7
+ local code="${1:-2}"
8
+ cat >&2 <<'USAGE'
9
+ Usage:
10
+ uninstall-systemd-user.sh --name <name> [--delete-env]
11
+ USAGE
12
+ exit "$code"
13
+ }
14
+
15
+ NAME=""
16
+ DELETE_ENV=0
17
+ while [[ $# -gt 0 ]]; do
18
+ case "$1" in
19
+ --name) NAME="$2"; shift 2 ;;
20
+ --delete-env) DELETE_ENV=1; shift ;;
21
+ -h|--help) usage 0 ;;
22
+ *) echo "unknown argument: $1" >&2; usage ;;
23
+ esac
24
+ done
25
+
26
+ [[ -n "$NAME" ]] || usage
27
+ SAFE_NAME="$(printf '%s' "$NAME" | tr -cs 'A-Za-z0-9_.@-' '-' | sed 's/^-//; s/-$//')"
28
+ [[ -n "$SAFE_NAME" ]] || { echo "invalid --name" >&2; exit 1; }
29
+
30
+ SERVICE_NAME="wtt-connect-$SAFE_NAME.service"
31
+ SERVICE_PATH="$HOME/.config/systemd/user/$SERVICE_NAME"
32
+ ENV_FILE="$ROOT/.env.$SAFE_NAME"
33
+
34
+ systemctl --user disable --now "$SERVICE_NAME" >/dev/null 2>&1 || true
35
+ rm -f "$SERVICE_PATH"
36
+ systemctl --user daemon-reload
37
+
38
+ if [[ "$DELETE_ENV" == "1" ]]; then
39
+ rm -f "$ENV_FILE"
40
+ fi
41
+
42
+ echo "uninstalled service: $SERVICE_NAME"