ework-aio 0.1.11 → 0.1.13
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/bin/install.sh +154 -10
- package/package.json +1 -1
package/bin/install.sh
CHANGED
|
@@ -38,7 +38,10 @@ BOT_NAME="ework-daemon"
|
|
|
38
38
|
NO_START=0
|
|
39
39
|
ASSUME_YES=0
|
|
40
40
|
NO_RESTART=0
|
|
41
|
+
AS_USER=""
|
|
42
|
+
ALLOW_ROOT=0
|
|
41
43
|
CFG_ARGS=()
|
|
44
|
+
MODE_ARGS=()
|
|
42
45
|
|
|
43
46
|
usage() {
|
|
44
47
|
cat <<'EOF'
|
|
@@ -66,6 +69,10 @@ Install options:
|
|
|
66
69
|
--bot-name <login> Bot username (default: ework-daemon)
|
|
67
70
|
--no-start Install units but don't start services
|
|
68
71
|
--yes Skip all prompts (use generated defaults)
|
|
72
|
+
--as-user <login> (sudo only) drop priv: re-exec install as <login>
|
|
73
|
+
after enabling linger. Data + opencode + npm
|
|
74
|
+
resolved under that user's HOME.
|
|
75
|
+
--allow-root (sudo only) override default refuse-on-root
|
|
69
76
|
|
|
70
77
|
Global options:
|
|
71
78
|
--no-restart With `config set`: edit .env but skip the restart
|
|
@@ -80,10 +87,19 @@ while [[ $# -gt 0 ]]; do
|
|
|
80
87
|
MODE="config"
|
|
81
88
|
shift
|
|
82
89
|
# Consume remaining args as config subcommand + its positionals.
|
|
90
|
+
# Global flags (--data-dir, --user, --system, etc.) are still
|
|
91
|
+
# respected here so `config set K V --data-dir X` works as expected.
|
|
83
92
|
while [[ $# -gt 0 ]]; do
|
|
84
93
|
case "$1" in
|
|
85
94
|
--no-restart) NO_RESTART=1; shift ;;
|
|
86
95
|
-h|--help) CFG_ARGS=("help"); shift ;;
|
|
96
|
+
--user) SCOPENAME="--user"; shift ;;
|
|
97
|
+
--system) SCOPENAME="--system"; shift ;;
|
|
98
|
+
--data-dir) DATA_DIR="$2"; shift 2 ;;
|
|
99
|
+
--port) WORK_PORT="$2"; shift 2 ;;
|
|
100
|
+
--daemon-port) DAEMON_PORT="$2"; shift 2 ;;
|
|
101
|
+
--bot-name) BOT_NAME="$2"; shift 2 ;;
|
|
102
|
+
--yes|-y) ASSUME_YES=1; shift ;;
|
|
87
103
|
*) CFG_ARGS+=("$1"); shift ;;
|
|
88
104
|
esac
|
|
89
105
|
done
|
|
@@ -97,11 +113,77 @@ while [[ $# -gt 0 ]]; do
|
|
|
97
113
|
--no-start) NO_START=1; shift ;;
|
|
98
114
|
--no-restart) NO_RESTART=1; shift ;;
|
|
99
115
|
--yes|-y) ASSUME_YES=1; shift ;;
|
|
116
|
+
--as-user) AS_USER="$2"; shift 2 ;;
|
|
117
|
+
--allow-root) ALLOW_ROOT=1; shift ;;
|
|
100
118
|
-h|--help) usage; exit 0 ;;
|
|
101
|
-
|
|
119
|
+
--*) die "Unknown option: $1 (try --help)" ;;
|
|
120
|
+
*) MODE_ARGS+=("$1"); shift ;;
|
|
102
121
|
esac
|
|
103
122
|
done
|
|
104
123
|
|
|
124
|
+
# ─── Root guard: refuse install as root by default ──────────────────────────
|
|
125
|
+
# Running install as root puts data under /root/.local/share/ework-aio, bakes
|
|
126
|
+
# root-only paths into systemd units, and looks for opencode in root's PATH
|
|
127
|
+
# (usually missing). The right answer is almost always: don't use sudo.
|
|
128
|
+
# --as-user re-execs the script as the named user after enabling linger.
|
|
129
|
+
# --allow-root is an explicit acknowledgement that you've set up root's
|
|
130
|
+
# environment (opencode installed, npm prefix configured, etc.).
|
|
131
|
+
if [[ "$MODE" == "install" && "${EUID:-$(id -u)}" == "0" ]]; then
|
|
132
|
+
if [[ -n "$AS_USER" ]]; then
|
|
133
|
+
AS_USER_HOME="$(getent passwd "$AS_USER" 2>/dev/null | cut -d: -f6 || true)"
|
|
134
|
+
AS_USER_UID="$(getent passwd "$AS_USER" 2>/dev/null | cut -d: -f3 || true)"
|
|
135
|
+
if [[ -z "$AS_USER_HOME" || -z "$AS_USER_UID" ]]; then
|
|
136
|
+
die "--as-user: user '$AS_USER' not found in passwd database"
|
|
137
|
+
fi
|
|
138
|
+
if [[ "$AS_USER_UID" == "0" ]]; then
|
|
139
|
+
die "--as-user: target user is root — just use --allow-root instead"
|
|
140
|
+
fi
|
|
141
|
+
log "Enable linger for '$AS_USER' so --user services survive logout..."
|
|
142
|
+
loginctl enable-linger "$AS_USER" \
|
|
143
|
+
|| die "Failed to enable linger for '$AS_USER'. Try: sudo loginctl enable-linger $AS_USER"
|
|
144
|
+
ok "Linger enabled for '$AS_USER'"
|
|
145
|
+
log "Re-execing as '$AS_USER' (HOME=$AS_USER_HOME)..."
|
|
146
|
+
# Strip --as-user <name> from argv, preserve everything else.
|
|
147
|
+
new_args=()
|
|
148
|
+
skip=0
|
|
149
|
+
for a in "$@"; do
|
|
150
|
+
if [[ "$skip" == "1" ]]; then skip=0; continue; fi
|
|
151
|
+
if [[ "$a" == "--as-user" ]]; then skip=1; continue; fi
|
|
152
|
+
new_args+=("$a")
|
|
153
|
+
done
|
|
154
|
+
# Resolve our own path (can't rely on $0 after sudo changes PATH).
|
|
155
|
+
SELF="$(command -v ework-aio 2>/dev/null || true)"
|
|
156
|
+
[[ -n "$SELF" ]] || SELF="${BASH_SOURCE[0]:-$0}"
|
|
157
|
+
exec sudo -u "$AS_USER" --login -- "$SELF" "${new_args[@]}"
|
|
158
|
+
fi
|
|
159
|
+
if [[ "$ALLOW_ROOT" != "1" ]]; then
|
|
160
|
+
cat >&2 <<EOF
|
|
161
|
+
${c_red}ework-aio install refuses to run as root by default.${c_reset}
|
|
162
|
+
|
|
163
|
+
Why this matters:
|
|
164
|
+
- Data goes under /root/.local/share/ework-aio (unreadable by other users)
|
|
165
|
+
- opencode is searched in root's PATH (usually not installed there)
|
|
166
|
+
- npm packages install to system-wide prefix owned by root
|
|
167
|
+
- systemd units bake root-only paths that other users can't run
|
|
168
|
+
|
|
169
|
+
${c_bold}Option A (recommended):${c_reset} run as a regular user.
|
|
170
|
+
npm config set prefix '~/.local' # one-time, makes -g user-writable
|
|
171
|
+
npm install -g ework-aio # no sudo needed
|
|
172
|
+
ework-aio install # uses --user systemd scope
|
|
173
|
+
|
|
174
|
+
${c_bold}Option B:${c_reset} install with sudo but target a regular user.
|
|
175
|
+
sudo ework-aio install --as-user $(logname 2>/dev/null || echo '<your-user>')
|
|
176
|
+
# All data + opencode + npm resolved under that user's HOME.
|
|
177
|
+
# systemd --user scope (linger auto-enabled for the target user).
|
|
178
|
+
|
|
179
|
+
${c_bold}Option C:${c_reset} really install as root (you've set up root's env).
|
|
180
|
+
sudo ework-aio install --allow-root
|
|
181
|
+
EOF
|
|
182
|
+
exit 1
|
|
183
|
+
fi
|
|
184
|
+
warn "Running install as root with --allow-root — data will live under /root."
|
|
185
|
+
fi
|
|
186
|
+
|
|
105
187
|
# Resolve scope: default by uid
|
|
106
188
|
if [[ "$SCOPENAME" == "--user" && "${EUID:-$(id -u)}" == "0" ]]; then
|
|
107
189
|
SCOPENAME="--system"
|
|
@@ -144,12 +226,13 @@ ensure_user_session || true # warn but don't hard-fail — let later steps rep
|
|
|
144
226
|
need_cmd() {
|
|
145
227
|
command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1. $2"
|
|
146
228
|
}
|
|
147
|
-
# Commands every mode needs (status/logs/env/config/uninstall all use systemctl).
|
|
148
|
-
need_cmd systemctl "This installer requires systemd."
|
|
149
229
|
need_cmd awk "Should be present on any Linux."
|
|
150
230
|
|
|
151
|
-
#
|
|
231
|
+
# install needs systemctl (to write unit files + reload). Other modes
|
|
232
|
+
# (status/logs/env/config/uninstall) can fall back to PID-file mode via
|
|
233
|
+
# the 'ework-aio' dispatcher if systemd is unreachable on this host.
|
|
152
234
|
if [[ "$MODE" == "install" ]]; then
|
|
235
|
+
need_cmd systemctl "Install requires systemd. For hosts without systemd, run 'npm install -g ework-aio' and use 'ework-aio start' directly (PID-file mode)."
|
|
153
236
|
need_cmd bun "Install from https://bun.sh"
|
|
154
237
|
need_cmd npm "Install Node.js or Bun (ships npm)"
|
|
155
238
|
need_cmd opencode "Install from https://opencode.ai"
|
|
@@ -158,6 +241,23 @@ if [[ "$MODE" == "install" ]]; then
|
|
|
158
241
|
need_cmd jq "Install jq (for opencode.json merge)."
|
|
159
242
|
fi
|
|
160
243
|
|
|
244
|
+
# EWORK_AIO_BIN: always resolvable (used by status/logs/config/uninstall
|
|
245
|
+
# to delegate to PID-file mode operations when systemd is unreachable).
|
|
246
|
+
EWORK_AIO_BIN="$(command -v ework-aio 2>/dev/null || true)"
|
|
247
|
+
[[ -n "$EWORK_AIO_BIN" ]] || EWORK_AIO_BIN="$(npm root -g 2>/dev/null)/ework-aio/bin/ework-aio"
|
|
248
|
+
|
|
249
|
+
# systemd_reachable: 0 if `systemctl $SCOPENAME` can talk to systemd, 1 if not.
|
|
250
|
+
# Used by status/logs/config/uninstall to decide whether to use systemd
|
|
251
|
+
# (preferred) or fall back to PID-file mode via the ework-aio dispatcher.
|
|
252
|
+
# Hosts where this returns 1: containers without systemd PID 1, polkit
|
|
253
|
+
# redirects under sudo that drop the bus, hosts where the user isn't
|
|
254
|
+
# permissioned for the system bus.
|
|
255
|
+
systemd_reachable() {
|
|
256
|
+
command -v systemctl >/dev/null 2>&1 || return 1
|
|
257
|
+
systemctl "$SCOPENAME" is-system-running >/dev/null 2>&1 \
|
|
258
|
+
|| systemctl "$SCOPENAME" list-units --no-pager >/dev/null 2>&1
|
|
259
|
+
}
|
|
260
|
+
|
|
161
261
|
# Verify the 3 npm packages are reachable. If not, try to install them now.
|
|
162
262
|
ensure_pkg() {
|
|
163
263
|
local pkg="$1"
|
|
@@ -469,6 +569,19 @@ service_for_key() {
|
|
|
469
569
|
restart_service() {
|
|
470
570
|
local svc="$1"
|
|
471
571
|
local rc=0
|
|
572
|
+
if ! systemd_reachable; then
|
|
573
|
+
log "systemd not reachable — using PID-file mode restart"
|
|
574
|
+
if [[ "$svc" == "both" ]]; then
|
|
575
|
+
"$EWORK_AIO_BIN" restart both && ok "Restarted ework-web + ework-daemon (PID-file mode)" || rc=$?
|
|
576
|
+
else
|
|
577
|
+
"$EWORK_AIO_BIN" restart "$svc" && ok "Restarted ework-$svc (PID-file mode)" || rc=$?
|
|
578
|
+
fi
|
|
579
|
+
if [[ $rc -ne 0 ]]; then
|
|
580
|
+
warn "PID-file restart of '$svc' failed (exit $rc)."
|
|
581
|
+
warn "The .env changes are saved; run 'ework-aio restart $svc' manually."
|
|
582
|
+
fi
|
|
583
|
+
return $rc
|
|
584
|
+
fi
|
|
472
585
|
case "$svc" in
|
|
473
586
|
web) ctl restart ework-web.service && ok "Restarted ework-web" || rc=$? ;;
|
|
474
587
|
daemon) ctl restart ework-daemon.service && ok "Restarted ework-daemon" || rc=$? ;;
|
|
@@ -622,7 +735,22 @@ config)
|
|
|
622
735
|
;;
|
|
623
736
|
|
|
624
737
|
status)
|
|
625
|
-
hr; log "ework-aio status
|
|
738
|
+
hr; log "ework-aio status"; hr
|
|
739
|
+
if ! systemd_reachable; then
|
|
740
|
+
log "(systemd unreachable — showing PID-file mode status)"
|
|
741
|
+
"$EWORK_AIO_BIN" ps 2>/dev/null || warn "ework-aio ps failed"
|
|
742
|
+
hr
|
|
743
|
+
log "Port listeners:"
|
|
744
|
+
p=""
|
|
745
|
+
for p in $(grep -hE '^(WORK_PORT|DAEMON_PORT)=' "$WEB_ENV" "$DAEMON_ENV" 2>/dev/null | cut -d= -f2 | sort -u); do
|
|
746
|
+
if curl -sf -o /dev/null --max-time 1 "http://127.0.0.1:$p/login" 2>/dev/null; then
|
|
747
|
+
printf ' :%s ✓ listening\n' "$p"
|
|
748
|
+
else
|
|
749
|
+
printf ' :%s ✗ not responding\n' "$p"
|
|
750
|
+
fi
|
|
751
|
+
done
|
|
752
|
+
exit 0
|
|
753
|
+
fi
|
|
626
754
|
ctl is-active ework-web.service || true
|
|
627
755
|
ctl is-active ework-daemon.service || true
|
|
628
756
|
ctl status --no-pager --lines=0 ework-web.service 2>/dev/null || true
|
|
@@ -632,18 +760,34 @@ status)
|
|
|
632
760
|
;;
|
|
633
761
|
|
|
634
762
|
logs)
|
|
635
|
-
svc="${
|
|
763
|
+
svc="${MODE_ARGS[0]:-ework-web.service}"
|
|
636
764
|
[[ "$svc" == "daemon" || "$svc" == "ework-daemon" ]] && svc="ework-daemon.service"
|
|
637
765
|
[[ "$svc" == "web" || "$svc" == "ework-web" ]] && svc="ework-web.service"
|
|
766
|
+
if ! systemd_reachable; then
|
|
767
|
+
svc_short="${svc%.service}"
|
|
768
|
+
svc_short="${svc_short#ework-}"
|
|
769
|
+
log_file="$DATA_DIR/run/${svc_short}.log"
|
|
770
|
+
if [[ ! -f "$log_file" ]]; then
|
|
771
|
+
die "systemd unreachable and no PID-file log at $log_file. Start services with: ework-aio start $svc_short"
|
|
772
|
+
fi
|
|
773
|
+
log "(systemd unreachable — tailing PID-file log $log_file)"
|
|
774
|
+
exec tail -n 200 -f "$log_file"
|
|
775
|
+
fi
|
|
638
776
|
exec journalctl "$SCOPENAME" -u "$svc" -f
|
|
639
777
|
;;
|
|
640
778
|
|
|
641
779
|
uninstall)
|
|
642
780
|
hr; log "Uninstalling ework-aio services (keeping data)"; hr
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
781
|
+
"$EWORK_AIO_BIN" stop both 2>/dev/null || true
|
|
782
|
+
if systemd_reachable; then
|
|
783
|
+
ctl stop ework-web.service ework-daemon.service 2>/dev/null || true
|
|
784
|
+
ctl disable ework-web.service ework-daemon.service 2>/dev/null || true
|
|
785
|
+
rm -f "$UNIT_DIR/ework-web.service" "$UNIT_DIR/ework-daemon.service"
|
|
786
|
+
ctl daemon-reload 2>/dev/null || true
|
|
787
|
+
else
|
|
788
|
+
log "(systemd unreachable — skipped unit cleanup; PID-file mode services stopped above)"
|
|
789
|
+
rm -f "$UNIT_DIR/ework-web.service" "$UNIT_DIR/ework-daemon.service" 2>/dev/null || true
|
|
790
|
+
fi
|
|
647
791
|
ok "Services removed. Data preserved at $DATA_DIR"
|
|
648
792
|
warn "To fully remove: rm -rf $DATA_DIR && npm uninstall -g ework-aio ework-web ework-daemon opencode-ework"
|
|
649
793
|
exit 0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ework-aio",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "All-in-one installer for ework (issue tracker) + ework-daemon (AI bridge) + opencode-ework (plugin). One command: npm i -g ework-aio && ework-aio install.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|