@jspg-ai/coding-bb 0.0.1 → 0.0.2-beta.23
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/cbb/bin/cbb.js +6 -0
- package/cbb/bin/cbbm.js +6 -0
- package/cbb/dev-standards/rules/cbb-ai-behavior.md +103 -103
- package/cbb/dev-standards/rules/cbb-priority.md +55 -55
- package/cbb/lib/install/claude-code.js +2 -0
- package/cbb/lib/install/init.js +496 -290
- package/cbb/lib/install/opencode.js +31 -0
- package/cbb/lib/install/qoder.js +2 -0
- package/cbb/lib/install/workspaces.js +53 -13
- package/cbb/lib/openspec/index.js +3 -3
- package/cbb/lib/superpowers/index.js +1 -1
- package/cbb/lib/utils/check-update.js +16 -14
- package/cbb/lib/utils/gitignore.js +2 -1
- package/cbb/lib/utils/output.js +92 -4
- package/cbb/lib/utils/version.js +2 -31
- package/cbb/lib/wiki/cli.js +25 -25
- package/cbb/lib/wiki/index.js +16 -16
- package/cbb/tools/cbb-design-to-wiki/SKILL.md +14 -14
- package/cbb/tools/cbb-wiki-ops/SKILL.md +22 -22
- package/cbb/worktrees/_shared/scripts/find-target-worktree.js +2 -2
- package/cbb/worktrees/_shared/scripts/find-workspace-root.js +7 -7
- package/cbb/worktrees/_shared/scripts/push-core.js +18 -0
- package/cbb/worktrees/commands/{close.md → worktree-close.md} +8 -8
- package/cbb/worktrees/commands/{init.md → worktree-init.md} +9 -9
- package/cbb/worktrees/commands/{push.md → worktree-push.md} +3 -3
- package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/SKILL.md +20 -20
- package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/scripts/check-env.js +4 -4
- package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/scripts/check-unarchived.js +1 -1
- package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/scripts/delete-branches.js +3 -3
- package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/scripts/discover-apps.js +6 -6
- package/cbb/worktrees/skills/{openspec-extend-worktree → cbb-worktree-close}/scripts/find-target-worktree.js +2 -2
- package/cbb/worktrees/skills/{openspec-push-worktrees → cbb-worktree-close}/scripts/find-workspace-root.js +7 -7
- package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/scripts/remove-worktrees.js +4 -4
- package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/scripts/safety-check.js +4 -4
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/SKILL.md +81 -107
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/auto-open.js +1 -1
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/check-env-deep.js +4 -4
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/check-env.js +6 -6
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/check-repos.js +4 -4
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/create-branches.js +4 -4
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/create-worktrees.js +8 -7
- package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-init}/scripts/find-workspace-root.js +7 -7
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/install-ai.js +33 -33
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/parse-config.js +7 -7
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/push-branches.js +9 -7
- package/cbb/worktrees/skills/{openspec-extend-worktree → cbb-worktree-init}/scripts/push-core.js +18 -0
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/sync-repos.js +10 -12
- package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-init}/scripts/update-gitignore.js +19 -6
- package/cbb/worktrees/skills/{openspec-push-worktrees → cbb-worktree-push}/SKILL.md +16 -16
- package/cbb/worktrees/skills/{openspec-push-worktrees → cbb-worktree-push}/scripts/commit-worktrees.js +2 -2
- package/cbb/worktrees/skills/{openspec-push-worktrees → cbb-worktree-push}/scripts/find-target-worktree.js +2 -2
- package/cbb/worktrees/skills/{openspec-extend-worktree → cbb-worktree-push}/scripts/find-workspace-root.js +7 -7
- package/cbb/worktrees/skills/{openspec-push-worktrees → cbb-worktree-push}/scripts/push-core.js +18 -0
- package/cbb/worktrees/skills/{openspec-push-worktrees → cbb-worktree-push}/scripts/push-worktrees.js +7 -5
- package/config/workspace-config.sample.json +18 -0
- package/config/workspaces.json +11 -6
- package/openspec/.version +6 -6
- package/openspec/commands/apply.md +7 -7
- package/openspec/commands/archive.md +7 -7
- package/openspec/commands/bulk-archive.md +7 -7
- package/openspec/commands/continue.md +7 -7
- package/openspec/commands/explore.md +7 -7
- package/openspec/commands/ff.md +7 -7
- package/openspec/commands/new.md +7 -7
- package/openspec/commands/onboard.md +7 -7
- package/openspec/commands/propose.md +7 -7
- package/openspec/commands/sync.md +7 -7
- package/openspec/commands/update.md +7 -7
- package/openspec/commands/verify.md +7 -7
- package/openspec/skills/openspec-apply-change/SKILL.md +187 -187
- package/openspec/skills/openspec-archive-change/SKILL.md +181 -181
- package/openspec/skills/openspec-bulk-archive-change/SKILL.md +338 -338
- package/openspec/skills/openspec-continue-change/SKILL.md +117 -117
- package/openspec/skills/openspec-explore/SKILL.md +342 -342
- package/openspec/skills/openspec-ff-change/SKILL.md +116 -116
- package/openspec/skills/openspec-new-change/SKILL.md +76 -76
- package/openspec/skills/openspec-onboard/SKILL.md +560 -560
- package/openspec/skills/openspec-propose/SKILL.md +162 -162
- package/openspec/skills/openspec-sync-specs/SKILL.md +261 -261
- package/openspec/skills/openspec-update-change/SKILL.md +90 -90
- package/openspec/skills/openspec-verify-change/SKILL.md +174 -174
- package/package.json +4 -3
- package/superpowers/.version +6 -6
- package/superpowers/skills/brainstorming/SKILL.md +250 -250
- package/superpowers/skills/brainstorming/scripts/helper.js +167 -167
- package/superpowers/skills/brainstorming/scripts/start-server.sh +209 -209
- package/superpowers/skills/brainstorming/scripts/stop-server.sh +120 -120
- package/superpowers/skills/brainstorming/spec-document-reviewer-prompt.md +49 -49
- package/superpowers/skills/brainstorming/visual-companion.md +299 -299
- package/superpowers/skills/dispatching-parallel-agents/SKILL.md +167 -167
- package/superpowers/skills/executing-plans/SKILL.md +64 -64
- package/superpowers/skills/finishing-a-development-branch/SKILL.md +225 -225
- package/superpowers/skills/receiving-code-review/SKILL.md +205 -205
- package/superpowers/skills/requesting-code-review/SKILL.md +95 -95
- package/superpowers/skills/requesting-code-review/code-reviewer.md +181 -181
- package/superpowers/skills/subagent-driven-development/SKILL.md +568 -568
- package/superpowers/skills/subagent-driven-development/implementer-prompt.md +154 -154
- package/superpowers/skills/subagent-driven-development/re-review-prompt.md +115 -115
- package/superpowers/skills/subagent-driven-development/task-reviewer-prompt.md +207 -207
- package/superpowers/skills/systematic-debugging/CREATION-LOG.md +119 -119
- package/superpowers/skills/systematic-debugging/SKILL.md +283 -283
- package/superpowers/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
- package/superpowers/skills/systematic-debugging/condition-based-waiting.md +115 -115
- package/superpowers/skills/systematic-debugging/defense-in-depth.md +122 -122
- package/superpowers/skills/systematic-debugging/find-polluter.sh +72 -72
- package/superpowers/skills/systematic-debugging/root-cause-tracing.md +169 -169
- package/superpowers/skills/systematic-debugging/test-academic.md +14 -14
- package/superpowers/skills/systematic-debugging/test-pressure-1.md +58 -58
- package/superpowers/skills/systematic-debugging/test-pressure-2.md +68 -68
- package/superpowers/skills/systematic-debugging/test-pressure-3.md +69 -69
- package/superpowers/skills/test-driven-development/SKILL.md +320 -320
- package/superpowers/skills/test-driven-development/writing-good-tests.md +198 -198
- package/superpowers/skills/using-git-worktrees/SKILL.md +167 -167
- package/superpowers/skills/using-superpowers/SKILL.md +63 -63
- package/superpowers/skills/using-superpowers/references/antigravity-tools.md +23 -23
- package/superpowers/skills/using-superpowers/references/codex-tools.md +108 -108
- package/superpowers/skills/using-superpowers/references/gemini-tools.md +63 -63
- package/superpowers/skills/using-superpowers/references/hermes-tools.md +56 -56
- package/superpowers/skills/using-superpowers/references/pi-tools.md +16 -16
- package/superpowers/skills/verification-before-completion/SKILL.md +120 -120
- package/superpowers/skills/writing-plans/SKILL.md +171 -171
- package/superpowers/skills/writing-plans/plan-document-reviewer-prompt.md +49 -49
- package/superpowers/skills/writing-skills/SKILL.md +679 -679
- package/superpowers/skills/writing-skills/anthropic-best-practices.md +1150 -1150
- package/superpowers/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -189
- package/superpowers/skills/writing-skills/persuasion-principles.md +187 -187
- package/superpowers/skills/writing-skills/render-graphs.js +169 -169
- package/superpowers/skills/writing-skills/testing-skills-with-subagents.md +384 -384
- package/cbb/worktrees/commands/extend.md +0 -65
- package/cbb/worktrees/skills/openspec-close-worktree/scripts/find-target-worktree.js +0 -96
- package/cbb/worktrees/skills/openspec-extend-worktree/SKILL.md +0 -390
- package/cbb/worktrees/skills/openspec-extend-worktree/scripts/check-env.js +0 -95
- package/cbb/worktrees/skills/openspec-extend-worktree/scripts/check-repos.js +0 -98
- package/cbb/worktrees/skills/openspec-extend-worktree/scripts/create-branches-and-worktrees.js +0 -135
- package/cbb/worktrees/skills/openspec-extend-worktree/scripts/install-ai.js +0 -150
- package/cbb/worktrees/skills/openspec-extend-worktree/scripts/list-available-apps.js +0 -88
- package/cbb/worktrees/skills/openspec-extend-worktree/scripts/sync-repos.js +0 -93
- package/cbb/worktrees/skills/openspec-init-worktree/scripts/find-workspace-root.js +0 -67
- package/cbb/worktrees/skills/openspec-init-worktree/scripts/generate-app-options.js +0 -93
- package/cbb/worktrees/skills/openspec-init-worktree/scripts/push-core.js +0 -136
- package/cbb/worktrees/skills/openspec-push-worktrees/scripts/silence-popup.js +0 -23
- package/config/config.sample.json +0 -16
- /package/cbb/worktrees/skills/{openspec-close-worktree → cbb-worktree-close}/scripts/silence-popup.js +0 -0
- /package/cbb/worktrees/skills/{openspec-extend-worktree → cbb-worktree-init}/scripts/silence-popup.js +0 -0
- /package/cbb/worktrees/skills/{openspec-init-worktree → cbb-worktree-push}/scripts/silence-popup.js +0 -0
|
@@ -1,209 +1,209 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Start the brainstorm server and output connection info
|
|
3
|
-
# Usage: start-server.sh [--project-dir <path>] [--host <bind-host>] [--url-host <display-host>] [--foreground] [--background]
|
|
4
|
-
#
|
|
5
|
-
# Starts server on a random high port, outputs JSON with URL.
|
|
6
|
-
# Each session gets its own directory to avoid conflicts.
|
|
7
|
-
#
|
|
8
|
-
# Options:
|
|
9
|
-
# --project-dir <path> Store session files under <path>/.superpowers/brainstorm/
|
|
10
|
-
# instead of /tmp. Files persist after server stops.
|
|
11
|
-
# --host <bind-host> Host/interface to bind (default: 127.0.0.1).
|
|
12
|
-
# Use 0.0.0.0 in remote/containerized environments.
|
|
13
|
-
# --url-host <host> Hostname shown in returned URL JSON.
|
|
14
|
-
# --idle-timeout-minutes <n> Shut down after n minutes idle (default 240 = 4h).
|
|
15
|
-
# --open Auto-open the browser on the first screen (use only
|
|
16
|
-
# after the user approves the visual companion).
|
|
17
|
-
# --foreground Run server in the current terminal (no backgrounding).
|
|
18
|
-
# --background Force background mode (overrides Codex auto-foreground).
|
|
19
|
-
|
|
20
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
21
|
-
|
|
22
|
-
# Parse arguments
|
|
23
|
-
PROJECT_DIR=""
|
|
24
|
-
FOREGROUND="false"
|
|
25
|
-
FORCE_BACKGROUND="false"
|
|
26
|
-
BIND_HOST="127.0.0.1"
|
|
27
|
-
URL_HOST=""
|
|
28
|
-
IDLE_TIMEOUT_MINUTES=""
|
|
29
|
-
while [[ $# -gt 0 ]]; do
|
|
30
|
-
case "$1" in
|
|
31
|
-
--project-dir)
|
|
32
|
-
PROJECT_DIR="$2"
|
|
33
|
-
shift 2
|
|
34
|
-
;;
|
|
35
|
-
--host)
|
|
36
|
-
BIND_HOST="$2"
|
|
37
|
-
shift 2
|
|
38
|
-
;;
|
|
39
|
-
--url-host)
|
|
40
|
-
URL_HOST="$2"
|
|
41
|
-
shift 2
|
|
42
|
-
;;
|
|
43
|
-
--idle-timeout-minutes)
|
|
44
|
-
IDLE_TIMEOUT_MINUTES="$2"
|
|
45
|
-
shift 2
|
|
46
|
-
;;
|
|
47
|
-
--open)
|
|
48
|
-
export BRAINSTORM_OPEN=1
|
|
49
|
-
shift
|
|
50
|
-
;;
|
|
51
|
-
--foreground|--no-daemon)
|
|
52
|
-
FOREGROUND="true"
|
|
53
|
-
shift
|
|
54
|
-
;;
|
|
55
|
-
--background|--daemon)
|
|
56
|
-
FORCE_BACKGROUND="true"
|
|
57
|
-
shift
|
|
58
|
-
;;
|
|
59
|
-
*)
|
|
60
|
-
echo "{\"error\": \"Unknown argument: $1\"}"
|
|
61
|
-
exit 1
|
|
62
|
-
;;
|
|
63
|
-
esac
|
|
64
|
-
done
|
|
65
|
-
|
|
66
|
-
if [[ -z "$URL_HOST" ]]; then
|
|
67
|
-
if [[ "$BIND_HOST" == "127.0.0.1" || "$BIND_HOST" == "localhost" ]]; then
|
|
68
|
-
URL_HOST="localhost"
|
|
69
|
-
else
|
|
70
|
-
URL_HOST="$BIND_HOST"
|
|
71
|
-
fi
|
|
72
|
-
fi
|
|
73
|
-
|
|
74
|
-
if [[ -n "$IDLE_TIMEOUT_MINUTES" ]]; then
|
|
75
|
-
if ! [[ "$IDLE_TIMEOUT_MINUTES" =~ ^[0-9]+$ ]] || [[ "$IDLE_TIMEOUT_MINUTES" -lt 1 ]]; then
|
|
76
|
-
echo "{\"error\": \"--idle-timeout-minutes must be a positive integer\"}"
|
|
77
|
-
exit 1
|
|
78
|
-
fi
|
|
79
|
-
export BRAINSTORM_IDLE_TIMEOUT_MS=$(( IDLE_TIMEOUT_MINUTES * 60 * 1000 ))
|
|
80
|
-
fi
|
|
81
|
-
|
|
82
|
-
is_windows_like_shell() {
|
|
83
|
-
case "${OSTYPE:-}" in
|
|
84
|
-
msys*|cygwin*|mingw*) return 0 ;;
|
|
85
|
-
esac
|
|
86
|
-
if [[ -n "${MSYSTEM:-}" ]]; then
|
|
87
|
-
return 0
|
|
88
|
-
fi
|
|
89
|
-
local uname_s
|
|
90
|
-
uname_s="$(uname -s 2>/dev/null || true)"
|
|
91
|
-
case "$uname_s" in
|
|
92
|
-
MSYS*|MINGW*|CYGWIN*) return 0 ;;
|
|
93
|
-
esac
|
|
94
|
-
return 1
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
# Some environments reap detached/background processes. Auto-foreground when detected.
|
|
98
|
-
if [[ -n "${CODEX_CI:-}" && "$FOREGROUND" != "true" && "$FORCE_BACKGROUND" != "true" ]]; then
|
|
99
|
-
FOREGROUND="true"
|
|
100
|
-
fi
|
|
101
|
-
|
|
102
|
-
# Windows/Git Bash reaps nohup background processes. Auto-foreground when detected.
|
|
103
|
-
if [[ "$FOREGROUND" != "true" && "$FORCE_BACKGROUND" != "true" ]]; then
|
|
104
|
-
if is_windows_like_shell; then
|
|
105
|
-
FOREGROUND="true"
|
|
106
|
-
fi
|
|
107
|
-
fi
|
|
108
|
-
|
|
109
|
-
# Session files (server.log, server-info, .last-token) embed the session key —
|
|
110
|
-
# keep everything this script and the server create owner-only.
|
|
111
|
-
umask 077
|
|
112
|
-
|
|
113
|
-
# Generate unique session directory
|
|
114
|
-
SESSION_ID="$$-$(date +%s)"
|
|
115
|
-
|
|
116
|
-
if [[ -n "$PROJECT_DIR" ]]; then
|
|
117
|
-
SESSION_DIR="${PROJECT_DIR}/.superpowers/brainstorm/${SESSION_ID}"
|
|
118
|
-
# Persist the bound port and key per project so a restart reuses them and an
|
|
119
|
-
# already-open browser tab reconnects to the same URL with a valid cookie.
|
|
120
|
-
export BRAINSTORM_PORT_FILE="${PROJECT_DIR}/.superpowers/brainstorm/.last-port"
|
|
121
|
-
export BRAINSTORM_TOKEN_FILE="${PROJECT_DIR}/.superpowers/brainstorm/.last-token"
|
|
122
|
-
else
|
|
123
|
-
SESSION_DIR="/tmp/brainstorm-${SESSION_ID}"
|
|
124
|
-
fi
|
|
125
|
-
|
|
126
|
-
STATE_DIR="${SESSION_DIR}/state"
|
|
127
|
-
PID_FILE="${STATE_DIR}/server.pid"
|
|
128
|
-
LOG_FILE="${STATE_DIR}/server.log"
|
|
129
|
-
SERVER_ID_FILE="${STATE_DIR}/server-instance-id"
|
|
130
|
-
|
|
131
|
-
# Create fresh session directory with content and state peers
|
|
132
|
-
mkdir -p "${SESSION_DIR}/content" "$STATE_DIR"
|
|
133
|
-
|
|
134
|
-
SERVER_ID=""
|
|
135
|
-
if [[ -r /dev/urandom ]]; then
|
|
136
|
-
SERVER_ID="$(od -An -N24 -tx1 /dev/urandom 2>/dev/null | tr -d ' \n' || true)"
|
|
137
|
-
fi
|
|
138
|
-
if ! [[ "$SERVER_ID" =~ ^[A-Za-z0-9_-]{32,64}$ ]]; then
|
|
139
|
-
SERVER_ID="$(printf '%08x%08x%08x%08x' "$$" "$(date +%s)" "${RANDOM:-0}" "${RANDOM:-0}")"
|
|
140
|
-
fi
|
|
141
|
-
printf '%s\n' "$SERVER_ID" > "$SERVER_ID_FILE"
|
|
142
|
-
chmod 600 "$SERVER_ID_FILE" 2>/dev/null || true
|
|
143
|
-
|
|
144
|
-
# Kill any existing server
|
|
145
|
-
if [[ -f "$PID_FILE" ]]; then
|
|
146
|
-
old_pid=$(cat "$PID_FILE")
|
|
147
|
-
kill "$old_pid" 2>/dev/null
|
|
148
|
-
rm -f "$PID_FILE"
|
|
149
|
-
fi
|
|
150
|
-
|
|
151
|
-
cd "$SCRIPT_DIR" || exit 1
|
|
152
|
-
|
|
153
|
-
# Resolve the harness PID (grandparent of this script).
|
|
154
|
-
# $PPID is the ephemeral shell the harness spawned to run us — it dies
|
|
155
|
-
# when this script exits. The harness itself is $PPID's parent.
|
|
156
|
-
OWNER_PID="$(ps -o ppid= -p "$PPID" 2>/dev/null | tr -d ' ')"
|
|
157
|
-
if [[ -z "$OWNER_PID" || "$OWNER_PID" == "1" ]]; then
|
|
158
|
-
OWNER_PID="$PPID"
|
|
159
|
-
fi
|
|
160
|
-
|
|
161
|
-
# Windows/MSYS2: Node.js cannot see POSIX PIDs from the MSYS2 namespace.
|
|
162
|
-
# Passing a PID node cannot verify causes server to log owner-pid-invalid
|
|
163
|
-
# and self-terminate at the 60-second lifecycle check. Clear it so the
|
|
164
|
-
# watchdog is disabled and the idle timeout becomes the only shutdown trigger.
|
|
165
|
-
if is_windows_like_shell; then
|
|
166
|
-
OWNER_PID=""
|
|
167
|
-
fi
|
|
168
|
-
|
|
169
|
-
# Foreground mode for environments that reap detached/background processes.
|
|
170
|
-
if [[ "$FOREGROUND" == "true" ]]; then
|
|
171
|
-
env BRAINSTORM_DIR="$SESSION_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs "--brainstorm-server-id=$SERVER_ID" &
|
|
172
|
-
SERVER_PID=$!
|
|
173
|
-
echo "$SERVER_PID" > "$PID_FILE"
|
|
174
|
-
wait "$SERVER_PID"
|
|
175
|
-
exit $?
|
|
176
|
-
fi
|
|
177
|
-
|
|
178
|
-
# Start server, capturing output to log file
|
|
179
|
-
# Use nohup to survive shell exit; disown to remove from job table
|
|
180
|
-
nohup env BRAINSTORM_DIR="$SESSION_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs "--brainstorm-server-id=$SERVER_ID" > "$LOG_FILE" 2>&1 &
|
|
181
|
-
SERVER_PID=$!
|
|
182
|
-
disown "$SERVER_PID" 2>/dev/null
|
|
183
|
-
echo "$SERVER_PID" > "$PID_FILE"
|
|
184
|
-
|
|
185
|
-
# Wait for server-started message (check log file)
|
|
186
|
-
for _ in {1..50}; do
|
|
187
|
-
if grep -q "server-started" "$LOG_FILE" 2>/dev/null; then
|
|
188
|
-
# Verify server is still alive after a short window (catches process reapers)
|
|
189
|
-
alive="true"
|
|
190
|
-
for _ in {1..20}; do
|
|
191
|
-
if ! kill -0 "$SERVER_PID" 2>/dev/null; then
|
|
192
|
-
alive="false"
|
|
193
|
-
break
|
|
194
|
-
fi
|
|
195
|
-
sleep 0.1
|
|
196
|
-
done
|
|
197
|
-
if [[ "$alive" != "true" ]]; then
|
|
198
|
-
echo "{\"error\": \"Server started but was killed. Retry in a persistent terminal with: $SCRIPT_DIR/start-server.sh${PROJECT_DIR:+ --project-dir $PROJECT_DIR} --host $BIND_HOST --url-host $URL_HOST --foreground\"}"
|
|
199
|
-
exit 1
|
|
200
|
-
fi
|
|
201
|
-
grep "server-started" "$LOG_FILE" | head -1
|
|
202
|
-
exit 0
|
|
203
|
-
fi
|
|
204
|
-
sleep 0.1
|
|
205
|
-
done
|
|
206
|
-
|
|
207
|
-
# Timeout - server didn't start
|
|
208
|
-
echo '{"error": "Server failed to start within 5 seconds"}'
|
|
209
|
-
exit 1
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Start the brainstorm server and output connection info
|
|
3
|
+
# Usage: start-server.sh [--project-dir <path>] [--host <bind-host>] [--url-host <display-host>] [--foreground] [--background]
|
|
4
|
+
#
|
|
5
|
+
# Starts server on a random high port, outputs JSON with URL.
|
|
6
|
+
# Each session gets its own directory to avoid conflicts.
|
|
7
|
+
#
|
|
8
|
+
# Options:
|
|
9
|
+
# --project-dir <path> Store session files under <path>/.superpowers/brainstorm/
|
|
10
|
+
# instead of /tmp. Files persist after server stops.
|
|
11
|
+
# --host <bind-host> Host/interface to bind (default: 127.0.0.1).
|
|
12
|
+
# Use 0.0.0.0 in remote/containerized environments.
|
|
13
|
+
# --url-host <host> Hostname shown in returned URL JSON.
|
|
14
|
+
# --idle-timeout-minutes <n> Shut down after n minutes idle (default 240 = 4h).
|
|
15
|
+
# --open Auto-open the browser on the first screen (use only
|
|
16
|
+
# after the user approves the visual companion).
|
|
17
|
+
# --foreground Run server in the current terminal (no backgrounding).
|
|
18
|
+
# --background Force background mode (overrides Codex auto-foreground).
|
|
19
|
+
|
|
20
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
21
|
+
|
|
22
|
+
# Parse arguments
|
|
23
|
+
PROJECT_DIR=""
|
|
24
|
+
FOREGROUND="false"
|
|
25
|
+
FORCE_BACKGROUND="false"
|
|
26
|
+
BIND_HOST="127.0.0.1"
|
|
27
|
+
URL_HOST=""
|
|
28
|
+
IDLE_TIMEOUT_MINUTES=""
|
|
29
|
+
while [[ $# -gt 0 ]]; do
|
|
30
|
+
case "$1" in
|
|
31
|
+
--project-dir)
|
|
32
|
+
PROJECT_DIR="$2"
|
|
33
|
+
shift 2
|
|
34
|
+
;;
|
|
35
|
+
--host)
|
|
36
|
+
BIND_HOST="$2"
|
|
37
|
+
shift 2
|
|
38
|
+
;;
|
|
39
|
+
--url-host)
|
|
40
|
+
URL_HOST="$2"
|
|
41
|
+
shift 2
|
|
42
|
+
;;
|
|
43
|
+
--idle-timeout-minutes)
|
|
44
|
+
IDLE_TIMEOUT_MINUTES="$2"
|
|
45
|
+
shift 2
|
|
46
|
+
;;
|
|
47
|
+
--open)
|
|
48
|
+
export BRAINSTORM_OPEN=1
|
|
49
|
+
shift
|
|
50
|
+
;;
|
|
51
|
+
--foreground|--no-daemon)
|
|
52
|
+
FOREGROUND="true"
|
|
53
|
+
shift
|
|
54
|
+
;;
|
|
55
|
+
--background|--daemon)
|
|
56
|
+
FORCE_BACKGROUND="true"
|
|
57
|
+
shift
|
|
58
|
+
;;
|
|
59
|
+
*)
|
|
60
|
+
echo "{\"error\": \"Unknown argument: $1\"}"
|
|
61
|
+
exit 1
|
|
62
|
+
;;
|
|
63
|
+
esac
|
|
64
|
+
done
|
|
65
|
+
|
|
66
|
+
if [[ -z "$URL_HOST" ]]; then
|
|
67
|
+
if [[ "$BIND_HOST" == "127.0.0.1" || "$BIND_HOST" == "localhost" ]]; then
|
|
68
|
+
URL_HOST="localhost"
|
|
69
|
+
else
|
|
70
|
+
URL_HOST="$BIND_HOST"
|
|
71
|
+
fi
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
if [[ -n "$IDLE_TIMEOUT_MINUTES" ]]; then
|
|
75
|
+
if ! [[ "$IDLE_TIMEOUT_MINUTES" =~ ^[0-9]+$ ]] || [[ "$IDLE_TIMEOUT_MINUTES" -lt 1 ]]; then
|
|
76
|
+
echo "{\"error\": \"--idle-timeout-minutes must be a positive integer\"}"
|
|
77
|
+
exit 1
|
|
78
|
+
fi
|
|
79
|
+
export BRAINSTORM_IDLE_TIMEOUT_MS=$(( IDLE_TIMEOUT_MINUTES * 60 * 1000 ))
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
is_windows_like_shell() {
|
|
83
|
+
case "${OSTYPE:-}" in
|
|
84
|
+
msys*|cygwin*|mingw*) return 0 ;;
|
|
85
|
+
esac
|
|
86
|
+
if [[ -n "${MSYSTEM:-}" ]]; then
|
|
87
|
+
return 0
|
|
88
|
+
fi
|
|
89
|
+
local uname_s
|
|
90
|
+
uname_s="$(uname -s 2>/dev/null || true)"
|
|
91
|
+
case "$uname_s" in
|
|
92
|
+
MSYS*|MINGW*|CYGWIN*) return 0 ;;
|
|
93
|
+
esac
|
|
94
|
+
return 1
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# Some environments reap detached/background processes. Auto-foreground when detected.
|
|
98
|
+
if [[ -n "${CODEX_CI:-}" && "$FOREGROUND" != "true" && "$FORCE_BACKGROUND" != "true" ]]; then
|
|
99
|
+
FOREGROUND="true"
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
# Windows/Git Bash reaps nohup background processes. Auto-foreground when detected.
|
|
103
|
+
if [[ "$FOREGROUND" != "true" && "$FORCE_BACKGROUND" != "true" ]]; then
|
|
104
|
+
if is_windows_like_shell; then
|
|
105
|
+
FOREGROUND="true"
|
|
106
|
+
fi
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
# Session files (server.log, server-info, .last-token) embed the session key —
|
|
110
|
+
# keep everything this script and the server create owner-only.
|
|
111
|
+
umask 077
|
|
112
|
+
|
|
113
|
+
# Generate unique session directory
|
|
114
|
+
SESSION_ID="$$-$(date +%s)"
|
|
115
|
+
|
|
116
|
+
if [[ -n "$PROJECT_DIR" ]]; then
|
|
117
|
+
SESSION_DIR="${PROJECT_DIR}/.superpowers/brainstorm/${SESSION_ID}"
|
|
118
|
+
# Persist the bound port and key per project so a restart reuses them and an
|
|
119
|
+
# already-open browser tab reconnects to the same URL with a valid cookie.
|
|
120
|
+
export BRAINSTORM_PORT_FILE="${PROJECT_DIR}/.superpowers/brainstorm/.last-port"
|
|
121
|
+
export BRAINSTORM_TOKEN_FILE="${PROJECT_DIR}/.superpowers/brainstorm/.last-token"
|
|
122
|
+
else
|
|
123
|
+
SESSION_DIR="/tmp/brainstorm-${SESSION_ID}"
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
STATE_DIR="${SESSION_DIR}/state"
|
|
127
|
+
PID_FILE="${STATE_DIR}/server.pid"
|
|
128
|
+
LOG_FILE="${STATE_DIR}/server.log"
|
|
129
|
+
SERVER_ID_FILE="${STATE_DIR}/server-instance-id"
|
|
130
|
+
|
|
131
|
+
# Create fresh session directory with content and state peers
|
|
132
|
+
mkdir -p "${SESSION_DIR}/content" "$STATE_DIR"
|
|
133
|
+
|
|
134
|
+
SERVER_ID=""
|
|
135
|
+
if [[ -r /dev/urandom ]]; then
|
|
136
|
+
SERVER_ID="$(od -An -N24 -tx1 /dev/urandom 2>/dev/null | tr -d ' \n' || true)"
|
|
137
|
+
fi
|
|
138
|
+
if ! [[ "$SERVER_ID" =~ ^[A-Za-z0-9_-]{32,64}$ ]]; then
|
|
139
|
+
SERVER_ID="$(printf '%08x%08x%08x%08x' "$$" "$(date +%s)" "${RANDOM:-0}" "${RANDOM:-0}")"
|
|
140
|
+
fi
|
|
141
|
+
printf '%s\n' "$SERVER_ID" > "$SERVER_ID_FILE"
|
|
142
|
+
chmod 600 "$SERVER_ID_FILE" 2>/dev/null || true
|
|
143
|
+
|
|
144
|
+
# Kill any existing server
|
|
145
|
+
if [[ -f "$PID_FILE" ]]; then
|
|
146
|
+
old_pid=$(cat "$PID_FILE")
|
|
147
|
+
kill "$old_pid" 2>/dev/null
|
|
148
|
+
rm -f "$PID_FILE"
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
cd "$SCRIPT_DIR" || exit 1
|
|
152
|
+
|
|
153
|
+
# Resolve the harness PID (grandparent of this script).
|
|
154
|
+
# $PPID is the ephemeral shell the harness spawned to run us — it dies
|
|
155
|
+
# when this script exits. The harness itself is $PPID's parent.
|
|
156
|
+
OWNER_PID="$(ps -o ppid= -p "$PPID" 2>/dev/null | tr -d ' ')"
|
|
157
|
+
if [[ -z "$OWNER_PID" || "$OWNER_PID" == "1" ]]; then
|
|
158
|
+
OWNER_PID="$PPID"
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
# Windows/MSYS2: Node.js cannot see POSIX PIDs from the MSYS2 namespace.
|
|
162
|
+
# Passing a PID node cannot verify causes server to log owner-pid-invalid
|
|
163
|
+
# and self-terminate at the 60-second lifecycle check. Clear it so the
|
|
164
|
+
# watchdog is disabled and the idle timeout becomes the only shutdown trigger.
|
|
165
|
+
if is_windows_like_shell; then
|
|
166
|
+
OWNER_PID=""
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
# Foreground mode for environments that reap detached/background processes.
|
|
170
|
+
if [[ "$FOREGROUND" == "true" ]]; then
|
|
171
|
+
env BRAINSTORM_DIR="$SESSION_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs "--brainstorm-server-id=$SERVER_ID" &
|
|
172
|
+
SERVER_PID=$!
|
|
173
|
+
echo "$SERVER_PID" > "$PID_FILE"
|
|
174
|
+
wait "$SERVER_PID"
|
|
175
|
+
exit $?
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
# Start server, capturing output to log file
|
|
179
|
+
# Use nohup to survive shell exit; disown to remove from job table
|
|
180
|
+
nohup env BRAINSTORM_DIR="$SESSION_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs "--brainstorm-server-id=$SERVER_ID" > "$LOG_FILE" 2>&1 &
|
|
181
|
+
SERVER_PID=$!
|
|
182
|
+
disown "$SERVER_PID" 2>/dev/null
|
|
183
|
+
echo "$SERVER_PID" > "$PID_FILE"
|
|
184
|
+
|
|
185
|
+
# Wait for server-started message (check log file)
|
|
186
|
+
for _ in {1..50}; do
|
|
187
|
+
if grep -q "server-started" "$LOG_FILE" 2>/dev/null; then
|
|
188
|
+
# Verify server is still alive after a short window (catches process reapers)
|
|
189
|
+
alive="true"
|
|
190
|
+
for _ in {1..20}; do
|
|
191
|
+
if ! kill -0 "$SERVER_PID" 2>/dev/null; then
|
|
192
|
+
alive="false"
|
|
193
|
+
break
|
|
194
|
+
fi
|
|
195
|
+
sleep 0.1
|
|
196
|
+
done
|
|
197
|
+
if [[ "$alive" != "true" ]]; then
|
|
198
|
+
echo "{\"error\": \"Server started but was killed. Retry in a persistent terminal with: $SCRIPT_DIR/start-server.sh${PROJECT_DIR:+ --project-dir $PROJECT_DIR} --host $BIND_HOST --url-host $URL_HOST --foreground\"}"
|
|
199
|
+
exit 1
|
|
200
|
+
fi
|
|
201
|
+
grep "server-started" "$LOG_FILE" | head -1
|
|
202
|
+
exit 0
|
|
203
|
+
fi
|
|
204
|
+
sleep 0.1
|
|
205
|
+
done
|
|
206
|
+
|
|
207
|
+
# Timeout - server didn't start
|
|
208
|
+
echo '{"error": "Server failed to start within 5 seconds"}'
|
|
209
|
+
exit 1
|
|
@@ -1,120 +1,120 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Stop the brainstorm server and clean up
|
|
3
|
-
# Usage: stop-server.sh <session_dir>
|
|
4
|
-
#
|
|
5
|
-
# Kills the server process. Only deletes session directory if it's
|
|
6
|
-
# under /tmp (ephemeral). Persistent directories (.superpowers/) are
|
|
7
|
-
# kept so mockups can be reviewed later.
|
|
8
|
-
|
|
9
|
-
SESSION_DIR="$1"
|
|
10
|
-
|
|
11
|
-
if [[ -z "$SESSION_DIR" ]]; then
|
|
12
|
-
echo '{"error": "Usage: stop-server.sh <session_dir>"}'
|
|
13
|
-
exit 1
|
|
14
|
-
fi
|
|
15
|
-
|
|
16
|
-
STATE_DIR="${SESSION_DIR}/state"
|
|
17
|
-
PID_FILE="${STATE_DIR}/server.pid"
|
|
18
|
-
SERVER_ID_FILE="${STATE_DIR}/server-instance-id"
|
|
19
|
-
|
|
20
|
-
mark_stopped() {
|
|
21
|
-
local reason="$1"
|
|
22
|
-
rm -f "${STATE_DIR}/server-info"
|
|
23
|
-
printf '{"reason":"%s","timestamp":%s}\n' "$reason" "$(date +%s)" > "${STATE_DIR}/server-stopped"
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
read_expected_server_id() {
|
|
27
|
-
[[ -f "$SERVER_ID_FILE" ]] || return 1
|
|
28
|
-
local id
|
|
29
|
-
id="$(tr -d '\r\n' < "$SERVER_ID_FILE" 2>/dev/null || true)"
|
|
30
|
-
[[ "$id" =~ ^[A-Za-z0-9_-]{32,64}$ ]] || return 1
|
|
31
|
-
printf '%s\n' "$id"
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
command_line_for_pid() {
|
|
35
|
-
local pid="$1"
|
|
36
|
-
if [[ -r "/proc/$pid/cmdline" ]]; then
|
|
37
|
-
tr '\0' '\n' < "/proc/$pid/cmdline" 2>/dev/null || true
|
|
38
|
-
return 0
|
|
39
|
-
fi
|
|
40
|
-
ps -ww -p "$pid" -o command= 2>/dev/null || ps -f -p "$pid" 2>/dev/null | sed '1d' || true
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
command_has_server_id() {
|
|
44
|
-
local pid="$1"
|
|
45
|
-
local expected="$2"
|
|
46
|
-
local expected_arg="--brainstorm-server-id=$expected"
|
|
47
|
-
if [[ -r "/proc/$pid/cmdline" ]]; then
|
|
48
|
-
local arg
|
|
49
|
-
while IFS= read -r -d '' arg || [[ -n "$arg" ]]; do
|
|
50
|
-
[[ "$arg" == "$expected_arg" ]] && return 0
|
|
51
|
-
done < "/proc/$pid/cmdline"
|
|
52
|
-
return 1
|
|
53
|
-
fi
|
|
54
|
-
local command_line
|
|
55
|
-
command_line="$(command_line_for_pid "$pid")"
|
|
56
|
-
[[ -n "$command_line" ]] || return 1
|
|
57
|
-
case " $command_line " in
|
|
58
|
-
*" $expected_arg "*) return 0 ;;
|
|
59
|
-
*) return 1 ;;
|
|
60
|
-
esac
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
# Confirm a PID has this session's per-start instance id, not just a familiar
|
|
64
|
-
# process name. Ambiguous or legacy metadata fails closed as stale_pid.
|
|
65
|
-
is_brainstorm_server() {
|
|
66
|
-
kill -0 "$1" 2>/dev/null || return 1
|
|
67
|
-
local expected_id
|
|
68
|
-
expected_id="$(read_expected_server_id)" || return 1
|
|
69
|
-
command_has_server_id "$1" "$expected_id" || return 1
|
|
70
|
-
return 0
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if [[ -f "$PID_FILE" ]]; then
|
|
74
|
-
pid=$(cat "$PID_FILE")
|
|
75
|
-
|
|
76
|
-
# Refuse to signal a PID we can't prove is our server. A stale pid file may
|
|
77
|
-
# point at an unrelated process after a reboot/PID wraparound.
|
|
78
|
-
if ! is_brainstorm_server "$pid"; then
|
|
79
|
-
rm -f "$PID_FILE" "$SERVER_ID_FILE"
|
|
80
|
-
mark_stopped "stale_pid"
|
|
81
|
-
echo '{"status": "stale_pid"}'
|
|
82
|
-
exit 0
|
|
83
|
-
fi
|
|
84
|
-
|
|
85
|
-
# Try to stop gracefully, fallback to force if still alive
|
|
86
|
-
kill "$pid" 2>/dev/null || true
|
|
87
|
-
|
|
88
|
-
# Wait for graceful shutdown (up to ~2s)
|
|
89
|
-
for _ in {1..20}; do
|
|
90
|
-
if ! kill -0 "$pid" 2>/dev/null; then
|
|
91
|
-
break
|
|
92
|
-
fi
|
|
93
|
-
sleep 0.1
|
|
94
|
-
done
|
|
95
|
-
|
|
96
|
-
# If still running, escalate to SIGKILL
|
|
97
|
-
if kill -0 "$pid" 2>/dev/null; then
|
|
98
|
-
kill -9 "$pid" 2>/dev/null || true
|
|
99
|
-
|
|
100
|
-
# Give SIGKILL a moment to take effect
|
|
101
|
-
sleep 0.1
|
|
102
|
-
fi
|
|
103
|
-
|
|
104
|
-
if kill -0 "$pid" 2>/dev/null; then
|
|
105
|
-
echo '{"status": "failed", "error": "process still running"}'
|
|
106
|
-
exit 1
|
|
107
|
-
fi
|
|
108
|
-
|
|
109
|
-
rm -f "$PID_FILE" "$SERVER_ID_FILE" "${STATE_DIR}/server.log"
|
|
110
|
-
mark_stopped "stop-server.sh"
|
|
111
|
-
|
|
112
|
-
# Only delete ephemeral /tmp directories
|
|
113
|
-
if [[ "$SESSION_DIR" == /tmp/* ]]; then
|
|
114
|
-
rm -rf "$SESSION_DIR"
|
|
115
|
-
fi
|
|
116
|
-
|
|
117
|
-
echo '{"status": "stopped"}'
|
|
118
|
-
else
|
|
119
|
-
echo '{"status": "not_running"}'
|
|
120
|
-
fi
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Stop the brainstorm server and clean up
|
|
3
|
+
# Usage: stop-server.sh <session_dir>
|
|
4
|
+
#
|
|
5
|
+
# Kills the server process. Only deletes session directory if it's
|
|
6
|
+
# under /tmp (ephemeral). Persistent directories (.superpowers/) are
|
|
7
|
+
# kept so mockups can be reviewed later.
|
|
8
|
+
|
|
9
|
+
SESSION_DIR="$1"
|
|
10
|
+
|
|
11
|
+
if [[ -z "$SESSION_DIR" ]]; then
|
|
12
|
+
echo '{"error": "Usage: stop-server.sh <session_dir>"}'
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
STATE_DIR="${SESSION_DIR}/state"
|
|
17
|
+
PID_FILE="${STATE_DIR}/server.pid"
|
|
18
|
+
SERVER_ID_FILE="${STATE_DIR}/server-instance-id"
|
|
19
|
+
|
|
20
|
+
mark_stopped() {
|
|
21
|
+
local reason="$1"
|
|
22
|
+
rm -f "${STATE_DIR}/server-info"
|
|
23
|
+
printf '{"reason":"%s","timestamp":%s}\n' "$reason" "$(date +%s)" > "${STATE_DIR}/server-stopped"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
read_expected_server_id() {
|
|
27
|
+
[[ -f "$SERVER_ID_FILE" ]] || return 1
|
|
28
|
+
local id
|
|
29
|
+
id="$(tr -d '\r\n' < "$SERVER_ID_FILE" 2>/dev/null || true)"
|
|
30
|
+
[[ "$id" =~ ^[A-Za-z0-9_-]{32,64}$ ]] || return 1
|
|
31
|
+
printf '%s\n' "$id"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
command_line_for_pid() {
|
|
35
|
+
local pid="$1"
|
|
36
|
+
if [[ -r "/proc/$pid/cmdline" ]]; then
|
|
37
|
+
tr '\0' '\n' < "/proc/$pid/cmdline" 2>/dev/null || true
|
|
38
|
+
return 0
|
|
39
|
+
fi
|
|
40
|
+
ps -ww -p "$pid" -o command= 2>/dev/null || ps -f -p "$pid" 2>/dev/null | sed '1d' || true
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
command_has_server_id() {
|
|
44
|
+
local pid="$1"
|
|
45
|
+
local expected="$2"
|
|
46
|
+
local expected_arg="--brainstorm-server-id=$expected"
|
|
47
|
+
if [[ -r "/proc/$pid/cmdline" ]]; then
|
|
48
|
+
local arg
|
|
49
|
+
while IFS= read -r -d '' arg || [[ -n "$arg" ]]; do
|
|
50
|
+
[[ "$arg" == "$expected_arg" ]] && return 0
|
|
51
|
+
done < "/proc/$pid/cmdline"
|
|
52
|
+
return 1
|
|
53
|
+
fi
|
|
54
|
+
local command_line
|
|
55
|
+
command_line="$(command_line_for_pid "$pid")"
|
|
56
|
+
[[ -n "$command_line" ]] || return 1
|
|
57
|
+
case " $command_line " in
|
|
58
|
+
*" $expected_arg "*) return 0 ;;
|
|
59
|
+
*) return 1 ;;
|
|
60
|
+
esac
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# Confirm a PID has this session's per-start instance id, not just a familiar
|
|
64
|
+
# process name. Ambiguous or legacy metadata fails closed as stale_pid.
|
|
65
|
+
is_brainstorm_server() {
|
|
66
|
+
kill -0 "$1" 2>/dev/null || return 1
|
|
67
|
+
local expected_id
|
|
68
|
+
expected_id="$(read_expected_server_id)" || return 1
|
|
69
|
+
command_has_server_id "$1" "$expected_id" || return 1
|
|
70
|
+
return 0
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if [[ -f "$PID_FILE" ]]; then
|
|
74
|
+
pid=$(cat "$PID_FILE")
|
|
75
|
+
|
|
76
|
+
# Refuse to signal a PID we can't prove is our server. A stale pid file may
|
|
77
|
+
# point at an unrelated process after a reboot/PID wraparound.
|
|
78
|
+
if ! is_brainstorm_server "$pid"; then
|
|
79
|
+
rm -f "$PID_FILE" "$SERVER_ID_FILE"
|
|
80
|
+
mark_stopped "stale_pid"
|
|
81
|
+
echo '{"status": "stale_pid"}'
|
|
82
|
+
exit 0
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# Try to stop gracefully, fallback to force if still alive
|
|
86
|
+
kill "$pid" 2>/dev/null || true
|
|
87
|
+
|
|
88
|
+
# Wait for graceful shutdown (up to ~2s)
|
|
89
|
+
for _ in {1..20}; do
|
|
90
|
+
if ! kill -0 "$pid" 2>/dev/null; then
|
|
91
|
+
break
|
|
92
|
+
fi
|
|
93
|
+
sleep 0.1
|
|
94
|
+
done
|
|
95
|
+
|
|
96
|
+
# If still running, escalate to SIGKILL
|
|
97
|
+
if kill -0 "$pid" 2>/dev/null; then
|
|
98
|
+
kill -9 "$pid" 2>/dev/null || true
|
|
99
|
+
|
|
100
|
+
# Give SIGKILL a moment to take effect
|
|
101
|
+
sleep 0.1
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
if kill -0 "$pid" 2>/dev/null; then
|
|
105
|
+
echo '{"status": "failed", "error": "process still running"}'
|
|
106
|
+
exit 1
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
rm -f "$PID_FILE" "$SERVER_ID_FILE" "${STATE_DIR}/server.log"
|
|
110
|
+
mark_stopped "stop-server.sh"
|
|
111
|
+
|
|
112
|
+
# Only delete ephemeral /tmp directories
|
|
113
|
+
if [[ "$SESSION_DIR" == /tmp/* ]]; then
|
|
114
|
+
rm -rf "$SESSION_DIR"
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
echo '{"status": "stopped"}'
|
|
118
|
+
else
|
|
119
|
+
echo '{"status": "not_running"}'
|
|
120
|
+
fi
|