ework-aio 0.1.12 → 0.1.14

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 (2) hide show
  1. package/bin/install.sh +83 -5
  2. package/package.json +1 -1
package/bin/install.sh CHANGED
@@ -38,6 +38,8 @@ 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=()
42
44
  MODE_ARGS=()
43
45
 
@@ -67,6 +69,10 @@ Install options:
67
69
  --bot-name <login> Bot username (default: ework-daemon)
68
70
  --no-start Install units but don't start services
69
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
70
76
 
71
77
  Global options:
72
78
  --no-restart With `config set`: edit .env but skip the restart
@@ -107,12 +113,77 @@ while [[ $# -gt 0 ]]; do
107
113
  --no-start) NO_START=1; shift ;;
108
114
  --no-restart) NO_RESTART=1; shift ;;
109
115
  --yes|-y) ASSUME_YES=1; shift ;;
116
+ --as-user) AS_USER="$2"; shift 2 ;;
117
+ --allow-root) ALLOW_ROOT=1; shift ;;
110
118
  -h|--help) usage; exit 0 ;;
111
119
  --*) die "Unknown option: $1 (try --help)" ;;
112
120
  *) MODE_ARGS+=("$1"); shift ;;
113
121
  esac
114
122
  done
115
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
+
116
187
  # Resolve scope: default by uid
117
188
  if [[ "$SCOPENAME" == "--user" && "${EUID:-$(id -u)}" == "0" ]]; then
118
189
  SCOPENAME="--system"
@@ -376,19 +447,25 @@ EOF
376
447
  fi
377
448
  }
378
449
 
379
- # ctl wraps `systemctl $SCOPENAME` with two hardenings over the bare command:
450
+ # ctl wraps `systemctl $SCOPENAME` with three hardenings over the bare command:
380
451
  # 1. If output contains "Unit ... not found", retry once after daemon-reload
381
452
  # — fresh user sessions (no linger, post-ssh-reconnect) often have a stale
382
453
  # unit cache and this is the standard fix.
383
- # 2. If output mentions bus connection failure, print an actionable hint
384
- # instead of letting the raw "No medium found" / "Failed to connect to
385
- # bus" message through unchanged.
454
+ # 2. If output mentions any D-Bus / user-bus connection failure, print an
455
+ # actionable hint. The user-bus daemon emits several variants across
456
+ # hosts and bash versions ("Failed to connect to bus", "No medium found",
457
+ # "Failed to get D-Bus connection: Operation not permitted",
458
+ # "Call to org.freedesktop.DBus ... failed"); we match the common stem
459
+ # "bus" / "D-Bus" so a new variant can't slip past.
460
+ # 3. ALWAYS return systemctl's real exit code. Previously the trailing
461
+ # `printf` masked the failure (returning 0) when no pattern matched,
462
+ # which made every caller's `|| fallback` silently not fire.
386
463
  ctl() {
387
464
  local out rc
388
465
  out="$(systemctl "$SCOPENAME" "$@" 2>&1)"
389
466
  rc=$?
390
467
  if [[ $rc -ne 0 ]]; then
391
- if [[ "$out" == *"Failed to connect to bus"* || "$out" == *"No medium found"* ]]; then
468
+ if [[ "$out" == *([Bb]us|D-Bus|No medium found)* ]]; then
392
469
  cat >&2 <<EOF
393
470
  ${c_red}systemctl $SCOPENAME failed to reach the user bus.${c_reset}
394
471
  $out
@@ -414,6 +491,7 @@ EOF
414
491
  fi
415
492
  fi
416
493
  printf '%s\n' "$out"
494
+ return "$rc"
417
495
  }
418
496
 
419
497
  # ─── Config mode: read/change runtime .env keys ─────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ework-aio",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
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",