@superclawd-ai/fda 0.1.0 → 0.1.1
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 +2 -0
- package/dist-server/cli.mjs +83 -83
- package/dist-server/index.mjs +72 -72
- package/package.json +4 -3
- package/scripts/install.sh +207 -67
- package/scripts/superclawd-fda.service.tmpl +2 -1
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@superclawd-ai/fda",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"private": false,
|
|
5
|
-
"description": "SuperClawd FDA
|
|
5
|
+
"description": "SuperClawd FDA — a forward deployed agent for your team: a Slack-driven AI developer that runs on its own machine with your SuperClawd workspace.",
|
|
6
6
|
"author": "SuperClawd <support@superclawd.com>",
|
|
7
7
|
"license": "SEE LICENSE IN LICENSE",
|
|
8
8
|
"homepage": "https://superclawd.com",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "https://github.com/superclawd-ai/superclawd"
|
|
11
|
+
"url": "git+https://github.com/superclawd-ai/superclawd.git"
|
|
12
12
|
},
|
|
13
13
|
"keywords": [
|
|
14
14
|
"superclawd",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"dev:api": "FDA_HOME=.fda-dev tsx watch server/index.ts",
|
|
39
39
|
"dev:web": "vite",
|
|
40
40
|
"build": "node build.mjs",
|
|
41
|
+
"prepublishOnly": "npm run build",
|
|
41
42
|
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.app.json",
|
|
42
43
|
"test": "tsx test/run-all.ts",
|
|
43
44
|
"lint": "eslint 'server/**/*.ts' 'src/**/*.{ts,tsx}'",
|
package/scripts/install.sh
CHANGED
|
@@ -1,109 +1,249 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# SuperClawd FDA machine
|
|
3
|
-
#
|
|
4
|
-
#
|
|
2
|
+
# SuperClawd FDA one-run machine bootstrap (Ubuntu 24.04+). Takes a fresh VM to a ready FDA machine:
|
|
3
|
+
# system packages, bubblewrap, Node.js (nvm), the GitHub CLI, Tailscale, Claude Code, the `superclawd`
|
|
4
|
+
# CLI + `@superclawd-ai/fda`, the systemd service, and optionally Homebrew and the browser. Only the
|
|
5
|
+
# logins and tokens are left at the end (it prints them).
|
|
6
|
+
#
|
|
7
|
+
# Run as the operator user, NOT root. It uses sudo only for system packages, Tailscale and the unit.
|
|
8
|
+
# Safe to re-run: every step checks first and skips what's already there.
|
|
9
|
+
#
|
|
10
|
+
# bash install.sh [options]
|
|
11
|
+
#
|
|
12
|
+
# --with-browser also install the optional browser (Playwright MCP + Chromium)
|
|
13
|
+
# --with-homebrew also install Homebrew (Linuxbrew); nothing here needs it
|
|
14
|
+
# --node nvm|system install Node this way even if one is found (default: keep a suitable
|
|
15
|
+
# Node 22.13+, otherwise nvm)
|
|
16
|
+
# --no-tailscale skip Tailscale
|
|
17
|
+
# --no-service skip the systemd unit (e.g. containers, or to run in the foreground)
|
|
18
|
+
# --git-name <name> set the commit identity, if git doesn't have one yet
|
|
19
|
+
# --git-email <email>
|
|
20
|
+
# --from-dir <dir> dev: install packed tarballs (superclawd-*.tgz, superclawd-ai-fda-*.tgz)
|
|
21
|
+
#
|
|
22
|
+
# Environment: TS_AUTHKEY (joins the tailnet without a browser login; never printed),
|
|
23
|
+
# SUPERCLAWD_VERSION / FDA_VERSION (default: latest).
|
|
5
24
|
set -euo pipefail
|
|
6
25
|
|
|
26
|
+
# Pinned versions (bumped together with the setup guide's dependency table).
|
|
27
|
+
NODE_MAJOR=22
|
|
28
|
+
NVM_VERSION="v0.40.8"
|
|
29
|
+
|
|
30
|
+
say() { printf '%s\n' "$*"; }
|
|
31
|
+
step() { printf '\n→ %s\n' "$*"; }
|
|
32
|
+
ok() { printf ' ✓ %s\n' "$*"; }
|
|
33
|
+
skip() { printf ' · %s\n' "$*"; }
|
|
34
|
+
warn() { printf ' ! %s\n' "$*" >&2; }
|
|
35
|
+
die() { printf '✗ %s\n' "$*" >&2; exit 1; }
|
|
36
|
+
have() { command -v "$1" >/dev/null 2>&1; }
|
|
37
|
+
# Run quietly; show the captured output only if it fails.
|
|
38
|
+
quiet() {
|
|
39
|
+
local log rc=0
|
|
40
|
+
log="$(mktemp)"
|
|
41
|
+
"$@" >"$log" 2>&1 || rc=$?
|
|
42
|
+
[ "$rc" -eq 0 ] || cat "$log" >&2
|
|
43
|
+
rm -f "$log"
|
|
44
|
+
return "$rc"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
node_ok() {
|
|
48
|
+
have node && node -e 'const [a,b]=process.versions.node.split(".").map(Number); process.exit(a>22||(a===22&&b>=13)?0:1)' 2>/dev/null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# nvm isn't written for `set -eu` (it returns non-zero and reads unset variables), so strict mode
|
|
52
|
+
# is off only while it runs.
|
|
53
|
+
load_nvm() {
|
|
54
|
+
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
|
55
|
+
[ -s "$NVM_DIR/nvm.sh" ] || return 0
|
|
56
|
+
set +eu
|
|
57
|
+
# shellcheck disable=SC1091
|
|
58
|
+
. "$NVM_DIR/nvm.sh"
|
|
59
|
+
set -eu
|
|
60
|
+
}
|
|
61
|
+
nvm_run() {
|
|
62
|
+
local rc=0
|
|
63
|
+
set +eu
|
|
64
|
+
quiet nvm "$@" || rc=$?
|
|
65
|
+
set -eu
|
|
66
|
+
return "$rc"
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# `npm i -g` needs sudo only when the global prefix isn't ours (system Node).
|
|
70
|
+
npm_g() {
|
|
71
|
+
if [ -w "$(npm prefix -g)" ]; then npm i -g --no-fund --no-audit "$@"; else sudo npm i -g --no-fund --no-audit "$@"; fi
|
|
72
|
+
}
|
|
73
|
+
|
|
7
74
|
main() {
|
|
8
|
-
|
|
9
|
-
local
|
|
10
|
-
local SUPERCLAWD_VERSION="${SUPERCLAWD_VERSION:-latest}"
|
|
11
|
-
local FDA_VERSION="${FDA_VERSION:-latest}"
|
|
12
|
-
local PLAYWRIGHT_MCP_VERSION="0.0.83"
|
|
13
|
-
local WITH_BROWSER=0
|
|
14
|
-
local FROM_DIR=""
|
|
75
|
+
local WITH_BROWSER=0 WITH_BREW=0 NODE_VIA="" TAILSCALE=1 SERVICE=1 FROM_DIR="" GIT_NAME="" GIT_EMAIL=""
|
|
76
|
+
local SUPERCLAWD_VERSION="${SUPERCLAWD_VERSION:-latest}" FDA_VERSION="${FDA_VERSION:-latest}"
|
|
15
77
|
|
|
16
78
|
while [ $# -gt 0 ]; do
|
|
17
79
|
case "$1" in
|
|
18
80
|
--with-browser) WITH_BROWSER=1 ;;
|
|
19
|
-
--
|
|
20
|
-
|
|
21
|
-
|
|
81
|
+
--with-homebrew) WITH_BREW=1 ;;
|
|
82
|
+
--node) NODE_VIA="${2:-}"; shift ;;
|
|
83
|
+
--no-tailscale) TAILSCALE=0 ;;
|
|
84
|
+
--no-service) SERVICE=0 ;;
|
|
85
|
+
--git-name) GIT_NAME="${2:-}"; shift ;;
|
|
86
|
+
--git-email) GIT_EMAIL="${2:-}"; shift ;;
|
|
87
|
+
--from-dir) FROM_DIR="${2:-}"; shift ;;
|
|
88
|
+
-h|--help) awk 'NR>1 && /^#/ {sub(/^# ?/,""); print; next} NR>1 {exit}' "$0"; return 0 ;;
|
|
89
|
+
*) die "Unknown option: $1 (see --help)" ;;
|
|
22
90
|
esac
|
|
23
91
|
shift
|
|
24
92
|
done
|
|
93
|
+
case "$NODE_VIA" in ""|nvm|system) ;; *) die "--node must be nvm or system" ;; esac
|
|
25
94
|
|
|
26
|
-
|
|
27
|
-
echo "✗ Run this as your regular user (not root). It uses sudo only where needed." >&2
|
|
28
|
-
return 1
|
|
29
|
-
fi
|
|
95
|
+
[ "$(id -u)" -ne 0 ] || die "Run this as your regular user (not root). It uses sudo only where needed."
|
|
30
96
|
. /etc/os-release 2>/dev/null || true
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
fi
|
|
97
|
+
[ "${ID:-}" = "ubuntu" ] || warn "Tested on Ubuntu 24.04+; continuing on ${PRETTY_NAME:-an unknown system}."
|
|
98
|
+
sudo -v || die "This needs sudo for system packages."
|
|
34
99
|
|
|
35
|
-
|
|
100
|
+
step "System packages (git, curl, jq, bubblewrap)"
|
|
36
101
|
sudo apt-get update -qq
|
|
37
|
-
sudo apt-get install -y -qq git curl ca-certificates jq gnupg bubblewrap >/dev/null
|
|
102
|
+
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq git curl ca-certificates jq gnupg bubblewrap >/dev/null
|
|
103
|
+
ok "installed"
|
|
38
104
|
|
|
39
|
-
if
|
|
40
|
-
|
|
41
|
-
|
|
105
|
+
if [ "$WITH_BREW" = 1 ]; then
|
|
106
|
+
step "Homebrew"
|
|
107
|
+
if [ -x /home/linuxbrew/.linuxbrew/bin/brew ] || have brew; then
|
|
108
|
+
skip "already installed"
|
|
109
|
+
else
|
|
110
|
+
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq build-essential procps file >/dev/null
|
|
111
|
+
NONINTERACTIVE=1 bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" >/dev/null
|
|
112
|
+
# shellcheck disable=SC2016
|
|
113
|
+
grep -q 'linuxbrew/bin/brew shellenv' "$HOME/.bashrc" 2>/dev/null \
|
|
114
|
+
|| echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"' >>"$HOME/.bashrc"
|
|
115
|
+
ok "installed (new shells pick it up from ~/.bashrc)"
|
|
116
|
+
fi
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
step "Node.js $NODE_MAJOR"
|
|
120
|
+
load_nvm
|
|
121
|
+
if [ -z "$NODE_VIA" ] && node_ok; then
|
|
122
|
+
skip "found $(node -v) at $(command -v node)"
|
|
123
|
+
elif [ "$NODE_VIA" = system ] && node_ok && [ "$(command -v node)" = /usr/bin/node ]; then
|
|
124
|
+
skip "found $(node -v) at /usr/bin/node"
|
|
125
|
+
elif [ "$NODE_VIA" != system ]; then
|
|
126
|
+
if [ ! -s "${NVM_DIR:-$HOME/.nvm}/nvm.sh" ]; then
|
|
127
|
+
curl -fsSL "https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_VERSION}/install.sh" -o "$HOME/.nvm-install.sh"
|
|
128
|
+
PROFILE="$HOME/.bashrc" quiet bash "$HOME/.nvm-install.sh" || die "the nvm installer failed"
|
|
129
|
+
rm -f "$HOME/.nvm-install.sh"
|
|
130
|
+
load_nvm
|
|
131
|
+
fi
|
|
132
|
+
nvm_run install "$NODE_MAJOR" || die "nvm install $NODE_MAJOR failed"
|
|
133
|
+
nvm_run alias default "$NODE_MAJOR"
|
|
134
|
+
nvm_run use default
|
|
135
|
+
ok "$(node -v) via nvm"
|
|
136
|
+
else
|
|
42
137
|
curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | sudo -E bash - >/dev/null
|
|
43
|
-
sudo apt-get install -y -qq nodejs >/dev/null
|
|
138
|
+
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq nodejs >/dev/null
|
|
139
|
+
ok "$(node -v) via NodeSource"
|
|
44
140
|
fi
|
|
141
|
+
node_ok || die "Node.js 22.13+ is still missing."
|
|
45
142
|
|
|
46
|
-
|
|
47
|
-
|
|
143
|
+
step "GitHub CLI"
|
|
144
|
+
if have gh; then
|
|
145
|
+
skip "already installed"
|
|
146
|
+
else
|
|
48
147
|
sudo mkdir -p -m 755 /etc/apt/keyrings
|
|
49
148
|
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg >/dev/null
|
|
50
149
|
sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg
|
|
51
150
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
|
|
52
151
|
| sudo tee /etc/apt/sources.list.d/github-cli.list >/dev/null
|
|
53
|
-
sudo apt-get update -qq && sudo apt-get install -y -qq gh >/dev/null
|
|
152
|
+
sudo apt-get update -qq && sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq gh >/dev/null
|
|
153
|
+
ok "installed"
|
|
54
154
|
fi
|
|
55
155
|
|
|
56
|
-
|
|
57
|
-
if [
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
156
|
+
local TS_STATE="skipped"
|
|
157
|
+
if [ "$TAILSCALE" = 1 ]; then
|
|
158
|
+
step "Tailscale"
|
|
159
|
+
if have tailscale; then skip "already installed"; else curl -fsSL https://tailscale.com/install.sh | sh >/dev/null 2>&1; ok "installed"; fi
|
|
160
|
+
if tailscale status >/dev/null 2>&1; then
|
|
161
|
+
TS_STATE="up"; skip "already on a tailnet"
|
|
162
|
+
elif [ -n "${TS_AUTHKEY:-}" ]; then
|
|
163
|
+
# The key goes through a file, not argv, so it doesn't show up in `ps`.
|
|
164
|
+
local kf; kf="$(mktemp)"; chmod 600 "$kf"; printf '%s' "$TS_AUTHKEY" >"$kf"
|
|
165
|
+
if sudo tailscale up --auth-key="file:$kf" >/dev/null; then TS_STATE="up"; ok "joined the tailnet"; else warn "tailscale up failed"; TS_STATE="down"; fi
|
|
166
|
+
rm -f "$kf"
|
|
167
|
+
else
|
|
168
|
+
TS_STATE="down"; skip "not logged in yet (listed at the end)"
|
|
169
|
+
fi
|
|
61
170
|
fi
|
|
62
171
|
|
|
63
|
-
|
|
64
|
-
|
|
172
|
+
step "Claude Code"
|
|
173
|
+
if have claude || [ -x "$HOME/.local/bin/claude" ]; then
|
|
174
|
+
skip "already installed"
|
|
175
|
+
else
|
|
65
176
|
curl -fsSL https://claude.ai/install.sh | bash >/dev/null
|
|
177
|
+
ok "installed"
|
|
178
|
+
fi
|
|
179
|
+
case ":$PATH:" in *":$HOME/.local/bin:"*) ;; *) export PATH="$HOME/.local/bin:$PATH" ;; esac
|
|
180
|
+
|
|
181
|
+
step "SuperClawd CLI + FDA"
|
|
182
|
+
if [ -n "$FROM_DIR" ]; then
|
|
183
|
+
npm_g "$FROM_DIR"/superclawd-[0-9]*.tgz "$FROM_DIR"/superclawd-ai-fda-*.tgz >/dev/null
|
|
184
|
+
else
|
|
185
|
+
npm_g "superclawd@${SUPERCLAWD_VERSION}" "@superclawd-ai/fda@${FDA_VERSION}" >/dev/null
|
|
66
186
|
fi
|
|
187
|
+
hash -r
|
|
188
|
+
ok "superclawd $(superclawd --version 2>/dev/null | tail -1), FDA $(npm ls -g --depth=0 @superclawd-ai/fda 2>/dev/null | grep -o 'fda@[^ ]*' | cut -d@ -f2)"
|
|
67
189
|
|
|
68
|
-
|
|
190
|
+
# Claude Code must not update itself under a running agent (the operator updates it on purpose).
|
|
191
|
+
mkdir -p "$HOME/.superclawd/fda" && chmod 700 "$HOME/.superclawd" "$HOME/.superclawd/fda"
|
|
69
192
|
local envfile="$HOME/.superclawd/fda/env"
|
|
70
193
|
if [ ! -f "$envfile" ]; then
|
|
71
|
-
printf 'DISABLE_AUTOUPDATER=1\n' >
|
|
194
|
+
printf 'DISABLE_AUTOUPDATER=1\n' >"$envfile"
|
|
72
195
|
chmod 600 "$envfile"
|
|
73
196
|
fi
|
|
74
197
|
|
|
198
|
+
step "git identity"
|
|
199
|
+
if [ -n "$GIT_NAME" ] && [ -z "$(git config --global user.name || true)" ]; then git config --global user.name "$GIT_NAME"; fi
|
|
200
|
+
if [ -n "$GIT_EMAIL" ] && [ -z "$(git config --global user.email || true)" ]; then git config --global user.email "$GIT_EMAIL"; fi
|
|
201
|
+
if [ -n "$(git config --global user.name || true)" ] && [ -n "$(git config --global user.email || true)" ]; then
|
|
202
|
+
ok "$(git config --global user.name) <$(git config --global user.email)>"
|
|
203
|
+
else
|
|
204
|
+
skip "not set yet (listed at the end)"
|
|
205
|
+
fi
|
|
206
|
+
|
|
75
207
|
if [ "$WITH_BROWSER" = 1 ]; then
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
208
|
+
step "Browser"
|
|
209
|
+
superclawd fda browser install --yes
|
|
210
|
+
fi
|
|
211
|
+
|
|
212
|
+
if [ "$SERVICE" = 1 ]; then
|
|
213
|
+
step "Service"
|
|
214
|
+
if [ -d /run/systemd/system ]; then
|
|
215
|
+
superclawd fda service install --yes >/dev/null
|
|
216
|
+
ok "superclawd-fda installed and enabled (starts on boot)"
|
|
217
|
+
if [ "$TS_STATE" = up ]; then
|
|
218
|
+
if sudo tailscale serve --bg --https=443 http://127.0.0.1:63220 >/dev/null 2>&1; then
|
|
219
|
+
ok "admin panel on the tailnet: https://$(tailscale status --json 2>/dev/null | jq -r '.Self.DNSName' | sed 's/\.$//')"
|
|
220
|
+
else
|
|
221
|
+
warn "tailscale serve didn't work (HTTPS may be off for the tailnet); see the setup guide"
|
|
222
|
+
fi
|
|
223
|
+
fi
|
|
224
|
+
else
|
|
225
|
+
warn "no systemd here; run it in the foreground with: superclawd fda run"
|
|
226
|
+
fi
|
|
84
227
|
fi
|
|
85
228
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
sudo
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
5. Open the admin panel (tailscale serve --bg --https=443 http://127.0.0.1:63220), sign in with
|
|
105
|
-
the password from: superclawd fda credentials
|
|
106
|
-
NEXT
|
|
229
|
+
step "Check"
|
|
230
|
+
superclawd fda doctor || true
|
|
231
|
+
|
|
232
|
+
say ""
|
|
233
|
+
say "✓ Machine ready. What's left is logins and tokens:"
|
|
234
|
+
local n=1
|
|
235
|
+
if [ -s "${NVM_DIR:-$HOME/.nvm}/nvm.sh" ]; then
|
|
236
|
+
say " $n. Open a new shell (or: source ~/.bashrc) so nvm's node is on PATH"; n=$((n + 1))
|
|
237
|
+
fi
|
|
238
|
+
if [ "$TS_STATE" = down ]; then say " $n. sudo tailscale up # join your tailnet"; n=$((n + 1)); fi
|
|
239
|
+
if [ -z "$(git config --global user.email || true)" ]; then
|
|
240
|
+
say " $n. git config --global user.name \"…\" && git config --global user.email \"…\""; n=$((n + 1))
|
|
241
|
+
fi
|
|
242
|
+
if ! gh auth status >/dev/null 2>&1; then say " $n. gh auth login # for pull requests"; n=$((n + 1)); fi
|
|
243
|
+
say " $n. superclawd login && superclawd fda setup"; n=$((n + 1))
|
|
244
|
+
say " $n. superclawd fda config set-key # your Anthropic API key, from stdin"; n=$((n + 1))
|
|
245
|
+
say " $n. superclawd fda start && superclawd fda credentials"; n=$((n + 1))
|
|
246
|
+
say " $n. In the admin panel: create the Slack app from the manifest, paste its tokens, set who can use it"
|
|
107
247
|
}
|
|
108
248
|
|
|
109
249
|
main "$@"
|
|
@@ -11,7 +11,8 @@ Type=simple
|
|
|
11
11
|
User=__USER__
|
|
12
12
|
WorkingDirectory=__HOME__
|
|
13
13
|
Environment=HOME=__HOME__
|
|
14
|
-
|
|
14
|
+
# Node's own bin dir comes early so nvm installs (node, npm globals like `superclawd`) are found.
|
|
15
|
+
Environment=PATH=__HOME__/.local/bin:__NODE_BIN__:/usr/local/bin:/usr/bin:/bin
|
|
15
16
|
Environment=NODE_ENV=production
|
|
16
17
|
EnvironmentFile=-__HOME__/.superclawd/fda/env
|
|
17
18
|
ExecStart=__NODE__ __CLI__ run
|