@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.
Files changed (47) hide show
  1. package/README.md +3 -2
  2. package/app/api/reach/probe/route.test.ts +22 -0
  3. package/app/api/reach/probe/route.ts +12 -0
  4. package/app/api/reach/route.ts +30 -0
  5. package/app/api/sessions/list-size.test.ts +77 -0
  6. package/app/api/sessions/route.ts +21 -10
  7. package/app/api/tasks/list-size.test.ts +50 -0
  8. package/app/api/tasks/route.ts +20 -3
  9. package/components/Chat/ViewSwitch.tsx +2 -1
  10. package/components/Devices/NetworkSection.tsx +2 -0
  11. package/components/Pane/index.tsx +4 -2
  12. package/components/Reach/ReachRow.tsx +137 -0
  13. package/components/Settings/SetupSection.tsx +2 -0
  14. package/components/Welcome/ConnectBlock.tsx +2 -0
  15. package/content/tips.json +10 -0
  16. package/content/whats-new.json +32 -0
  17. package/data/devices/queries.ts +6 -1
  18. package/data/reach/index.ts +33 -0
  19. package/lib/connect/serve.ts +22 -3
  20. package/lib/connect/supervise.ts +33 -9
  21. package/lib/db/queries.ts +31 -4
  22. package/lib/db/types.ts +3 -1
  23. package/lib/guides.ts +6 -0
  24. package/lib/hosts/remote-menu.ts +4 -2
  25. package/lib/reach/check-cli.ts +117 -0
  26. package/lib/reach/checkReach.ts +62 -0
  27. package/lib/reach/classify.ts +283 -0
  28. package/lib/reach/cli.ts +38 -0
  29. package/lib/reach/gatherFacts.ts +167 -0
  30. package/lib/reach/index.ts +4 -0
  31. package/lib/reach/probeConnect.ts +122 -0
  32. package/lib/reach/types.ts +96 -0
  33. package/lib/security/auth.ts +2 -0
  34. package/lib/security/demo.ts +2 -0
  35. package/lib/sessions/has-prompt.ts +7 -0
  36. package/lib/sessions/omit-null.ts +8 -0
  37. package/lib/tasks/contract/views.ts +12 -0
  38. package/lib/tasks/index.ts +9 -5
  39. package/lib/tasks/session.ts +15 -1
  40. package/lib/tips/view.ts +2 -4
  41. package/package.json +1 -1
  42. package/scripts/agent-os +3 -0
  43. package/scripts/install.sh +12 -1
  44. package/scripts/lib/commands.sh +59 -65
  45. package/scripts/lib/service.sh +230 -0
  46. package/scripts/lib/setup.sh +303 -0
  47. 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/&/\&amp;/g' -e 's/</\&lt;/g' -e 's/>/\&gt;/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
+ }