@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/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@superclawd-ai/fda",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "private": false,
5
- "description": "SuperClawd FDA \u2014 a forward deployed agent for your team: a Slack-driven AI developer that runs on its own machine with your SuperClawd workspace.",
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}'",
@@ -1,109 +1,249 @@
1
1
  #!/usr/bin/env bash
2
- # SuperClawd FDA machine installer (Ubuntu 24.04+). Run as the operator user, NOT root:
3
- # curl -fsSLO https://superclawd.com/fda/install.sh && bash install.sh [--with-browser]
4
- # It uses sudo only for system packages and the systemd unit. Safe to re-run (idempotent).
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
- # Pinned versions (bumped together with the setup guide's dependency table).
9
- local NODE_MAJOR=22
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
- --from-dir) FROM_DIR="$2"; shift ;; # dev: install packed tarballs (superclawd-*.tgz, superclawd-ai-fda-*.tgz)
20
- -h|--help) echo "Usage: bash install.sh [--with-browser] [--from-dir <dir-with-tgz>]"; return 0 ;;
21
- *) echo "Unknown option: $1" >&2; return 64 ;;
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
- if [ "$(id -u)" -eq 0 ]; then
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
- if [ "${ID:-}" != "ubuntu" ]; then
32
- echo "! This installer is tested on Ubuntu 24.04+; continuing on ${PRETTY_NAME:-an unknown system}." >&2
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
- echo "→ System packages"
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 ! command -v node >/dev/null || [ "$(node -p 'process.versions.node.split(".")[0]')" -lt "$NODE_MAJOR" ] \
40
- || ! node -e 'const [a,b]=process.versions.node.split(".").map(Number); process.exit(a>22||(a===22&&b>=13)?0:1)'; then
41
- echo "→ Node.js $NODE_MAJOR (NodeSource)"
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
- if ! command -v gh >/dev/null; then
47
- echo "→ GitHub CLI"
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
- echo "→ SuperClawd CLI + FDA"
57
- if [ -n "$FROM_DIR" ]; then
58
- sudo npm i -g --no-fund --no-audit "$FROM_DIR"/superclawd-[0-9]*.tgz "$FROM_DIR"/superclawd-ai-fda-*.tgz >/dev/null
59
- else
60
- sudo npm i -g --no-fund --no-audit "superclawd@${SUPERCLAWD_VERSION}" "@superclawd-ai/fda@${FDA_VERSION}" >/dev/null
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
- if ! command -v claude >/dev/null && [ ! -x "$HOME/.local/bin/claude" ]; then
64
- echo "→ Claude Code"
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
- mkdir -p -m 700 "$HOME/.superclawd/fda"
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' > "$envfile"
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
- echo "→ Browser (Playwright MCP $PLAYWRIGHT_MCP_VERSION + Chromium)"
77
- local bdir="$HOME/.superclawd/fda/browser"
78
- mkdir -p "$bdir"
79
- (cd "$bdir" && npm init -y >/dev/null && npm i --no-fund --no-audit "@playwright/mcp@${PLAYWRIGHT_MCP_VERSION}" >/dev/null)
80
- local pwv
81
- pwv="$(node -p "require('$bdir/node_modules/playwright/package.json').version")"
82
- sudo npx -y "playwright@${pwv}" install-deps chromium >/dev/null
83
- npx -y "playwright@${pwv}" install chromium >/dev/null
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
- echo "→ Service"
87
- local node cli tmpl
88
- node="$(command -v node)"
89
- cli="$(npm root -g)/@superclawd-ai/fda/dist-server/cli.mjs"
90
- tmpl="$(npm root -g)/@superclawd-ai/fda/scripts/superclawd-fda.service.tmpl"
91
- [ -f "$cli" ] || { echo "✗ FDA package not found at $cli" >&2; return 1; }
92
- sed -e "s|__USER__|$(id -un)|g" -e "s|__HOME__|$HOME|g" -e "s|__NODE__|$node|g" -e "s|__CLI__|$cli|g" "$tmpl" \
93
- | sudo tee /etc/systemd/system/superclawd-fda.service >/dev/null
94
- sudo systemctl daemon-reload
95
- sudo systemctl enable superclawd-fda >/dev/null
96
-
97
- cat <<NEXT
98
-
99
- ✓ Installed. Next steps (see the setup guide):
100
- 1. superclawd login
101
- 2. superclawd fda setup --workspace <your-workspace-slug>
102
- 3. printf '%s' "\$ANTHROPIC_API_KEY" | superclawd fda config set-key
103
- 4. superclawd fda start && superclawd fda doctor
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
- Environment=PATH=__HOME__/.local/bin:/usr/local/bin:/usr/bin:/bin
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