@jonit-dev/night-watch-cli 1.8.4-beta.4 → 1.8.4-beta.6
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/dist/cli.js +159 -25
- package/dist/commands/queue.d.ts.map +1 -1
- package/dist/commands/queue.js +51 -8
- package/dist/commands/queue.js.map +1 -1
- package/dist/commands/shared/env-builder.d.ts.map +1 -1
- package/dist/commands/shared/env-builder.js +9 -1
- package/dist/commands/shared/env-builder.js.map +1 -1
- package/dist/commands/slice.d.ts.map +1 -1
- package/dist/commands/slice.js +0 -8
- package/dist/commands/slice.js.map +1 -1
- package/dist/scripts/night-watch-audit-cron.sh +3 -14
- package/dist/scripts/night-watch-cron.sh +2 -14
- package/dist/scripts/night-watch-helpers.sh +47 -53
- package/dist/scripts/night-watch-plan-cron.sh +23 -0
- package/dist/scripts/night-watch-pr-reviewer-cron.sh +2 -17
- package/dist/scripts/night-watch-qa-cron.sh +2 -14
- package/dist/scripts/night-watch-slicer-cron.sh +3 -13
- package/dist/web/assets/index-DnHkqbOa.js +386 -0
- package/dist/web/assets/index-DsYIWZ86.css +1 -0
- package/dist/web/assets/index-bFijnpuU.js +381 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
|
@@ -102,24 +102,12 @@ latest_failure_detail() {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
# ── Global Job Queue Gate ────────────────────────────────────────────────────
|
|
105
|
-
#
|
|
106
|
-
# When gate is busy, enqueue the job and exit cleanly.
|
|
105
|
+
# Atomically claim a DB slot or enqueue for later dispatch — no flock needed.
|
|
107
106
|
if [ "${NW_QUEUE_ENABLED:-0}" = "1" ]; then
|
|
108
107
|
if [ "${NW_QUEUE_DISPATCHED:-0}" = "1" ]; then
|
|
109
108
|
arm_global_queue_cleanup
|
|
110
|
-
elif acquire_global_gate; then
|
|
111
|
-
if queue_can_start_now; then
|
|
112
|
-
arm_global_queue_cleanup
|
|
113
|
-
else
|
|
114
|
-
release_global_gate
|
|
115
|
-
enqueue_job "${SCRIPT_TYPE}" "${PROJECT_DIR}"
|
|
116
|
-
emit_result "queued"
|
|
117
|
-
exit 0
|
|
118
|
-
fi
|
|
119
109
|
else
|
|
120
|
-
|
|
121
|
-
emit_result "queued"
|
|
122
|
-
exit 0
|
|
110
|
+
claim_or_enqueue "${SCRIPT_TYPE}" "${PROJECT_DIR}"
|
|
123
111
|
fi
|
|
124
112
|
fi
|
|
125
113
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
@@ -1029,55 +1029,13 @@ find_eligible_board_issue() {
|
|
|
1029
1029
|
|
|
1030
1030
|
# ── Global Job Queue Gate ─────────────────────────────────────────────────────
|
|
1031
1031
|
|
|
1032
|
-
# Get the path to the queue lock file
|
|
1033
|
-
get_queue_lock_path() {
|
|
1034
|
-
local queue_home="${NIGHT_WATCH_HOME:-${HOME}/.night-watch}"
|
|
1035
|
-
printf "%s/%s" "${queue_home}" "queue.lock"
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
# Try to acquire the global queue gate using flock.
|
|
1039
|
-
# Uses a shared lock file (~/.night-watch/queue.lock).
|
|
1040
|
-
# Returns 0 if acquired (gate is free), 1 if busy.
|
|
1041
|
-
# Holds the lock fd open for the caller via GLOBAL_GATE_FD variable.
|
|
1042
|
-
acquire_global_gate() {
|
|
1043
|
-
local lock_path
|
|
1044
|
-
lock_path=$(get_queue_lock_path)
|
|
1045
|
-
|
|
1046
|
-
# Ensure the .night-watch directory exists
|
|
1047
|
-
mkdir -p "$(dirname "${lock_path}")"
|
|
1048
|
-
|
|
1049
|
-
# Open the lock file as fd 200
|
|
1050
|
-
exec 200>"${lock_path}"
|
|
1051
|
-
|
|
1052
|
-
# Try non-blocking flock
|
|
1053
|
-
if flock --nonblock 200; then
|
|
1054
|
-
GLOBAL_GATE_FD=200
|
|
1055
|
-
return 0
|
|
1056
|
-
else
|
|
1057
|
-
GLOBAL_GATE_FD=""
|
|
1058
|
-
return 1
|
|
1059
|
-
fi
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
# Release the global queue gate
|
|
1063
|
-
release_global_gate() {
|
|
1064
|
-
if [ -n "${GLOBAL_GATE_FD:-}" ]; then
|
|
1065
|
-
flock --unlock "${GLOBAL_GATE_FD}" 2>/dev/null || true
|
|
1066
|
-
exec 200>&-
|
|
1067
|
-
GLOBAL_GATE_FD=""
|
|
1068
|
-
fi
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
1032
|
__night_watch_queue_cleanup() {
|
|
1072
1033
|
local exit_code="${1:-0}"
|
|
1073
|
-
|
|
1074
1034
|
if [ "${NW_QUEUE_CLEANUP_ARMED:-0}" = "1" ]; then
|
|
1075
1035
|
NW_QUEUE_CLEANUP_ARMED=0
|
|
1076
1036
|
complete_queued_job
|
|
1077
1037
|
dispatch_next_queued_job
|
|
1078
|
-
release_global_gate
|
|
1079
1038
|
fi
|
|
1080
|
-
|
|
1081
1039
|
return "${exit_code}"
|
|
1082
1040
|
}
|
|
1083
1041
|
|
|
@@ -1090,6 +1048,35 @@ arm_global_queue_cleanup() {
|
|
|
1090
1048
|
append_exit_trap "__night_watch_queue_cleanup \$?"
|
|
1091
1049
|
}
|
|
1092
1050
|
|
|
1051
|
+
# Atomically claim a queue slot or enqueue for later dispatch.
|
|
1052
|
+
# Uses DB transaction (via `queue claim` CLI) for atomicity — no flock needed.
|
|
1053
|
+
# Sets NW_QUEUE_ENTRY_ID on success and arms the cleanup trap.
|
|
1054
|
+
# Calls enqueue_job and exits 0 if no slot is available.
|
|
1055
|
+
claim_or_enqueue() {
|
|
1056
|
+
local script_type="${1:?script_type required}"
|
|
1057
|
+
local project_dir="${2:?project_dir required}"
|
|
1058
|
+
local provider_key
|
|
1059
|
+
provider_key=$(resolve_provider_key "${project_dir}" "${script_type}")
|
|
1060
|
+
|
|
1061
|
+
local cli_bin
|
|
1062
|
+
cli_bin=$(resolve_night_watch_cli) || {
|
|
1063
|
+
log "ERROR: Cannot resolve night-watch CLI for claim"
|
|
1064
|
+
return 1
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
local claim_id
|
|
1068
|
+
if claim_id=$("${cli_bin}" queue claim "${script_type}" "${project_dir}" --provider-key "${provider_key}" 2>/dev/null); then
|
|
1069
|
+
NW_QUEUE_ENTRY_ID="${claim_id}"
|
|
1070
|
+
export NW_QUEUE_ENTRY_ID
|
|
1071
|
+
arm_global_queue_cleanup
|
|
1072
|
+
return 0
|
|
1073
|
+
else
|
|
1074
|
+
enqueue_job "${script_type}" "${project_dir}"
|
|
1075
|
+
emit_result "queued"
|
|
1076
|
+
exit 0
|
|
1077
|
+
fi
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1093
1080
|
# Enqueue the current job to the SQLite queue.
|
|
1094
1081
|
# Usage: enqueue_job <job_type> <project_dir>
|
|
1095
1082
|
# Stores project path/name, job type, and relevant NW_* env vars for later dispatch.
|
|
@@ -1115,7 +1102,12 @@ enqueue_job() {
|
|
|
1115
1102
|
|
|
1116
1103
|
log "QUEUE: Enqueueing ${job_type} for ${project_name} (gate busy)"
|
|
1117
1104
|
|
|
1118
|
-
|
|
1105
|
+
local provider_key_arg=()
|
|
1106
|
+
if [ -n "${NW_PROVIDER_KEY:-}" ]; then
|
|
1107
|
+
provider_key_arg=(--provider-key "${NW_PROVIDER_KEY}")
|
|
1108
|
+
fi
|
|
1109
|
+
|
|
1110
|
+
"${cli_bin}" queue enqueue "${job_type}" "${project_dir}" --env "${env_json}" "${provider_key_arg[@]}" >> "${LOG_FILE:-/dev/null}" 2>&1 || {
|
|
1119
1111
|
log "WARN: Failed to enqueue job"
|
|
1120
1112
|
return 1
|
|
1121
1113
|
}
|
|
@@ -1123,6 +1115,18 @@ enqueue_job() {
|
|
|
1123
1115
|
return 0
|
|
1124
1116
|
}
|
|
1125
1117
|
|
|
1118
|
+
# Resolve the provider bucket key for a given project and job type.
|
|
1119
|
+
# Uses the night-watch CLI to compute the canonical bucket key (e.g. claude-native, codex).
|
|
1120
|
+
# Prints the key to stdout, or an empty string if it cannot be resolved.
|
|
1121
|
+
# Usage: resolve_provider_key <project_dir> <job_type>
|
|
1122
|
+
resolve_provider_key() {
|
|
1123
|
+
local project_dir="${1:?project_dir required}"
|
|
1124
|
+
local job_type="${2:?job_type required}"
|
|
1125
|
+
local cli_bin
|
|
1126
|
+
cli_bin=$(resolve_night_watch_cli) || { printf ""; return 0; }
|
|
1127
|
+
"${cli_bin}" queue resolve-key --project "${project_dir}" --job-type "${job_type}" 2>/dev/null || printf ""
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1126
1130
|
# Dispatch the next queued job after the current job completes.
|
|
1127
1131
|
# Picks highest-priority pending job, marks it dispatched, and spawns it.
|
|
1128
1132
|
dispatch_next_queued_job() {
|
|
@@ -1143,16 +1147,6 @@ dispatch_next_queued_job() {
|
|
|
1143
1147
|
"${cli_bin}" queue dispatch --log "${LOG_FILE:-/dev/null}" 2>/dev/null || true
|
|
1144
1148
|
}
|
|
1145
1149
|
|
|
1146
|
-
queue_can_start_now() {
|
|
1147
|
-
local cli_bin
|
|
1148
|
-
cli_bin=$(resolve_night_watch_cli) || {
|
|
1149
|
-
log "WARN: Cannot resolve night-watch CLI for queue slot check"
|
|
1150
|
-
return 0
|
|
1151
|
-
}
|
|
1152
|
-
|
|
1153
|
-
"${cli_bin}" queue can-start >/dev/null 2>&1
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
1150
|
complete_queued_job() {
|
|
1157
1151
|
local queue_entry_id="${NW_QUEUE_ENTRY_ID:-}"
|
|
1158
1152
|
if [ -z "${queue_entry_id}" ]; then
|
|
@@ -17,10 +17,12 @@ PROJECT_DIR="${1:?Usage: $0 /path/to/project}"
|
|
|
17
17
|
PROJECT_NAME=$(basename "${PROJECT_DIR}")
|
|
18
18
|
LOG_DIR="${PROJECT_DIR}/logs"
|
|
19
19
|
LOG_FILE="${LOG_DIR}/plan.log"
|
|
20
|
+
LOCK_FILE=""
|
|
20
21
|
MAX_RUNTIME="${NW_PLAN_MAX_RUNTIME:-1800}"
|
|
21
22
|
MAX_LOG_SIZE="524288" # 512 KB
|
|
22
23
|
PROVIDER_CMD="${NW_PROVIDER_CMD:-claude}"
|
|
23
24
|
PROVIDER_LABEL="${NW_PROVIDER_LABEL:-}"
|
|
25
|
+
SCRIPT_TYPE="planner"
|
|
24
26
|
SCRIPT_START_TIME=$(date +%s)
|
|
25
27
|
|
|
26
28
|
mkdir -p "${LOG_DIR}"
|
|
@@ -53,6 +55,8 @@ if ! ensure_provider_on_path "${PROVIDER_CMD}"; then
|
|
|
53
55
|
exit 1
|
|
54
56
|
fi
|
|
55
57
|
|
|
58
|
+
PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
|
|
59
|
+
LOCK_FILE="/tmp/night-watch-plan-${PROJECT_RUNTIME_KEY}.lock"
|
|
56
60
|
PROVIDER_MODEL_DISPLAY=$(resolve_provider_model_display "${PROVIDER_CMD}" "${PROVIDER_LABEL}")
|
|
57
61
|
PRD_DIR="${NW_PRD_DIR:-docs/PRDs}"
|
|
58
62
|
PLAN_TASK="${NW_PLAN_TASK:-}"
|
|
@@ -62,6 +66,25 @@ log_separator
|
|
|
62
66
|
log "RUN-START: planner invoked project=${PROJECT_DIR} provider=${PROVIDER_CMD} dry_run=${NW_DRY_RUN:-0}"
|
|
63
67
|
log "CONFIG: max_runtime=${MAX_RUNTIME}s prd_dir=${PRD_DIR}"
|
|
64
68
|
|
|
69
|
+
if ! acquire_lock "${LOCK_FILE}"; then
|
|
70
|
+
exit 0
|
|
71
|
+
fi
|
|
72
|
+
# ── Global Job Queue Gate ────────────────────────────────────────────────────
|
|
73
|
+
# Atomically claim a DB slot or enqueue for later dispatch — no flock needed.
|
|
74
|
+
if [ "${NW_QUEUE_ENABLED:-0}" = "1" ]; then
|
|
75
|
+
if [ "${NW_QUEUE_DISPATCHED:-0}" = "1" ]; then
|
|
76
|
+
arm_global_queue_cleanup
|
|
77
|
+
else
|
|
78
|
+
claim_or_enqueue "${SCRIPT_TYPE}" "${PROJECT_DIR}"
|
|
79
|
+
fi
|
|
80
|
+
fi
|
|
81
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
|
82
|
+
cleanup_on_exit() {
|
|
83
|
+
rm -f "${LOCK_FILE}"
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
trap cleanup_on_exit EXIT
|
|
87
|
+
|
|
65
88
|
# Dry-run mode
|
|
66
89
|
if [ "${NW_DRY_RUN:-0}" = "1" ]; then
|
|
67
90
|
echo "=== Dry Run: PRD Planner ==="
|
|
@@ -82,9 +82,6 @@ else
|
|
|
82
82
|
LOCK_FILE="${GLOBAL_LOCK_FILE}"
|
|
83
83
|
fi
|
|
84
84
|
|
|
85
|
-
# ── Global Job Queue Gate ────────────────────────────────────────────────────
|
|
86
|
-
# Acquire global gate before per-project lock to serialize jobs across projects.
|
|
87
|
-
# When gate is busy, enqueue the job and exit cleanly.
|
|
88
85
|
SCRIPT_TYPE="reviewer"
|
|
89
86
|
|
|
90
87
|
emit_result() {
|
|
@@ -106,24 +103,12 @@ extract_review_score_from_text() {
|
|
|
106
103
|
}
|
|
107
104
|
|
|
108
105
|
# ── Global Job Queue Gate ────────────────────────────────────────────────────
|
|
109
|
-
#
|
|
110
|
-
# When gate is busy, enqueue the job and exit cleanly.
|
|
106
|
+
# Atomically claim a DB slot or enqueue for later dispatch — no flock needed.
|
|
111
107
|
if [ "${NW_QUEUE_ENABLED:-0}" = "1" ]; then
|
|
112
108
|
if [ "${NW_QUEUE_DISPATCHED:-0}" = "1" ]; then
|
|
113
109
|
arm_global_queue_cleanup
|
|
114
|
-
elif acquire_global_gate; then
|
|
115
|
-
if queue_can_start_now; then
|
|
116
|
-
arm_global_queue_cleanup
|
|
117
|
-
else
|
|
118
|
-
release_global_gate
|
|
119
|
-
enqueue_job "${SCRIPT_TYPE}" "${PROJECT_DIR}"
|
|
120
|
-
emit_result "queued"
|
|
121
|
-
exit 0
|
|
122
|
-
fi
|
|
123
110
|
else
|
|
124
|
-
|
|
125
|
-
emit_result "queued"
|
|
126
|
-
exit 0
|
|
111
|
+
claim_or_enqueue "${SCRIPT_TYPE}" "${PROJECT_DIR}"
|
|
127
112
|
fi
|
|
128
113
|
fi
|
|
129
114
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
@@ -56,24 +56,12 @@ emit_result() {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
# ── Global Job Queue Gate ────────────────────────────────────────────────────
|
|
59
|
-
#
|
|
60
|
-
# When gate is busy, enqueue the job and exit cleanly.
|
|
59
|
+
# Atomically claim a DB slot or enqueue for later dispatch — no flock needed.
|
|
61
60
|
if [ "${NW_QUEUE_ENABLED:-0}" = "1" ]; then
|
|
62
61
|
if [ "${NW_QUEUE_DISPATCHED:-0}" = "1" ]; then
|
|
63
62
|
arm_global_queue_cleanup
|
|
64
|
-
elif acquire_global_gate; then
|
|
65
|
-
if queue_can_start_now; then
|
|
66
|
-
arm_global_queue_cleanup
|
|
67
|
-
else
|
|
68
|
-
release_global_gate
|
|
69
|
-
enqueue_job "${SCRIPT_TYPE}" "${PROJECT_DIR}"
|
|
70
|
-
emit_result "queued"
|
|
71
|
-
exit 0
|
|
72
|
-
fi
|
|
73
63
|
else
|
|
74
|
-
|
|
75
|
-
emit_result "queued"
|
|
76
|
-
exit 0
|
|
64
|
+
claim_or_enqueue "${SCRIPT_TYPE}" "${PROJECT_DIR}"
|
|
77
65
|
fi
|
|
78
66
|
fi
|
|
79
67
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
@@ -61,23 +61,13 @@ log "CONFIG: max_runtime=${MAX_RUNTIME}s roadmap_path=${NW_ROADMAP_PATH:-ROADMAP
|
|
|
61
61
|
if ! acquire_lock "${LOCK_FILE}"; then
|
|
62
62
|
exit 0
|
|
63
63
|
fi
|
|
64
|
-
# ── Global Job Queue Gate
|
|
64
|
+
# ── Global Job Queue Gate ────────────────────────────────────────────────────
|
|
65
|
+
# Atomically claim a DB slot or enqueue for later dispatch — no flock needed.
|
|
65
66
|
if [ "${NW_QUEUE_ENABLED:-0}" = "1" ]; then
|
|
66
67
|
if [ "${NW_QUEUE_DISPATCHED:-0}" = "1" ]; then
|
|
67
68
|
arm_global_queue_cleanup
|
|
68
|
-
elif acquire_global_gate; then
|
|
69
|
-
if queue_can_start_now; then
|
|
70
|
-
arm_global_queue_cleanup
|
|
71
|
-
else
|
|
72
|
-
release_global_gate
|
|
73
|
-
enqueue_job "slicer" "${PROJECT_DIR}"
|
|
74
|
-
emit_result "queued"
|
|
75
|
-
exit 0
|
|
76
|
-
fi
|
|
77
69
|
else
|
|
78
|
-
|
|
79
|
-
emit_result "queued"
|
|
80
|
-
exit 0
|
|
70
|
+
claim_or_enqueue "slicer" "${PROJECT_DIR}"
|
|
81
71
|
fi
|
|
82
72
|
fi
|
|
83
73
|
# ──────────────────────────────────────────────────────────────────────────────
|