@orchestrator-claude/definitions 3.12.1 → 3.12.3

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.
@@ -1,180 +1,202 @@
1
1
  #!/bin/bash
2
- # Post-Phase Checkpoint Hook (v2 — REST API)
3
- # Defense-in-depth: agents create checkpoints via MCP, this is a safety net.
4
- # Creates git checkpoints when workflow phases complete.
2
+ # Post-Phase Checkpoint Hook (RFC-012 compatible)
3
+ # Registers a checkpoint in PostgreSQL via the Orchestrator REST API.
4
+ # Replaces the deprecated orchestrator-index.json approach (TD-105 F-04).
5
5
  #
6
6
  # Triggered by: PostToolUse on Task tool (after agent completes)
7
- # Requirements: git
8
- # Debug mode: ORCH_CHECKPOINT_DEBUG=1 to enable verbose logging
7
+ # Behavior: Infers the just-completed phase from the active workflow state
8
+ # and registers a checkpoint via the REST API.
9
+ #
10
+ # Required env vars:
11
+ # ORCHESTRATOR_API_URL - Base URL (e.g. http://localhost:3000)
12
+ # ORCHESTRATOR_PROJECT_TOKEN - Bearer token for API authentication
13
+ #
14
+ # Optional env vars:
15
+ # ORCHESTRATOR_PROJECT_ID - Project ID for project-scoped workflow query
16
+ # ORCH_CHECKPOINT_DEBUG=1 - Enable verbose logging
17
+ #
18
+ # Always exits 0 (non-blocking).
9
19
 
10
- set -e
20
+ set -euo pipefail
11
21
 
12
- # Configuration
13
22
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
14
- PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
15
- STATE_DIR="$PROJECT_ROOT/.orchestrator/.state"
16
- LOG_FILE="$STATE_DIR/checkpoint-hook.log"
17
- LAST_PHASE_FILE="$STATE_DIR/last-checkpointed-phase"
18
-
19
- # API Configuration
20
- API_URL="${ORCHESTRATOR_API_URL:-http://localhost:3001}"
21
- AUTH_EMAIL="${ORCHESTRATOR_AUTH_EMAIL:-admin@orchestrator.local}"
22
- AUTH_PASSWORD="${ORCHESTRATOR_AUTH_PASSWORD:-admin123}"
23
- PROJECT_ID="${ORCHESTRATOR_PROJECT_ID:-d001d65d-25f5-4d13-b1dc-d006873e949c}"
23
+ STATE_DIR="$SCRIPT_DIR/../../.orchestrator/.state"
24
+ LOG_FILE="${STATE_DIR}/checkpoint-hook.log"
25
+ LAST_PHASE_FILE="${STATE_DIR}/last-checkpointed-phase"
26
+ DEBUG="${ORCH_CHECKPOINT_DEBUG:-0}"
24
27
 
25
28
  mkdir -p "$STATE_DIR"
26
29
 
27
- DEBUG="${ORCH_CHECKPOINT_DEBUG:-0}"
30
+ log_info() { echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] [INFO] $1" >> "$LOG_FILE"; }
31
+ log_warn() { echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] [WARN] $1" >> "$LOG_FILE"; echo "[WARN] $1" >&2; }
32
+ log_debug() { [ "$DEBUG" = "1" ] && echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] [DEBUG] $1" >> "$LOG_FILE" || true; }
28
33
 
29
- log_debug() {
30
- if [ "$DEBUG" = "1" ]; then
31
- echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] [DEBUG] $1" >> "$LOG_FILE"
32
- echo "[DEBUG] $1" >&2
33
- fi
34
- }
34
+ # ── prerequisites ─────────────────────────────────────────────────────────────
35
35
 
