ework-aio 0.1.9 → 0.1.11
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/ework-aio +5 -4
- package/bin/install.sh +148 -24
- package/package.json +1 -1
package/bin/ework-aio
CHANGED
|
@@ -253,10 +253,11 @@ function startOne(svc: SvcName, foreground: boolean): boolean {
|
|
|
253
253
|
}
|
|
254
254
|
const envPath = svcEnvPath(svc);
|
|
255
255
|
if (!existsSync(envPath)) {
|
|
256
|
-
console.error(
|
|
257
|
-
|
|
258
|
-
);
|
|
259
|
-
console.error("
|
|
256
|
+
console.error(`${SVC_CONFIG[svc].label}: missing .env at ${envPath}.`);
|
|
257
|
+
console.error(" Run `ework-aio install` first to scaffold config + data dir.");
|
|
258
|
+
console.error(" If install fails with npm ENOTEMPTY, a stale temp dir is blocking it.");
|
|
259
|
+
console.error(" Manual fix: sudo rm -rf \"$(npm root -g)/.ework-*\" \"$(npm root -g)\"/*.Trash*");
|
|
260
|
+
console.error(" Then re-run: ework-aio install");
|
|
260
261
|
return false;
|
|
261
262
|
}
|
|
262
263
|
|
package/bin/install.sh
CHANGED
|
@@ -12,6 +12,13 @@
|
|
|
12
12
|
|
|
13
13
|
set -euo pipefail
|
|
14
14
|
|
|
15
|
+
# ERR trap: when `set -e` kills the script, print the line + last command so
|
|
16
|
+
# the user sees why instead of an empty prompt. Tricky failure modes (root
|
|
17
|
+
# scope + polkit redirect, missing systemd PID 1, journalctl-as-root) all
|
|
18
|
+
# manifest as silent exit at a systemctl call — without this trace, the user
|
|
19
|
+
# just sees the prompt return and assumes install succeeded.
|
|
20
|
+
trap 'rc=$?; if [[ $rc -ne 0 ]]; then echo "${c_red}✗${c_reset} install.sh exited with code $rc at line $LINENO (most recent command: ${BASH_COMMAND})" >&2; echo " If systemd is unavailable on this host, retry with: ework-aio install --no-start" >&2; echo " Then start services with: ework-aio start" >&2; fi' ERR
|
|
21
|
+
|
|
15
22
|
# ─── Pretty output ──────────────────────────────────────────────────────────
|
|
16
23
|
c_reset=$'\033[0m'; c_bold=$'\033[1m'; c_dim=$'\033[2m'
|
|
17
24
|
c_red=$'\033[31m'; c_grn=$'\033[32m'; c_ylw=$'\033[33m'; c_blu=$'\033[34m'
|
|
@@ -162,12 +169,59 @@ ensure_pkg() {
|
|
|
162
169
|
}
|
|
163
170
|
case "$MODE" in
|
|
164
171
|
install)
|
|
172
|
+
# Pre-clean stale npm rename temp-dirs from any previous failed install.
|
|
173
|
+
# npm's atomic-install pattern renames the existing package dir to a
|
|
174
|
+
# `.<pkg>-<rand>` sibling before extracting the new tarball; if a prior
|
|
175
|
+
# install crashed mid-flight, that temp dir stays behind and the NEXT
|
|
176
|
+
# install fails with ENOTEMPTY on the rename. Symptoms:
|
|
177
|
+
# npm error code ENOTEMPTY
|
|
178
|
+
# npm error syscall rename
|
|
179
|
+
# npm error path .../node_modules/ework-web
|
|
180
|
+
# npm error dest .../node_modules/.ework-web-Cx2Tkt83
|
|
181
|
+
clean_npm_stale_dirs() {
|
|
182
|
+
local npm_root
|
|
183
|
+
npm_root="$(npm root -g 2>/dev/null)" || return 0
|
|
184
|
+
[[ -d "$npm_root" ]] || return 0
|
|
185
|
+
# Stale temp patterns: .ework-web-XXXX, .ework-daemon-XXXX, .opencode-ework-XXXX,
|
|
186
|
+
# .ework-aio-XXXX, plus the .Trash suffix some npm versions use.
|
|
187
|
+
local found=()
|
|
188
|
+
while IFS= read -r p; do
|
|
189
|
+
[[ -n "$p" ]] && found+=("$p")
|
|
190
|
+
done < <(find "$npm_root" -maxdepth 1 \( \
|
|
191
|
+
-name '.ework-web-*' -o \
|
|
192
|
+
-name '.ework-daemon-*' -o \
|
|
193
|
+
-name '.ework-aio-*' -o \
|
|
194
|
+
-name '.opencode-ework-*' -o \
|
|
195
|
+
-name 'ework-*.Trash*' -o \
|
|
196
|
+
-name 'opencode-ework.Trash*' \) 2>/dev/null || true)
|
|
197
|
+
if [[ ${#found[@]} -gt 0 ]]; then
|
|
198
|
+
log "Found ${#found[@]} stale npm temp dir(s) under $npm_root from a previous failed install:"
|
|
199
|
+
printf ' %s\n' "${found[@]}"
|
|
200
|
+
log "Removing..."
|
|
201
|
+
local d
|
|
202
|
+
for d in "${found[@]}"; do
|
|
203
|
+
rm -rf "$d" 2>/dev/null || warn "could not remove $d (try: sudo rm -rf $d)"
|
|
204
|
+
done
|
|
205
|
+
fi
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
npm_install_global() {
|
|
209
|
+
# One retry after a cleanup pass — covers the ENOTEMPTY case where npm
|
|
210
|
+
# itself produced a fresh temp dir during this same failed run.
|
|
211
|
+
if ! npm install -g "$1@latest"; then
|
|
212
|
+
warn "npm install -g $1@latest failed; cleaning stale temp dirs and retrying once..."
|
|
213
|
+
clean_npm_stale_dirs
|
|
214
|
+
npm install -g "$1@latest"
|
|
215
|
+
fi
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
clean_npm_stale_dirs
|
|
165
219
|
log "Installing/updating npm packages globally..."
|
|
166
220
|
# Install each as a top-level global so bins are linked to PATH.
|
|
167
221
|
# (npm v9 doesn't hoist nested deps' bins from `npm i -g ework-aio`.)
|
|
168
222
|
# Always run `npm install -g` (not gated on presence) so re-runs pick up new versions.
|
|
169
223
|
for pkg in ework-web ework-daemon opencode-ework ework-aio; do
|
|
170
|
-
|
|
224
|
+
npm_install_global "$pkg" || die "npm install -g $pkg@latest failed (even after cleanup retry; manual fix: sudo rm -rf $(npm root -g)/.$pkg-* $(npm root -g)/$pkg.Trash*)"
|
|
171
225
|
done
|
|
172
226
|
ok "npm packages ready"
|
|
173
227
|
|
|
@@ -634,22 +688,59 @@ write_unit_file \
|
|
|
634
688
|
ok "Wrote $WEB_UNIT"
|
|
635
689
|
|
|
636
690
|
# ─── Reload + start ework-web ──────────────────────────────────────────────
|
|
637
|
-
|
|
638
|
-
|
|
691
|
+
# systemd calls here are best-effort. If systemd isn't functional on this host
|
|
692
|
+
# (containers without systemd PID 1, polkit redirects under sudo, missing
|
|
693
|
+
# /etc/systemd/system) we still want install to complete so the user can run
|
|
694
|
+
# `ework-aio start` (PID-file mode) against the scaffolded .env.
|
|
695
|
+
SYSTEMD_OK=1
|
|
696
|
+
if ! ctl daemon-reload; then
|
|
697
|
+
warn "systemctl daemon-reload failed — systemd may be unavailable on this host."
|
|
698
|
+
SYSTEMD_OK=0
|
|
699
|
+
fi
|
|
700
|
+
|
|
701
|
+
if [[ "$NO_START" == "0" && "$SYSTEMD_OK" == "1" ]]; then
|
|
639
702
|
log "Starting ework-web..."
|
|
640
|
-
ctl enable ework-web.service
|
|
641
|
-
|
|
703
|
+
ctl enable ework-web.service || { warn "systemctl enable failed"; SYSTEMD_OK=0; }
|
|
704
|
+
if [[ "$SYSTEMD_OK" == "1" ]]; then
|
|
705
|
+
ctl restart ework-web.service || { warn "systemctl restart failed"; SYSTEMD_OK=0; }
|
|
706
|
+
fi
|
|
707
|
+
if [[ "$SYSTEMD_OK" == "1" ]]; then
|
|
708
|
+
for i in $(seq 1 60); do
|
|
709
|
+
if curl -sf -o /dev/null "http://127.0.0.1:$WORK_PORT/login"; then
|
|
710
|
+
ok "ework-web listening on :$WORK_PORT (after ${i} half-seconds)"
|
|
711
|
+
break
|
|
712
|
+
fi
|
|
713
|
+
sleep 0.5
|
|
714
|
+
[[ $i -eq 60 ]] && { warn "ework-web did not come up via systemd in 30s. Falling back to PID-file mode."; SYSTEMD_OK=0; }
|
|
715
|
+
done
|
|
716
|
+
fi
|
|
717
|
+
else
|
|
718
|
+
warn "--no-start: unit enabled but not started"
|
|
719
|
+
ctl enable ework-web.service 2>/dev/null || true
|
|
720
|
+
fi
|
|
721
|
+
|
|
722
|
+
# ─── PID-file mode fallback for bot bootstrap ──────────────────────────────
|
|
723
|
+
# If systemd couldn't start web (or we're on a host without systemd), bring
|
|
724
|
+
# web up briefly via the PID-file path so the bot user + PAT bootstrap can
|
|
725
|
+
# still complete. We leave web running in PID-file mode so subsequent curl
|
|
726
|
+
# calls to :WORK_PORT succeed.
|
|
727
|
+
if [[ "$NO_START" == "0" && "$SYSTEMD_OK" == "0" ]]; then
|
|
728
|
+
log "Starting ework-web via PID-file mode (nohup) for bot bootstrap..."
|
|
729
|
+
WEB_LOG_FILE="$DATA_DIR/run/web.log"
|
|
730
|
+
mkdir -p "$(dirname "$WEB_LOG_FILE")"
|
|
731
|
+
setsid nohup "$EWORK_WEB_BIN" >>"$WEB_LOG_FILE" 2>&1 </dev/null &
|
|
732
|
+
WEB_PID=$!
|
|
733
|
+
disown "$WEB_PID" 2>/dev/null || true
|
|
734
|
+
printf '%s\n' "$WEB_PID" > "$DATA_DIR/run/web.pid"
|
|
735
|
+
ok "ework-web started in PID-file mode (pid $WEB_PID, log $WEB_LOG_FILE)"
|
|
642
736
|
for i in $(seq 1 60); do
|
|
643
737
|
if curl -sf -o /dev/null "http://127.0.0.1:$WORK_PORT/login"; then
|
|
644
|
-
ok "ework-web listening on :$WORK_PORT (after ${i} half-seconds)"
|
|
738
|
+
ok "ework-web listening on :$WORK_PORT (after ${i} half-seconds, PID-file mode)"
|
|
645
739
|
break
|
|
646
740
|
fi
|
|
647
741
|
sleep 0.5
|
|
648
|
-
[[ $i -eq 60 ]] && die "ework-web did not come up in 30s.
|
|
742
|
+
[[ $i -eq 60 ]] && die "ework-web did not come up in 30s. Tail of $WEB_LOG_FILE: $(tail -n 20 "$WEB_LOG_FILE" 2>/dev/null)"
|
|
649
743
|
done
|
|
650
|
-
else
|
|
651
|
-
warn "--no-start: unit enabled but not started"
|
|
652
|
-
ctl enable ework-web.service
|
|
653
744
|
fi
|
|
654
745
|
|
|
655
746
|
# ─── Bootstrap bot user + PAT (idempotent) ─────────────────────────────────
|
|
@@ -662,10 +753,23 @@ COOKIE_SIG=$(printf '%s' "$WORK_TOKEN_VAL" \
|
|
|
662
753
|
AUTH_COOKIE="ework_auth=${WORK_TOKEN_VAL}.${COOKIE_SIG}"
|
|
663
754
|
|
|
664
755
|
BOT_TOKEN=""
|
|
756
|
+
BOT_BOOTSTRAP_OK=1
|
|
665
757
|
if [[ -f "$BOT_TOKEN_FILE" ]]; then
|
|
666
758
|
BOT_TOKEN=$(cat "$BOT_TOKEN_FILE")
|
|
667
759
|
ok "Reusing saved bot token from $BOT_TOKEN_FILE"
|
|
668
760
|
else
|
|
761
|
+
# Pre-flight: web must be reachable for the bot bootstrap HTTP calls below.
|
|
762
|
+
# In --no-start mode OR on hosts where systemd couldn't bring web up, skip
|
|
763
|
+
# the bootstrap entirely — daemon .env still gets written (with empty token)
|
|
764
|
+
# so the user can `ework-aio start` and re-run install later.
|
|
765
|
+
if ! curl -sf -o /dev/null "http://127.0.0.1:$WORK_PORT/login"; then
|
|
766
|
+
warn "ework-web not reachable at :$WORK_PORT — skipping bot bootstrap."
|
|
767
|
+
warn "Daemon will not be able to talk to web until you re-run install with web running."
|
|
768
|
+
BOT_BOOTSTRAP_OK=0
|
|
769
|
+
fi
|
|
770
|
+
fi
|
|
771
|
+
|
|
772
|
+
if [[ "$BOT_BOOTSTRAP_OK" == "1" && ! -f "$BOT_TOKEN_FILE" ]]; then
|
|
669
773
|
log "Bootstrapping bot user '$BOT_NAME'..."
|
|
670
774
|
BOT_PW=$(openssl rand -hex 24)
|
|
671
775
|
CREATE_CODE=$(curl -sS -o /dev/null -w '%{http_code}' -X POST \
|
|
@@ -678,9 +782,11 @@ else
|
|
|
678
782
|
case "$CREATE_CODE" in
|
|
679
783
|
303) ok "Bot user '$BOT_NAME' created" ;;
|
|
680
784
|
400|409) warn "Bot user '$BOT_NAME' already exists (continuing)" ;;
|
|
681
|
-
*)
|
|
785
|
+
*) warn "Failed to create bot user: HTTP $CREATE_CODE — skipping PAT mint; daemon will not be able to talk to web until re-run"; BOT_BOOTSTRAP_OK=0 ;;
|
|
682
786
|
esac
|
|
787
|
+
fi
|
|
683
788
|
|
|
789
|
+
if [[ "$BOT_BOOTSTRAP_OK" == "1" && ! -f "$BOT_TOKEN_FILE" ]]; then
|
|
684
790
|
log "Logging in as bot to mint PAT..."
|
|
685
791
|
COOKIE_JAR=$(mktemp)
|
|
686
792
|
LOGIN_CODE=$(curl -sS -c "$COOKIE_JAR" -X POST "http://127.0.0.1:$WORK_PORT/login" \
|
|
@@ -689,18 +795,25 @@ else
|
|
|
689
795
|
-o /dev/null -w '%{http_code}') || LOGIN_CODE=000
|
|
690
796
|
BOT_COOKIE=$(awk '/ework_auth/ {print $7}' "$COOKIE_JAR")
|
|
691
797
|
rm -f "$COOKIE_JAR"
|
|
692
|
-
[[ "$LOGIN_CODE"
|
|
693
|
-
|
|
798
|
+
if [[ "$LOGIN_CODE" != "302" || -z "$BOT_COOKIE" ]]; then
|
|
799
|
+
warn "Bot login failed: HTTP $LOGIN_CODE — skipping PAT mint"
|
|
800
|
+
BOT_BOOTSTRAP_OK=0
|
|
801
|
+
fi
|
|
802
|
+
fi
|
|
694
803
|
|
|
804
|
+
if [[ "$BOT_BOOTSTRAP_OK" == "1" && ! -f "$BOT_TOKEN_FILE" ]]; then
|
|
695
805
|
log "Minting PAT..."
|
|
696
806
|
PAT_RES=$(curl -sS -X POST "http://127.0.0.1:$WORK_PORT/me/tokens/create" \
|
|
697
807
|
-H "Cookie: ework_auth=$BOT_COOKIE" \
|
|
698
808
|
--data-urlencode "name=aio-$(date +%s)")
|
|
699
809
|
BOT_TOKEN=$(printf '%s' "$PAT_RES" | grep -oE 'id="t">[a-f0-9]{40}<' | grep -oE '[a-f0-9]{40}' | head -1 || true)
|
|
700
|
-
[[ -
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
810
|
+
if [[ -z "$BOT_TOKEN" ]]; then
|
|
811
|
+
warn "Could not extract PAT from token-create response — daemon .env will have empty token"
|
|
812
|
+
else
|
|
813
|
+
printf '%s' "$BOT_TOKEN" > "$BOT_TOKEN_FILE"
|
|
814
|
+
chmod 600 "$BOT_TOKEN_FILE"
|
|
815
|
+
ok "Bot PAT saved to $BOT_TOKEN_FILE"
|
|
816
|
+
fi
|
|
704
817
|
fi
|
|
705
818
|
|
|
706
819
|
# ─── Write ework-daemon .env ───────────────────────────────────────────────
|
|
@@ -719,19 +832,30 @@ write_unit_file \
|
|
|
719
832
|
# (trailing " \"\"" is a no-op separator; kept for future extra env injection)
|
|
720
833
|
ok "Wrote $DAEMON_UNIT"
|
|
721
834
|
|
|
722
|
-
ctl daemon-reload
|
|
723
|
-
if [[ "$NO_START" == "0" ]]; then
|
|
835
|
+
ctl daemon-reload 2>/dev/null || true
|
|
836
|
+
if [[ "$NO_START" == "0" && "$SYSTEMD_OK" == "1" ]]; then
|
|
724
837
|
log "Starting ework-daemon..."
|
|
725
|
-
ctl enable ework-daemon.service
|
|
726
|
-
ctl restart ework-daemon.service
|
|
838
|
+
ctl enable ework-daemon.service 2>/dev/null || true
|
|
839
|
+
ctl restart ework-daemon.service 2>/dev/null || warn "ework-daemon restart via systemd failed; use 'ework-aio start daemon' for PID-file mode"
|
|
727
840
|
sleep 1
|
|
728
|
-
if ctl is-active --quiet ework-daemon.service; then
|
|
841
|
+
if ctl is-active --quiet ework-daemon.service 2>/dev/null; then
|
|
729
842
|
ok "ework-daemon active"
|
|
730
843
|
else
|
|
731
|
-
warn "ework-daemon did not report active;
|
|
844
|
+
warn "ework-daemon did not report active; run 'ework-aio start daemon' for PID-file mode"
|
|
732
845
|
fi
|
|
733
846
|
else
|
|
734
|
-
ctl enable ework-daemon.service
|
|
847
|
+
ctl enable ework-daemon.service 2>/dev/null || true
|
|
848
|
+
fi
|
|
849
|
+
|
|
850
|
+
if [[ "$SYSTEMD_OK" == "0" ]]; then
|
|
851
|
+
hr
|
|
852
|
+
warn "systemd mode did not come up cleanly on this host."
|
|
853
|
+
warn "Services are scaffolded but not auto-started under systemd."
|
|
854
|
+
warn "Use PID-file mode instead:"
|
|
855
|
+
echo " ework-aio start # start web + daemon in background"
|
|
856
|
+
echo " ework-aio ps # check status"
|
|
857
|
+
echo " ework-aio logs web # tail web log"
|
|
858
|
+
hr
|
|
735
859
|
fi
|
|
736
860
|
|
|
737
861
|
# ─── Register opencode-ework plugin ────────────────────────────────────────
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ework-aio",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
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",
|