@saadnvd1/agent-os 0.3.810 → 0.3.815
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 +3 -2
- package/app/api/reach/probe/route.test.ts +22 -0
- package/app/api/reach/probe/route.ts +12 -0
- package/app/api/reach/route.ts +30 -0
- package/app/api/sessions/list-size.test.ts +77 -0
- package/app/api/sessions/route.ts +21 -10
- package/app/api/tasks/list-size.test.ts +50 -0
- package/app/api/tasks/route.ts +20 -3
- package/components/Chat/ViewSwitch.tsx +2 -1
- package/components/Devices/NetworkSection.tsx +2 -0
- package/components/Pane/index.tsx +4 -2
- package/components/Reach/ReachRow.tsx +137 -0
- package/components/Settings/SetupSection.tsx +2 -0
- package/components/Welcome/ConnectBlock.tsx +2 -0
- package/content/tips.json +10 -0
- package/content/whats-new.json +32 -0
- package/data/devices/queries.ts +6 -1
- package/data/reach/index.ts +33 -0
- package/lib/connect/serve.ts +22 -3
- package/lib/connect/supervise.ts +33 -9
- package/lib/db/queries.ts +31 -4
- package/lib/db/types.ts +3 -1
- package/lib/guides.ts +6 -0
- package/lib/hosts/remote-menu.ts +4 -2
- package/lib/reach/check-cli.ts +117 -0
- package/lib/reach/checkReach.ts +62 -0
- package/lib/reach/classify.ts +283 -0
- package/lib/reach/cli.ts +38 -0
- package/lib/reach/gatherFacts.ts +167 -0
- package/lib/reach/index.ts +4 -0
- package/lib/reach/probeConnect.ts +122 -0
- package/lib/reach/types.ts +96 -0
- package/lib/security/auth.ts +2 -0
- package/lib/security/demo.ts +2 -0
- package/lib/sessions/has-prompt.ts +7 -0
- package/lib/sessions/omit-null.ts +8 -0
- package/lib/tasks/contract/views.ts +12 -0
- package/lib/tasks/index.ts +9 -5
- package/lib/tasks/session.ts +15 -1
- package/lib/tips/view.ts +2 -4
- package/package.json +1 -1
- package/scripts/agent-os +3 -0
- package/scripts/install.sh +12 -1
- package/scripts/lib/commands.sh +59 -65
- package/scripts/lib/service.sh +230 -0
- package/scripts/lib/setup.sh +303 -0
- package/scripts/lib/setup.test.ts +389 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# The background service: launchd on macOS, systemd elsewhere. One render per
|
|
3
|
+
# init system, and one reconcile that writes only what changed, used by
|
|
4
|
+
# `agent-os enable` and `agent-os setup`.
|
|
5
|
+
|
|
6
|
+
LAUNCHD_LABEL="com.agent-os"
|
|
7
|
+
|
|
8
|
+
launchd_plist_path() { echo "$HOME/Library/LaunchAgents/$LAUNCHD_LABEL.plist"; }
|
|
9
|
+
systemd_unit_path() { echo "$HOME/.config/systemd/user/agent-os.service"; }
|
|
10
|
+
launchd_domain() { echo "gui/$(id -u)"; }
|
|
11
|
+
|
|
12
|
+
# launchd | systemd | none
|
|
13
|
+
service_kind() {
|
|
14
|
+
if [[ "$OS" == "macos" ]]; then
|
|
15
|
+
echo launchd
|
|
16
|
+
elif has_systemd; then
|
|
17
|
+
echo systemd
|
|
18
|
+
else
|
|
19
|
+
echo none
|
|
20
|
+
fi
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
service_path() {
|
|
24
|
+
case "$(service_kind)" in
|
|
25
|
+
launchd) launchd_plist_path ;;
|
|
26
|
+
systemd) systemd_unit_path ;;
|
|
27
|
+
esac
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# A path as plist text: & < > would break the XML.
|
|
31
|
+
# sed, not ${//}: bash 5.2 reads & in a replacement as the match.
|
|
32
|
+
xml_text() {
|
|
33
|
+
printf '%s' "$1" | sed -e 's/&/\&/g' -e 's/</\</g' -e 's/>/\>/g'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# A path as one systemd ExecStart word: % is a specifier, and a space would
|
|
37
|
+
# split it. A plain path comes out as it went in.
|
|
38
|
+
systemd_word() {
|
|
39
|
+
local s="${1//%/%%}"
|
|
40
|
+
if [[ "$s" =~ [[:space:]\"\'\\] ]]; then
|
|
41
|
+
s="${s//\\/\\\\}"
|
|
42
|
+
s="\"${s//\"/\\\"}\""
|
|
43
|
+
fi
|
|
44
|
+
printf '%s' "$s"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
# The server runs in the foreground as launchd's own process, so a crash is
|
|
48
|
+
# restarted (a clean exit isn't), and terminals (tmux) and chat workers
|
|
49
|
+
# outlive a restart, as KillMode=process does on Linux.
|
|
50
|
+
render_launchd_plist() {
|
|
51
|
+
local script_path log_file
|
|
52
|
+
script_path=$(xml_text "$1")
|
|
53
|
+
log_file=$(xml_text "$LOG_FILE")
|
|
54
|
+
cat << EOF
|
|
55
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
56
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
57
|
+
<plist version="1.0">
|
|
58
|
+
<dict>
|
|
59
|
+
<key>Label</key>
|
|
60
|
+
<string>$LAUNCHD_LABEL</string>
|
|
61
|
+
<key>ProgramArguments</key>
|
|
62
|
+
<array>
|
|
63
|
+
<string>$script_path</string>
|
|
64
|
+
<string>start-foreground</string>
|
|
65
|
+
</array>
|
|
66
|
+
<key>RunAtLoad</key>
|
|
67
|
+
<true/>
|
|
68
|
+
<key>KeepAlive</key>
|
|
69
|
+
<dict>
|
|
70
|
+
<key>SuccessfulExit</key>
|
|
71
|
+
<false/>
|
|
72
|
+
</dict>
|
|
73
|
+
<key>ThrottleInterval</key>
|
|
74
|
+
<integer>10</integer>
|
|
75
|
+
<key>AbandonProcessGroup</key>
|
|
76
|
+
<true/>
|
|
77
|
+
<key>StandardOutPath</key>
|
|
78
|
+
<string>$log_file</string>
|
|
79
|
+
<key>StandardErrorPath</key>
|
|
80
|
+
<string>$log_file</string>
|
|
81
|
+
<key>EnvironmentVariables</key>
|
|
82
|
+
<dict>
|
|
83
|
+
<key>PATH</key>
|
|
84
|
+
<string>/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin</string>
|
|
85
|
+
</dict>
|
|
86
|
+
</dict>
|
|
87
|
+
</plist>
|
|
88
|
+
EOF
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# What `agent-os enable` wrote before: it ran `agent-os start`, which exits at
|
|
92
|
+
# once, so launchd never restarted a crashed server. Recognised so setup can
|
|
93
|
+
# offer the update rather than calling it yours.
|
|
94
|
+
render_launchd_plist_v1() {
|
|
95
|
+
local script_path log_file
|
|
96
|
+
script_path=$(xml_text "$1")
|
|
97
|
+
log_file=$(xml_text "$LOG_FILE")
|
|
98
|
+
cat << EOF
|
|
99
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
100
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
101
|
+
<plist version="1.0">
|
|
102
|
+
<dict>
|
|
103
|
+
<key>Label</key>
|
|
104
|
+
<string>$LAUNCHD_LABEL</string>
|
|
105
|
+
<key>ProgramArguments</key>
|
|
106
|
+
<array>
|
|
107
|
+
<string>$script_path</string>
|
|
108
|
+
<string>start</string>
|
|
109
|
+
</array>
|
|
110
|
+
<key>RunAtLoad</key>
|
|
111
|
+
<true/>
|
|
112
|
+
<key>KeepAlive</key>
|
|
113
|
+
<false/>
|
|
114
|
+
<key>StandardOutPath</key>
|
|
115
|
+
<string>$log_file</string>
|
|
116
|
+
<key>StandardErrorPath</key>
|
|
117
|
+
<string>$log_file</string>
|
|
118
|
+
<key>EnvironmentVariables</key>
|
|
119
|
+
<dict>
|
|
120
|
+
<key>PATH</key>
|
|
121
|
+
<string>/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin</string>
|
|
122
|
+
</dict>
|
|
123
|
+
</dict>
|
|
124
|
+
</plist>
|
|
125
|
+
EOF
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
render_systemd_unit() {
|
|
129
|
+
local script_path
|
|
130
|
+
script_path=$(systemd_word "$1")
|
|
131
|
+
cat << EOF
|
|
132
|
+
[Unit]
|
|
133
|
+
Description=AgentOS - AI Coding Session Manager
|
|
134
|
+
After=network.target
|
|
135
|
+
|
|
136
|
+
[Service]
|
|
137
|
+
Type=simple
|
|
138
|
+
ExecStart=$script_path start-foreground
|
|
139
|
+
Restart=on-failure
|
|
140
|
+
RestartSec=10
|
|
141
|
+
# Only the server is stopped on a restart: terminals (tmux), chat workers and
|
|
142
|
+
# an update in progress outlive it, as they do under launchd.
|
|
143
|
+
KillMode=process
|
|
144
|
+
Environment=PATH=/usr/local/bin:/usr/bin:/bin
|
|
145
|
+
|
|
146
|
+
[Install]
|
|
147
|
+
WantedBy=default.target
|
|
148
|
+
EOF
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
render_service() {
|
|
152
|
+
case "$(service_kind)" in
|
|
153
|
+
launchd) render_launchd_plist "$1" ;;
|
|
154
|
+
systemd) render_systemd_unit "$1" ;;
|
|
155
|
+
esac
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
service_script_path() { realpath "$0"; }
|
|
159
|
+
|
|
160
|
+
# absent | same | older | changed: the file on disk against what we'd write.
|
|
161
|
+
service_file_state() {
|
|
162
|
+
local path script_path
|
|
163
|
+
path=$(service_path)
|
|
164
|
+
script_path=$(service_script_path)
|
|
165
|
+
if [[ -z "$path" || ! -f "$path" ]]; then
|
|
166
|
+
echo absent
|
|
167
|
+
elif [[ "$(cat "$path")" == "$(render_service "$script_path")" ]]; then
|
|
168
|
+
echo same
|
|
169
|
+
elif [[ "$(service_kind)" == launchd && "$(cat "$path")" == "$(render_launchd_plist_v1 "$script_path")" ]]; then
|
|
170
|
+
echo older
|
|
171
|
+
else
|
|
172
|
+
echo changed
|
|
173
|
+
fi
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
# Writes the service file when it differs. Prints unchanged | installed |
|
|
177
|
+
# updated | kept-yours. "keep" leaves a file that isn't ours as it is; one
|
|
178
|
+
# that is replaced is kept next to it as .bak first.
|
|
179
|
+
service_reconcile() {
|
|
180
|
+
local mode="${1:-replace}" state path
|
|
181
|
+
state=$(service_file_state)
|
|
182
|
+
path=$(service_path)
|
|
183
|
+
case "$state" in
|
|
184
|
+
same) echo unchanged; return 0 ;;
|
|
185
|
+
changed)
|
|
186
|
+
if [[ "$mode" == keep ]]; then echo kept-yours; return 0; fi
|
|
187
|
+
cp -p "$path" "$path.bak"
|
|
188
|
+
log_info "Kept your version as $path.bak" >&2
|
|
189
|
+
;;
|
|
190
|
+
esac
|
|
191
|
+
mkdir -p "${path%/*}"
|
|
192
|
+
render_service "$(service_script_path)" > "$path"
|
|
193
|
+
[[ "$state" == absent ]] && echo installed || echo updated
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
launchd_loaded() {
|
|
197
|
+
launchctl print "$(launchd_domain)/$LAUNCHD_LABEL" > /dev/null 2>&1
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
# Whether the plist on disk is the foreground one launchd can keep alive.
|
|
201
|
+
launchd_keeps_alive() {
|
|
202
|
+
local path
|
|
203
|
+
path=$(launchd_plist_path)
|
|
204
|
+
[[ -f "$path" ]] && grep -q "<string>start-foreground</string>" "$path"
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
# (Re)load the agent. bootout first: bootstrap refuses an agent already
|
|
208
|
+
# loaded. Fails over ssh with no GUI login: the caller says what to do.
|
|
209
|
+
launchd_bootstrap() {
|
|
210
|
+
launchctl bootout "$(launchd_domain)/$LAUNCHD_LABEL" > /dev/null 2>&1 || true
|
|
211
|
+
launchctl bootstrap "$(launchd_domain)" "$(launchd_plist_path)" 2> /dev/null
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
launchd_no_gui_session() {
|
|
215
|
+
log_warn "launchd wouldn't load AgentOS in this session (no-gui-session: no desktop login, for example over ssh)."
|
|
216
|
+
echo " Run 'agent-os enable' once from a terminal on the Mac itself."
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
launchd_bootout() {
|
|
220
|
+
launchctl bootout "$(launchd_domain)/$LAUNCHD_LABEL" > /dev/null 2>&1 || true
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
launchd_restart() {
|
|
224
|
+
launchctl kickstart -k "$(launchd_domain)/$LAUNCHD_LABEL" > /dev/null 2>&1
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
# Starts it if it isn't running; never kills one that is.
|
|
228
|
+
launchd_kickstart() {
|
|
229
|
+
launchctl kickstart "$(launchd_domain)/$LAUNCHD_LABEL" > /dev/null 2>&1
|
|
230
|
+
}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# agent-os setup: install, keep it running, choose how other devices reach
|
|
3
|
+
# this machine, check that they can, then add a phone. Every step checks first,
|
|
4
|
+
# so running it again only does what's missing. It never touches the firewall,
|
|
5
|
+
# Tailscale, the router or DNS: it says what to do instead.
|
|
6
|
+
|
|
7
|
+
# Runs a TypeScript entry point from the checkout this script belongs to.
|
|
8
|
+
aos_tsx() { (cd "$(dirname "$SCRIPT_DIR")" && npx tsx "$@"); }
|
|
9
|
+
|
|
10
|
+
SETUP_VIA=""
|
|
11
|
+
SETUP_YES=0
|
|
12
|
+
SETUP_SERVICE=1
|
|
13
|
+
SETUP_PAIR=1
|
|
14
|
+
|
|
15
|
+
setup_title() { echo ""; echo -e "${BOLD}$1${NC}"; }
|
|
16
|
+
setup_ok() { echo -e " ${GREEN}✓${NC} $1"; }
|
|
17
|
+
setup_done() { echo -e " ${GREEN}✓${NC} $1 (already done)"; }
|
|
18
|
+
setup_note() { echo " $1"; }
|
|
19
|
+
|
|
20
|
+
# A yes/no question. Under --yes it takes $3 (or the default) without asking.
|
|
21
|
+
setup_yn() {
|
|
22
|
+
if [[ "$SETUP_YES" == 1 ]] || ! is_interactive; then
|
|
23
|
+
[[ "${3:-$2}" == y ]]
|
|
24
|
+
return
|
|
25
|
+
fi
|
|
26
|
+
prompt_yn " $1" "$2"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
setup_api() {
|
|
30
|
+
curl -fsS --max-time "${2:-5}" "http://127.0.0.1:$PORT$1" 2> /dev/null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# One field from JSON on stdin, by dotted path; empty when missing.
|
|
34
|
+
json_field() {
|
|
35
|
+
node -e '
|
|
36
|
+
let s = "";
|
|
37
|
+
process.stdin.on("data", (d) => (s += d)).on("end", () => {
|
|
38
|
+
let v;
|
|
39
|
+
try { v = process.argv[1].split(".").reduce((o, k) => o?.[k], JSON.parse(s)); } catch {}
|
|
40
|
+
process.stdout.write(v == null ? "" : String(v));
|
|
41
|
+
});' "$1"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# up | not-agentos | down: what answers on AgentOS's port.
|
|
45
|
+
setup_server_state() {
|
|
46
|
+
local health
|
|
47
|
+
if health=$(setup_api /api/health 3) && [[ "$(echo "$health" | json_field startedAt)" != "" ]]; then
|
|
48
|
+
echo up
|
|
49
|
+
elif curl -s --max-time 3 -o /dev/null "http://127.0.0.1:$PORT/" 2> /dev/null; then
|
|
50
|
+
echo not-agentos
|
|
51
|
+
else
|
|
52
|
+
echo down
|
|
53
|
+
fi
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# Whether the server on the port is ours to manage: our pid file, our launchd
|
|
57
|
+
# agent or our systemd unit. Otherwise something else runs it.
|
|
58
|
+
setup_server_is_ours() {
|
|
59
|
+
is_running && return 0
|
|
60
|
+
[[ "${OS:-}" == "macos" ]] && launchd_loaded && return 0
|
|
61
|
+
systemd_unit_active
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
setup_wait_up() {
|
|
65
|
+
local i
|
|
66
|
+
for ((i = 0; i < ${1:-30}; i++)); do
|
|
67
|
+
[[ "$(setup_server_state)" == up ]] && return 0
|
|
68
|
+
sleep 1
|
|
69
|
+
done
|
|
70
|
+
return 1
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
setup_install() {
|
|
74
|
+
setup_title "1. Install"
|
|
75
|
+
if [[ -f "$REPO_DIR/.next/BUILD_ID" ]]; then
|
|
76
|
+
setup_done "AgentOS is installed"
|
|
77
|
+
else
|
|
78
|
+
cmd_install
|
|
79
|
+
fi
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
setup_service() {
|
|
83
|
+
setup_title "2. Keep it running"
|
|
84
|
+
local server
|
|
85
|
+
server=$(setup_server_state)
|
|
86
|
+
if [[ "$server" == not-agentos ]]; then
|
|
87
|
+
log_error "Another program holds port $PORT."
|
|
88
|
+
aos_tsx lib/reach/cli.ts "$PORT"
|
|
89
|
+
exit 2
|
|
90
|
+
fi
|
|
91
|
+
if [[ "$server" == up ]] && ! setup_server_is_ours; then
|
|
92
|
+
setup_ok "AgentOS is already running, started some other way: leaving it as it is"
|
|
93
|
+
return 0
|
|
94
|
+
fi
|
|
95
|
+
if [[ "$SETUP_SERVICE" == 1 && "$(service_kind)" != none ]]; then
|
|
96
|
+
local path
|
|
97
|
+
path=$(service_path)
|
|
98
|
+
case "$(service_file_state)" in
|
|
99
|
+
same)
|
|
100
|
+
setup_done "Starts at login and restarts after a crash"
|
|
101
|
+
;;
|
|
102
|
+
absent)
|
|
103
|
+
if setup_yn "Start AgentOS at login and keep it running?" y; then
|
|
104
|
+
cmd_enable
|
|
105
|
+
fi
|
|
106
|
+
;;
|
|
107
|
+
older)
|
|
108
|
+
setup_note "The service AgentOS installed before doesn't restart it after a crash."
|
|
109
|
+
if setup_yn "Update it?" y n; then
|
|
110
|
+
cmd_enable
|
|
111
|
+
else
|
|
112
|
+
setup_ok "Kept the current service file"
|
|
113
|
+
fi
|
|
114
|
+
;;
|
|
115
|
+
changed)
|
|
116
|
+
setup_note "$path differs from what AgentOS would write (edited by you?)."
|
|
117
|
+
if setup_yn "Replace it with AgentOS's own?" n n; then
|
|
118
|
+
cmd_enable
|
|
119
|
+
else
|
|
120
|
+
setup_ok "Kept yours"
|
|
121
|
+
fi
|
|
122
|
+
;;
|
|
123
|
+
esac
|
|
124
|
+
fi
|
|
125
|
+
if [[ "$(setup_server_state)" == up ]]; then
|
|
126
|
+
setup_ok "AgentOS is running on port $PORT"
|
|
127
|
+
return 0
|
|
128
|
+
fi
|
|
129
|
+
cmd_start
|
|
130
|
+
if setup_wait_up 30; then
|
|
131
|
+
setup_ok "AgentOS is running on port $PORT"
|
|
132
|
+
else
|
|
133
|
+
log_error "AgentOS didn't answer within 30 seconds."
|
|
134
|
+
aos_tsx lib/reach/cli.ts "$PORT"
|
|
135
|
+
exit 2
|
|
136
|
+
fi
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
# The route already set up, or Connect.
|
|
140
|
+
setup_default_via() {
|
|
141
|
+
local net="$1"
|
|
142
|
+
if [[ -n "$(echo "$net" | json_field connect.hostname)" || "$(echo "$net" | json_field connect.enrolled)" == true ]]; then
|
|
143
|
+
echo connect
|
|
144
|
+
elif [[ "$(echo "$net" | json_field tailscale.state)" == running ]]; then
|
|
145
|
+
echo tailscale
|
|
146
|
+
elif [[ "$(echo "$net" | json_field lan.on)" == true ]]; then
|
|
147
|
+
echo lan
|
|
148
|
+
else
|
|
149
|
+
echo connect
|
|
150
|
+
fi
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
setup_choose_via() {
|
|
154
|
+
local net="$1" default n
|
|
155
|
+
default=$(setup_default_via "$net")
|
|
156
|
+
if [[ -n "$SETUP_VIA" ]]; then
|
|
157
|
+
return 0
|
|
158
|
+
fi
|
|
159
|
+
if [[ "$SETUP_YES" == 1 ]] || ! is_interactive; then
|
|
160
|
+
SETUP_VIA="$default"
|
|
161
|
+
return 0
|
|
162
|
+
fi
|
|
163
|
+
local -a vias=(connect tailscale lan)
|
|
164
|
+
local d=1
|
|
165
|
+
[[ "$default" == tailscale ]] && d=2
|
|
166
|
+
[[ "$default" == lan ]] && d=3
|
|
167
|
+
setup_note "1) Connect: works from any network, nothing to open on your router. Free for one machine."
|
|
168
|
+
setup_note "2) Tailscale: your own private network, if you already use it."
|
|
169
|
+
setup_note "3) Same Wi-Fi only."
|
|
170
|
+
read -p " Choose [$d]: " -r n
|
|
171
|
+
n="${n:-$d}"
|
|
172
|
+
if [[ ! "$n" =~ ^[123]$ ]]; then
|
|
173
|
+
log_warn "Not one of 1, 2 or 3: using $d"
|
|
174
|
+
n="$d"
|
|
175
|
+
fi
|
|
176
|
+
SETUP_VIA="${vias[$((n - 1))]}"
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
setup_connect() {
|
|
180
|
+
local net="$1"
|
|
181
|
+
if [[ -z "$(echo "$net" | json_field connect.hostname)" && "$(echo "$net" | json_field connect.enrolled)" != true ]]; then
|
|
182
|
+
if [[ "$SETUP_YES" == 1 ]] || ! is_interactive; then
|
|
183
|
+
setup_note "Connect needs a link code: run 'agent-os connect' and paste it."
|
|
184
|
+
return 0
|
|
185
|
+
fi
|
|
186
|
+
setup_note "Connect needs a free account and a link code."
|
|
187
|
+
if setup_yn "Open the sign-up page?" y; then
|
|
188
|
+
open_url "https://runagentos.com/sign-in?mode=sign-up" || true
|
|
189
|
+
fi
|
|
190
|
+
# The connect command asks for the code itself: it never passes
|
|
191
|
+
# through this script.
|
|
192
|
+
aos_tsx lib/connect/cli.ts || return 0
|
|
193
|
+
elif [[ "$(echo "$net" | json_field connect.state)" == off ]]; then
|
|
194
|
+
aos_tsx lib/connect/cli.ts --on || return 0
|
|
195
|
+
else
|
|
196
|
+
setup_done "Connect is linked"
|
|
197
|
+
fi
|
|
198
|
+
local i state
|
|
199
|
+
printf " Connecting"
|
|
200
|
+
for ((i = 0; i < 60; i++)); do
|
|
201
|
+
state=$(setup_api /api/devices/network | json_field connect.state)
|
|
202
|
+
[[ "$state" == up ]] && break
|
|
203
|
+
[[ "$state" == denied || "$state" == refused ]] && break
|
|
204
|
+
printf "."
|
|
205
|
+
sleep 1
|
|
206
|
+
done
|
|
207
|
+
echo ""
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
setup_lan() {
|
|
211
|
+
local net="$1"
|
|
212
|
+
if [[ "$(echo "$net" | json_field lan.on)" == true ]]; then
|
|
213
|
+
setup_done "Devices on this Wi-Fi can connect"
|
|
214
|
+
return 0
|
|
215
|
+
fi
|
|
216
|
+
setup_note "Use this on networks you trust: Wi-Fi traffic to AgentOS isn't encrypted."
|
|
217
|
+
if setup_yn "Allow paired devices on this Wi-Fi?" y; then
|
|
218
|
+
if curl -fsS --max-time 5 -X PUT -H "Content-Type: application/json" \
|
|
219
|
+
-d '{"lan":true}' "http://127.0.0.1:$PORT/api/devices/network" > /dev/null 2>&1; then
|
|
220
|
+
setup_ok "Devices on this Wi-Fi can connect"
|
|
221
|
+
else
|
|
222
|
+
log_warn "Couldn't turn it on: do it in Settings → Devices & access"
|
|
223
|
+
fi
|
|
224
|
+
fi
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
setup_route() {
|
|
228
|
+
setup_title "3. How other devices reach this machine"
|
|
229
|
+
local net
|
|
230
|
+
net=$(setup_api /api/devices/network) || net="{}"
|
|
231
|
+
setup_choose_via "$net"
|
|
232
|
+
case "$SETUP_VIA" in
|
|
233
|
+
connect) setup_connect "$net" ;;
|
|
234
|
+
tailscale) setup_note "AgentOS never changes Tailscale: the check below says what it needs." ;;
|
|
235
|
+
lan) setup_lan "$net" ;;
|
|
236
|
+
esac
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
setup_check() {
|
|
240
|
+
setup_title "4. Check"
|
|
241
|
+
local tries=0 code
|
|
242
|
+
while :; do
|
|
243
|
+
code=0
|
|
244
|
+
aos_tsx lib/reach/cli.ts "$PORT" --via "$SETUP_VIA" || code=$?
|
|
245
|
+
[[ "$code" -lt 2 ]] && return 0
|
|
246
|
+
tries=$((tries + 1))
|
|
247
|
+
if [[ "$tries" -ge 3 ]] || ! setup_yn "Fixed it? Check again?" y n; then
|
|
248
|
+
echo ""
|
|
249
|
+
echo " Run 'agent-os setup' again once it's fixed; finished steps are skipped."
|
|
250
|
+
return 1
|
|
251
|
+
fi
|
|
252
|
+
done
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
setup_phone() {
|
|
256
|
+
setup_title "5. Your phone"
|
|
257
|
+
if [[ "$SETUP_PAIR" == 1 ]] && setup_yn "Add your phone now?" y n; then
|
|
258
|
+
node "$SCRIPT_DIR/pair.mjs" "$PORT"
|
|
259
|
+
else
|
|
260
|
+
setup_note "Add one any time: agent-os pair"
|
|
261
|
+
fi
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
cmd_setup() {
|
|
265
|
+
while [[ $# -gt 0 ]]; do
|
|
266
|
+
case "$1" in
|
|
267
|
+
--via)
|
|
268
|
+
if [[ $# -lt 2 ]]; then
|
|
269
|
+
log_error "--via takes connect, tailscale or lan"
|
|
270
|
+
exit 2
|
|
271
|
+
fi
|
|
272
|
+
SETUP_VIA="$2"
|
|
273
|
+
shift
|
|
274
|
+
;;
|
|
275
|
+
--via=*) SETUP_VIA="${1#--via=}" ;;
|
|
276
|
+
--yes | -y) SETUP_YES=1 ;;
|
|
277
|
+
--no-service) SETUP_SERVICE=0 ;;
|
|
278
|
+
--no-pair) SETUP_PAIR=0 ;;
|
|
279
|
+
*)
|
|
280
|
+
log_error "Unknown option: $1"
|
|
281
|
+
echo "Usage: agent-os setup [--via connect|tailscale|lan] [--yes] [--no-service] [--no-pair]"
|
|
282
|
+
exit 2
|
|
283
|
+
;;
|
|
284
|
+
esac
|
|
285
|
+
shift
|
|
286
|
+
done
|
|
287
|
+
if [[ -n "$SETUP_VIA" && ! "$SETUP_VIA" =~ ^(connect|tailscale|lan)$ ]]; then
|
|
288
|
+
log_error "--via takes connect, tailscale or lan"
|
|
289
|
+
exit 2
|
|
290
|
+
fi
|
|
291
|
+
|
|
292
|
+
echo ""
|
|
293
|
+
echo -e "${BOLD}AgentOS setup${NC} (safe to run again: finished steps are skipped)"
|
|
294
|
+
setup_install
|
|
295
|
+
setup_service
|
|
296
|
+
setup_route
|
|
297
|
+
local ok=0
|
|
298
|
+
setup_check || ok=1
|
|
299
|
+
[[ "$ok" == 0 ]] && setup_phone
|
|
300
|
+
echo ""
|
|
301
|
+
echo "Check again any time: agent-os check"
|
|
302
|
+
return "$ok"
|
|
303
|
+
}
|