36
- log_info() {
37
- echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] [INFO] $1" >> "$LOG_FILE"
38
- if [ "$DEBUG" = "1" ]; then
39
- echo "[INFO] $1" >&2
36
+ check_prerequisites() {
37
+ if ! command -v curl &>/dev/null; then
38
+ log_warn "curl not found. Checkpoints disabled."
39
+ return 1
40
+ fi
41
+ if ! command -v jq &>/dev/null; then
42
+ log_warn "jq not found. Checkpoints disabled."
43
+ return 1
40
44
  fi
45
+ if [ -z "${ORCHESTRATOR_API_URL:-}" ]; then
46
+ log_debug "ORCHESTRATOR_API_URL not set. Checkpoints disabled."
47
+ return 1
48
+ fi
49
+ if [ -z "${ORCHESTRATOR_PROJECT_TOKEN:-}" ]; then
50
+ log_debug "ORCHESTRATOR_PROJECT_TOKEN not set. Checkpoints disabled."
51
+ return 1
52
+ fi
53
+ return 0
41
54
  }
42
55
 
43
- log_error() {
44
- echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] [ERROR] $1" >> "$LOG_FILE"
45
- echo "[ERROR] $1" >&2
46
- }
56
+ # ── API helpers ────────────────────────────────────────────────────────────────
47
57
 
48
- # Auth (shared with other hooks — cached JWT)
49
- get_token() {
50
- local cached="$STATE_DIR/jwt-token"
51
- if [ -f "$cached" ]; then
52
- local age=$(( $(date +%s) - $(stat -c%Y "$cached" 2>/dev/null || stat -f%m "$cached" 2>/dev/null || echo 0) ))
53
- if [ "$age" -lt 3500 ]; then
54
- cat "$cached"; return
55
- fi
56
- fi
57
- local resp
58
- resp=$(curl -s -f -X POST "${API_URL}/api/v1/auth/login" \
58
+ get_active_workflow() {
59
+ local project_id="${ORCHESTRATOR_PROJECT_ID:-}"
60
+ local url="${ORCHESTRATOR_API_URL}/api/v1/workflows?status=in_progress&limit=1"
61
+ [ -n "$project_id" ] && url="${url}&projectId=${project_id}"
62
+
63
+ local tmpfile
64
+ tmpfile=$(mktemp 2>/dev/null) || return 1
65
+
66
+ curl -sf \
67
+ -H "Authorization: Bearer ${ORCHESTRATOR_PROJECT_TOKEN}" \
59
68
  -H "Content-Type: application/json" \
60
- -d "{\"email\":\"${AUTH_EMAIL}\",\"password\":\"${AUTH_PASSWORD}\"}" 2>/dev/null) || return 1
61
- local token
62
- token=$(echo "$resp" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{const j=JSON.parse(d);console.log(j.data?.accessToken||j.accessToken||j.token||'')}catch{console.log('')}})" 2>/dev/null)
63
- [ -n "$token" ] && { echo "$token" > "$cached"; echo "$token"; }
69
+ "${url}" \
70
+ -o "$tmpfile" 2>/dev/null || { rm -f "$tmpfile"; return 1; }
71
+
72
+ local result
73
+ result=$(node -e "
74
+ const fs=require('fs');
75
+ try {
76
+ const d=fs.readFileSync('${tmpfile}','utf8');
77
+ const j=JSON.parse(d);
78
+ const wf=Array.isArray(j)?j[0]:j;
79
+ if(wf&&wf.id){ process.stdout.write(JSON.stringify(wf)); process.exit(0); }
80
+ process.exit(1);
81
+ } catch { process.exit(1); }" 2>/dev/null)
82
+ local rc=$?
83
+ rm -f "$tmpfile"
84
+ [ $rc -eq 0 ] && echo "$result" || return 1
64
85
  }
65
86
 
66
- # Get workflow info from REST API
67
- get_workflow_info() {
68
- local token
69
- token=$(get_token) || return 1
70
- curl -s -f "${API_URL}/api/v1/workflows?status=in_progress" \
71
- -H "Authorization: Bearer $token" \
72
- -H "X-Project-ID: $PROJECT_ID" 2>/dev/null
73
- }
87
+ create_checkpoint() {
88
+ local workflow_id="$1"
89
+ local phase="$2"
90
+ local description="$3"
91
+
92
+ local body
93
+ body=$(jq -n \
94
+ --arg phase "$phase" \
95
+ --arg desc "$description" \
96
+ '{ phase: $phase, description: $desc }')
97
+
98
+ local http_code
99
+ http_code=$(curl -sf -o /dev/null -w "%{http_code}" \
100
+ -X POST \
101
+ -H "Authorization: Bearer ${ORCHESTRATOR_PROJECT_TOKEN}" \
102
+ -H "Content-Type: application/json" \
103
+ -d "$body" \
104
+ "${ORCHESTRATOR_API_URL}/api/v1/workflows/${workflow_id}/checkpoints" 2>/dev/null) || echo ""
74
105
 
75
- # Determine completed phase from current phase
76
- # When currentPhase = PLAN, SPECIFY just completed; etc.
77
- get_completed_phase() {
78
- case "$1" in
79
- plan|PLAN) echo "specify" ;;
80
- tasks|TASKS) echo "plan" ;;
81
- implement|IMPLEMENT) echo "tasks" ;;
82
- completed) echo "implement" ;;
83
- *) echo "" ;;
84
- esac
106
+ echo "$http_code"
85
107
  }
86
108
 
87
- # Create git checkpoint
88
- create_git_checkpoint() {
89
- local phase="$1"
90
-
91
- log_info "Creating git checkpoint for phase: $phase"
109
+ # ── dedup guard ────────────────────────────────────────────────────────────────
92
110
 
93
- git -C "$PROJECT_ROOT" add -A 2>/dev/null || return 1
111
+ get_saved_last_phase() {
112
+ [ -f "$LAST_PHASE_FILE" ] && cat "$LAST_PHASE_FILE" || echo ""
113
+ }
94
114
 
95
- # Check if there are staged changes
96
- if git -C "$PROJECT_ROOT" diff --cached --quiet 2>/dev/null; then
97
- log_info "No changes to commit for phase: $phase"
98
- echo ""
99
- return 0
100
- fi
115
+ save_last_phase() {
116
+ echo "$1" > "$LAST_PHASE_FILE"
117
+ }
101
118
 
102
- local commit_msg="[orchestrator] ${phase}: Auto-checkpoint after ${phase} phase"
119
+ # ── phase inference ────────────────────────────────────────────────────────────
103
120
 
104
- if git -C "$PROJECT_ROOT" commit -m "$commit_msg" --no-verify 2>/dev/null; then
105
- local commit_hash
106
- commit_hash=$(git -C "$PROJECT_ROOT" rev-parse HEAD 2>/dev/null)
107
- log_info "Checkpoint commit created: $commit_hash"
108
- echo "$commit_hash"
109
- else
110
- log_error "Failed to create commit"
111
- echo ""
112
- return 1
113
- fi
121
+ # When workflow is at PLAN, it means SPECIFY just completed, etc.
122
+ get_completed_phase() {
123
+ local current="$1"
124
+ local status="$2"
125
+ case "$status" in
126
+ "completed") echo "implement"; return ;;
127
+ esac
128
+ case "${current,,}" in
129
+ "plan") echo "specify" ;;
130
+ "tasks") echo "plan" ;;
131
+ "implement") echo "tasks" ;;
132
+ *) echo "" ;;
133
+ esac
114
134
  }
115
135
 
116
- # Register checkpoint via REST API
117
- register_checkpoint() {
118
- local wf_id="$1"
119
- local phase="$2"
120
- local commit_hash="$3"
121
- local description="$4"
122
- local token
123
- token=$(get_token) || return 1
124
- curl -s -f -X POST "${API_URL}/api/v1/checkpoints" \
125
- -H "Content-Type: application/json" \
126
- -H "Authorization: Bearer $token" \
127
- -H "X-Project-ID: $PROJECT_ID" \
128
- -d "{\"workflowId\":\"${wf_id}\",\"description\":\"${description}\",\"commitHash\":\"${commit_hash}\"}" >> "$LOG_FILE" 2>&1 || true
129
- }
136
+ # ── main ───────────────────────────────────────────────────────────────────────
130
137
 
131
138
  main() {
132
139
  log_debug "Hook triggered"
133
140
 
134
- # Prerequisites
135
- command -v git &>/dev/null || { log_error "git not found"; exit 0; }
136
- git -C "$PROJECT_ROOT" rev-parse --is-inside-work-tree &>/dev/null || { exit 0; }
137
-
138
- # Get workflow from API
139
- WORKFLOW_RESP=$(get_workflow_info) || { log_debug "No API access"; exit 0; }
140
-
141
- WORKFLOW_DATA=$(echo "$WORKFLOW_RESP" | node -e "
142
- let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{
143
- try{
144
- const j=JSON.parse(d);
145
- const wfs=j.data||j;
146
- const wf=Array.isArray(wfs)?wfs[0]:wfs;
147
- if(!wf){console.log('{}');return;}
148
- console.log(JSON.stringify({id:wf.id||'',phase:wf.currentPhase||wf.phase||'',status:wf.status||''}));
149
- }catch{console.log('{}')}
150
- });
151
- " 2>/dev/null)
141
+ if ! check_prerequisites; then
142
+ exit 0
143
+ fi
152
144
 
153
- WF_ID=$(echo "$WORKFLOW_DATA" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{console.log(JSON.parse(d).id||'')}catch{console.log('')}})" 2>/dev/null)
154
- CURRENT_PHASE=$(echo "$WORKFLOW_DATA" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{console.log(JSON.parse(d).phase||'')}catch{console.log('')}})" 2>/dev/null)
145
+ local workflow_json
146
+ workflow_json=$(get_active_workflow 2>/dev/null) || workflow_json=""
155
147
 
156
- [ -z "$CURRENT_PHASE" ] && { log_debug "No active phase"; exit 0; }
148
+ if [ -z "$workflow_json" ]; then
149
+ log_debug "No active workflow found via API."
150
+ exit 0
151
+ fi
157
152
 
158
- COMPLETED_PHASE=$(get_completed_phase "$CURRENT_PHASE")
159
- [ -z "$COMPLETED_PHASE" ] && { log_debug "No completed phase from: $CURRENT_PHASE"; exit 0; }
153
+ local workflow_id current_phase workflow_status
154
+ workflow_id=$(echo "$workflow_json" | jq -r '.id // empty' 2>/dev/null || echo "")
155
+ current_phase=$(echo "$workflow_json" | jq -r '.currentPhase // empty' 2>/dev/null || echo "")
156
+ workflow_status=$(echo "$workflow_json" | jq -r '.status // empty' 2>/dev/null || echo "")
160
157
 
161
- # Check if already checkpointed
162
- SAVED=$(cat "$LAST_PHASE_FILE" 2>/dev/null || echo "")
163
- [ "$COMPLETED_PHASE" = "$SAVED" ] && { log_debug "Already checkpointed: $COMPLETED_PHASE"; exit 0; }
158
+ if [ -z "$workflow_id" ] || [ -z "$current_phase" ]; then
159
+ log_debug "Could not parse workflow ID or phase."
160
+ exit 0
161
+ fi
164
162
 
165
- log_debug "Current phase: $CURRENT_PHASE, Completed phase: $COMPLETED_PHASE, WF: $WF_ID"
163
+ local completed_phase
164
+ completed_phase=$(get_completed_phase "$current_phase" "$workflow_status")
166
165
 
167
- # Create git checkpoint
168
- COMMIT_HASH=$(create_git_checkpoint "$COMPLETED_PHASE")
166
+ if [ -z "$completed_phase" ]; then
167
+ log_debug "No completed phase inferred from current_phase=${current_phase}."
168
+ exit 0
169
+ fi
169
170
 
170
- if [ -n "$COMMIT_HASH" ]; then
171
- register_checkpoint "$WF_ID" "$COMPLETED_PHASE" "$COMMIT_HASH" "Auto-checkpoint after ${COMPLETED_PHASE} phase"
172
- log_info "Checkpoint: phase=$COMPLETED_PHASE commit=$COMMIT_HASH"
173
- else
174
- log_info "No changes for phase: $COMPLETED_PHASE"
171
+ # Dedup guard — skip if we already checkpointed this phase
172
+ local saved_last
173
+ saved_last=$(get_saved_last_phase)
174
+ if [ "$completed_phase" = "$saved_last" ]; then
175
+ log_debug "Phase ${completed_phase} already checkpointed. Skipping."
176
+ exit 0
175
177
  fi
176
178
 
177
- echo "$COMPLETED_PHASE" > "$LAST_PHASE_FILE"
179
+ local description="Auto-checkpoint after ${completed_phase} phase"
180
+ local http_code
181
+ http_code=$(create_checkpoint "$workflow_id" "$completed_phase" "$description")
182
+
183
+ case "$http_code" in
184
+ 201|200)
185
+ save_last_phase "$completed_phase"
186
+ log_info "Checkpoint registered: workflow=${workflow_id}, phase=${completed_phase}"
187
+ ;;
188
+ 409)
189
+ save_last_phase "$completed_phase"
190
+ log_info "Checkpoint already exists (409): phase=${completed_phase}"
191
+ ;;
192
+ "")
193
+ log_warn "API unreachable. Checkpoint skipped for phase=${completed_phase}."
194
+ ;;
195
+ *)
196
+ log_warn "Unexpected HTTP ${http_code} creating checkpoint for phase=${completed_phase}."
197
+ ;;
198
+ esac
199
+
178
200
  exit 0
179
201
  }
180
202
 
@@ -0,0 +1,41 @@
1
+ #!/bin/bash
2
+ # prompt-orchestrator.sh — ADR-013 Phase 5 Hook (JSON Structured Output)
3
+ # Trigger: UserPromptSubmit
4
+ # Purpose: On every user prompt, inject orchestrator context.
5
+ # If no workflow active, remind about workflow requirement.
6
+ # If workflow active, inject current state.
7
+ #
8
+ # Output: JSON with additionalContext (never blocks prompts)
9
+
10
+ set -euo pipefail
11
+
12
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
+ source "$SCRIPT_DIR/orch-helpers.sh"
14
+
15
+ orch_log "PROMPT-ORCH: UserPromptSubmit triggered"
16
+
17
+ # Find active workflow
18
+ WORKFLOW_ID=$(orch_get_active_workflow 2>/dev/null) || WORKFLOW_ID=""
19
+
20
+ if [ -z "$WORKFLOW_ID" ]; then
21
+ # No workflow — inject reminder (lightweight, no API calls)
22
+ orch_log "PROMPT-ORCH: No active workflow, injecting reminder"
23
+ echo '{"hookSpecificOutput":{"hookEventName":"UserPromptSubmit","additionalContext":"[ORCHESTRATOR] No active workflow. If this request involves creating, modifying, or fixing code, you must start a workflow first (detectWorkflow → startWorkflow). The workflow-guard hook will block writes to src/ without a workflow."}}'
24
+ exit 0
25
+ fi
26
+
27
+ # Active workflow — inject state
28
+ NEXT_ACTION=$(orch_get_next_action "$WORKFLOW_ID" 2>/dev/null) || NEXT_ACTION=""
29
+ HAS_ACTION=$(orch_json_field "$NEXT_ACTION" "hasAction")
30
+ AGENT=$(orch_json_field "$NEXT_ACTION" "pendingAction.agent")
31
+ PA_STATUS=$(orch_json_field "$NEXT_ACTION" "pendingAction.status")
32
+
33
+ if [ "$HAS_ACTION" = "true" ]; then
34
+ CONTEXT="[ORCHESTRATOR] Active workflow: ${WORKFLOW_ID}. Next: invoke '${AGENT}' (${PA_STATUS})."
35
+ else
36
+ CONTEXT="[ORCHESTRATOR] Active workflow: ${WORKFLOW_ID}. No pending actions."
37
+ fi
38
+
39
+ orch_log "PROMPT-ORCH: workflow=$WORKFLOW_ID hasAction=$HAS_ACTION"
40
+ echo "{\"hookSpecificOutput\":{\"hookEventName\":\"UserPromptSubmit\",\"additionalContext\":$(echo "$CONTEXT" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>console.log(JSON.stringify(d)))" 2>/dev/null)}}"
41
+ exit 0
@@ -0,0 +1,54 @@
1
+ #!/bin/bash
2
+ # session-orchestrator.sh — ADR-013 Phase 5 Hook (JSON Structured Output)
3
+ # Trigger: SessionStart
4
+ # Purpose: On session start, check for active workflow and inject context.
5
+ # Helps the LLM resume work from where it left off.
6
+ #
7
+ # Output: JSON with additionalContext containing workflow state
8
+
9
+ set -euo pipefail
10
+
11
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
+ source "$SCRIPT_DIR/orch-helpers.sh"
13
+
14
+ orch_log "SESSION-ORCH: SessionStart hook triggered"
15
+
16
+ # Find active workflow
17
+ WORKFLOW_ID=$(orch_get_active_workflow 2>/dev/null) || WORKFLOW_ID=""
18
+
19
+ if [ -z "$WORKFLOW_ID" ]; then
20
+ orch_log "SESSION-ORCH: No active workflow"
21
+ echo '{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"[ORCHESTRATOR] No active workflow. For feature requests, bug fixes, or refactoring, start with:\n1. mcp__orchestrator-tools__detectWorkflow\n2. mcp__orchestrator-tools__startWorkflow\n3. Follow the nextStep returned\n\nDirect implementation is blocked by the workflow-guard hook."}}'
22
+ exit 0
23
+ fi
24
+
25
+ # Get workflow details
26
+ TOKEN=$(orch_get_token 2>/dev/null) || TOKEN=""
27
+ if [ -z "$TOKEN" ]; then
28
+ echo "{\"hookSpecificOutput\":{\"hookEventName\":\"SessionStart\",\"additionalContext\":\"[ORCHESTRATOR] Active workflow: ${WORKFLOW_ID} (could not fetch details — auth failed).\"}}"
29
+ exit 0
30
+ fi
31
+
32
+ STATUS_RESP=$(curl -sf --max-time 5 "${API_URL}/api/v1/workflows/${WORKFLOW_ID}/status" \
33
+ -H "Authorization: Bearer $TOKEN" \
34
+ -H "X-Project-ID: $PROJECT_ID" 2>/dev/null) || STATUS_RESP=""
35
+
36
+ PHASE=$(orch_json_field "$STATUS_RESP" "currentPhase")
37
+ STATUS=$(orch_json_field "$STATUS_RESP" "status")
38
+ WF_TYPE=$(orch_json_field "$STATUS_RESP" "type")
39
+
40
+ # Get next action
41
+ NEXT_ACTION=$(orch_get_next_action "$WORKFLOW_ID" 2>/dev/null) || NEXT_ACTION=""
42
+ HAS_ACTION=$(orch_json_field "$NEXT_ACTION" "hasAction")
43
+ AGENT=$(orch_json_field "$NEXT_ACTION" "pendingAction.agent")
44
+ PA_STATUS=$(orch_json_field "$NEXT_ACTION" "pendingAction.status")
45
+
46
+ CONTEXT="[ORCHESTRATOR] Active workflow: ${WORKFLOW_ID}\nType: ${WF_TYPE}\nPhase: ${PHASE}\nStatus: ${STATUS}"
47
+
48
+ if [ "$HAS_ACTION" = "true" ]; then
49
+ CONTEXT="${CONTEXT}\nNext action: invoke '${AGENT}' (${PA_STATUS})"
50
+ fi
51
+
52
+ orch_log "SESSION-ORCH: Injecting workflow context (wf=$WORKFLOW_ID phase=$PHASE)"
53
+ echo "{\"hookSpecificOutput\":{\"hookEventName\":\"SessionStart\",\"additionalContext\":$(echo "$CONTEXT" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>console.log(JSON.stringify(d)))" 2>/dev/null)}}"
54
+ exit 0