nightpay 0.3.2 → 0.3.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.

Potentially problematic release.


This version of nightpay might be problematic. Click here for more details.

@@ -0,0 +1,428 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ usage() {
5
+ cat <<'EOF'
6
+ Usage:
7
+ bash bin/deploy-hetzner-ci.sh \
8
+ --host <hostname-or-ip> \
9
+ --ssh-key <path-to-private-key> \
10
+ [--ssh-port 22] \
11
+ [--remote-dir /opt/nightpay] \
12
+ [--bridge-dir /opt/nightpay-bridge] \
13
+ [--masumi-dir /opt/masumi-services-dev-quickstart] \
14
+ [--ui-port 3333] \
15
+ [--mip-port 8090] \
16
+ [--skip-npm-install] \
17
+ [--skip-proof-recreate] \
18
+ [--skip-masumi-recreate]
19
+
20
+ This script is intended for CI/CD usage.
21
+ It syncs the current committed HEAD to the server, restarts NightPay services,
22
+ recreates Docker services, and fails fast on health-check failures.
23
+ EOF
24
+ }
25
+
26
+ HOST=""
27
+ SSH_KEY=""
28
+ SSH_PORT="22"
29
+ REMOTE_DIR="/opt/nightpay"
30
+ BRIDGE_DIR="/opt/nightpay-bridge"
31
+ MASUMI_DIR="/opt/masumi-services-dev-quickstart"
32
+ SKIP_NPM_INSTALL=0
33
+ SKIP_PROOF_RECREATE=0
34
+ SKIP_MASUMI_RECREATE=0
35
+ UI_PORT=""
36
+ MIP_PORT=""
37
+
38
+ while [[ $# -gt 0 ]]; do
39
+ case "$1" in
40
+ --host)
41
+ HOST="${2:-}"
42
+ shift 2
43
+ ;;
44
+ --ssh-key)
45
+ SSH_KEY="${2:-}"
46
+ shift 2
47
+ ;;
48
+ --ssh-port)
49
+ SSH_PORT="${2:-}"
50
+ shift 2
51
+ ;;
52
+ --remote-dir)
53
+ REMOTE_DIR="${2:-}"
54
+ shift 2
55
+ ;;
56
+ --bridge-dir)
57
+ BRIDGE_DIR="${2:-}"
58
+ shift 2
59
+ ;;
60
+ --masumi-dir)
61
+ MASUMI_DIR="${2:-}"
62
+ shift 2
63
+ ;;
64
+ --skip-npm-install)
65
+ SKIP_NPM_INSTALL=1
66
+ shift
67
+ ;;
68
+ --skip-proof-recreate)
69
+ SKIP_PROOF_RECREATE=1
70
+ shift
71
+ ;;
72
+ --skip-masumi-recreate)
73
+ SKIP_MASUMI_RECREATE=1
74
+ shift
75
+ ;;
76
+ --ui-port)
77
+ UI_PORT="${2:-}"
78
+ shift 2
79
+ ;;
80
+ --mip-port)
81
+ MIP_PORT="${2:-}"
82
+ shift 2
83
+ ;;
84
+ -h|--help)
85
+ usage
86
+ exit 0
87
+ ;;
88
+ *)
89
+ echo "ERROR: unknown arg '$1'" >&2
90
+ usage
91
+ exit 1
92
+ ;;
93
+ esac
94
+ done
95
+
96
+ if [[ -z "$HOST" || -z "$SSH_KEY" ]]; then
97
+ usage
98
+ exit 1
99
+ fi
100
+
101
+ if [[ ! -f "$SSH_KEY" ]]; then
102
+ echo "ERROR: SSH key not found: $SSH_KEY" >&2
103
+ exit 1
104
+ fi
105
+
106
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
107
+ SSH_OPTS=(
108
+ -i "$SSH_KEY"
109
+ -p "$SSH_PORT"
110
+ -o BatchMode=yes
111
+ -o StrictHostKeyChecking=accept-new
112
+ -o ConnectTimeout=20
113
+ -o ServerAliveInterval=15
114
+ -o ServerAliveCountMax=4
115
+ )
116
+
117
+ run_ssh() {
118
+ local remote_cmd="$1"
119
+ ssh "${SSH_OPTS[@]}" "root@${HOST}" "$remote_cmd"
120
+ }
121
+
122
+ echo "[1/9] Verify SSH connectivity..."
123
+ run_ssh "echo connected: \$(hostname)"
124
+
125
+ echo "[2/9] Verify server architecture..."
126
+ ARCH="$(run_ssh "uname -m")"
127
+ if [[ "$ARCH" != "x86_64" ]]; then
128
+ echo "ERROR: expected x86_64 server for Masumi compatibility; found: $ARCH" >&2
129
+ exit 1
130
+ fi
131
+
132
+ echo "[3/9] Backup remote env files..."
133
+ run_ssh "\
134
+ set -euo pipefail; \
135
+ mkdir -p '${REMOTE_DIR}/.backups'; \
136
+ ts=\$(date +%Y%m%d-%H%M%S); \
137
+ for f in '${REMOTE_DIR}/.agent-playground.env' '${REMOTE_DIR}/.env' '${BRIDGE_DIR}/.env' '${MASUMI_DIR}/.env'; do \
138
+ if [[ -f \"\$f\" ]]; then cp \"\$f\" '${REMOTE_DIR}/.backups/'\"\$(basename \"\$f\")\"'.'\"\$ts\"; fi; \
139
+ done; \
140
+ echo backup_timestamp=\$ts"
141
+
142
+ echo "[4/9] Stop NightPay services before sync..."
143
+ run_ssh "\
144
+ set -euo pipefail; \
145
+ if [[ ! -d '${REMOTE_DIR}' ]]; then exit 0; fi; \
146
+ if id -u deploy >/dev/null 2>&1; then \
147
+ if [[ -f '${REMOTE_DIR}/scripts/agent-playground-setup.sh' ]]; then \
148
+ su - deploy -c \"cd '${REMOTE_DIR}' && bash scripts/agent-playground-setup.sh stop || true\"; \
149
+ fi; \
150
+ pkill -u deploy -f '${REMOTE_DIR}/skills/nightpay/scripts/mip003-server.sh' || true; \
151
+ pkill -u deploy -f '${REMOTE_DIR}/ui/node_modules/.bin/vite' || true; \
152
+ fi; \
153
+ rm -f '${REMOTE_DIR}/.agent-playground/run/'*.pid || true; \
154
+ rm -rf '${REMOTE_DIR}/ui/.vite' '${REMOTE_DIR}/ui/node_modules/.vite' || true"
155
+
156
+ echo "[5/9] Sync tracked commit to ${HOST}:${REMOTE_DIR}..."
157
+ TMP_SYNC_DIR="$(mktemp -d)"
158
+ cleanup_tmp() { rm -rf "$TMP_SYNC_DIR"; }
159
+ trap cleanup_tmp EXIT
160
+
161
+ git -C "$ROOT_DIR" archive --format=tar HEAD | tar -xf - -C "$TMP_SYNC_DIR"
162
+
163
+ # If ui/ exists locally (for example checked-out private submodule), include it in deploy payload.
164
+ if [[ -f "$ROOT_DIR/ui/package.json" ]]; then
165
+ mkdir -p "$TMP_SYNC_DIR/ui"
166
+ tar -C "$ROOT_DIR/ui" \
167
+ --exclude=.git \
168
+ --exclude=node_modules \
169
+ --exclude=dist \
170
+ -cf - . | tar -xf - -C "$TMP_SYNC_DIR/ui"
171
+ fi
172
+
173
+ tar -C "$TMP_SYNC_DIR" -cf - . \
174
+ | ssh "${SSH_OPTS[@]}" "root@${HOST}" "\
175
+ set -euo pipefail; \
176
+ mkdir -p '${REMOTE_DIR}'; \
177
+ find '${REMOTE_DIR}' -mindepth 1 -maxdepth 1 \
178
+ ! -name '.backups' \
179
+ ! -name '.agent-playground' \
180
+ ! -name '.agent-playground.env' \
181
+ ! -name '.env' \
182
+ -exec rm -rf {} +; \
183
+ tar -xf - -C '${REMOTE_DIR}'; \
184
+ if ! id -u deploy >/dev/null 2>&1; then useradd -m -s /bin/bash -G sudo,docker deploy; fi; \
185
+ chown -R deploy:deploy '${REMOTE_DIR}'; \
186
+ find '${REMOTE_DIR}' -type f -name '*.sh' -exec sed -i 's/\r$//' {} +"
187
+
188
+ cleanup_tmp
189
+ trap - EXIT
190
+
191
+ echo "[6/9] Restart NightPay services..."
192
+ ssh "${SSH_OPTS[@]}" "root@${HOST}" \
193
+ "REMOTE_DIR='${REMOTE_DIR}' SKIP_NPM_INSTALL='${SKIP_NPM_INSTALL}' UI_PORT='${UI_PORT}' MIP_PORT='${MIP_PORT}' SKIP_MASUMI_RECREATE='${SKIP_MASUMI_RECREATE}' bash -s" <<'REMOTE'
194
+ set -euo pipefail
195
+
196
+ if [[ "$SKIP_NPM_INSTALL" != "1" ]]; then
197
+ su - deploy -c "cd '$REMOTE_DIR' && npm install --no-audit --no-fund"
198
+ if [[ -f "$REMOTE_DIR/ui/package.json" ]]; then
199
+ su - deploy -c "cd '$REMOTE_DIR/ui' && npm install --no-audit --no-fund"
200
+ else
201
+ echo "WARN: $REMOTE_DIR/ui/package.json missing; skipping UI npm install."
202
+ fi
203
+ fi
204
+
205
+ if [[ ! -f "$REMOTE_DIR/.agent-playground.env" ]]; then
206
+ if [[ "$SKIP_MASUMI_RECREATE" == "1" && "$REMOTE_DIR" != "/opt/nightpay" && -f "/opt/nightpay/.agent-playground.env" ]]; then
207
+ cp /opt/nightpay/.agent-playground.env "$REMOTE_DIR/.agent-playground.env"
208
+ chown deploy:deploy "$REMOTE_DIR/.agent-playground.env" || true
209
+ else
210
+ su - deploy -c "cd '$REMOTE_DIR' && bash scripts/agent-playground-setup.sh init"
211
+ fi
212
+ fi
213
+
214
+ if [[ "$SKIP_MASUMI_RECREATE" == "1" && -f "/opt/nightpay/.agent-playground.env" ]]; then
215
+ python3 - <<'PY'
216
+ from pathlib import Path
217
+ import os
218
+
219
+ remote_env = Path(os.environ["REMOTE_DIR"]) / ".agent-playground.env"
220
+ source_env = Path("/opt/nightpay/.agent-playground.env")
221
+ if not remote_env.exists() or not source_env.exists():
222
+ raise SystemExit(0)
223
+
224
+ def parse(lines):
225
+ out = {}
226
+ for line in lines:
227
+ if not line.startswith("export "):
228
+ continue
229
+ if "=" not in line:
230
+ continue
231
+ key, value = line.split("=", 1)
232
+ key = key.replace("export ", "", 1).strip()
233
+ out[key] = value.strip().strip('"').strip("'")
234
+ return out
235
+
236
+ def is_placeholder(value: str) -> bool:
237
+ if not value:
238
+ return True
239
+ lowered = value.lower()
240
+ return ("<fill-in" in lowered) or (value == "your-key")
241
+
242
+ remote_lines = remote_env.read_text().splitlines()
243
+ source_lines = source_env.read_text().splitlines()
244
+ remote_vals = parse(remote_lines)
245
+ source_vals = parse(source_lines)
246
+
247
+ remote_key = remote_vals.get("MASUMI_API_KEY", "")
248
+ source_key = source_vals.get("MASUMI_API_KEY", "")
249
+
250
+ if not is_placeholder(remote_key):
251
+ raise SystemExit(0)
252
+ if is_placeholder(source_key):
253
+ raise SystemExit(0)
254
+
255
+ updated = False
256
+ out = []
257
+ for line in remote_lines:
258
+ if line.startswith("export MASUMI_API_KEY="):
259
+ out.append(f'export MASUMI_API_KEY="{source_key}"')
260
+ updated = True
261
+ else:
262
+ out.append(line)
263
+ if not updated:
264
+ out.append(f'export MASUMI_API_KEY="{source_key}"')
265
+
266
+ remote_env.write_text("\n".join(out) + "\n")
267
+ PY
268
+ chown deploy:deploy "$REMOTE_DIR/.agent-playground.env" || true
269
+ fi
270
+
271
+ if [[ -n "${UI_PORT:-}" || -n "${MIP_PORT:-}" ]]; then
272
+ python3 - <<'PY'
273
+ from pathlib import Path
274
+ import os
275
+
276
+ env_path = Path(os.environ["REMOTE_DIR"]) / ".agent-playground.env"
277
+ if not env_path.exists():
278
+ raise SystemExit(f"Missing env file: {env_path}")
279
+
280
+ ui_port = os.environ.get("UI_PORT", "").strip()
281
+ mip_port = os.environ.get("MIP_PORT", "").strip()
282
+ updates = {}
283
+ if ui_port:
284
+ updates["export UI_PORT"] = f"\"{ui_port}\""
285
+ if mip_port:
286
+ updates["export MIP_PORT"] = f"\"{mip_port}\""
287
+
288
+ if updates:
289
+ lines = env_path.read_text().splitlines()
290
+ out = []
291
+ seen = set()
292
+ for line in lines:
293
+ replaced = False
294
+ for key, value in updates.items():
295
+ if line.startswith(key + "="):
296
+ out.append(f"{key}={value}")
297
+ seen.add(key)
298
+ replaced = True
299
+ break
300
+ if not replaced:
301
+ out.append(line)
302
+ for key, value in updates.items():
303
+ if key not in seen:
304
+ out.append(f"{key}={value}")
305
+ env_path.write_text("\n".join(out) + "\n")
306
+ PY
307
+ chown deploy:deploy "$REMOTE_DIR/.agent-playground.env" || true
308
+ fi
309
+
310
+ su - deploy -c "cd '$REMOTE_DIR' && bash scripts/agent-playground-setup.sh stop || true"
311
+
312
+ # Best-effort cleanup for stale processes that survived previous stop operations.
313
+ pkill -u deploy -f "$REMOTE_DIR/skills/nightpay/scripts/mip003-server.sh" || true
314
+ pkill -u deploy -f "$REMOTE_DIR/ui/node_modules/.bin/vite" || true
315
+ rm -f "$REMOTE_DIR/.agent-playground/run/"*.pid || true
316
+
317
+ mip_port="${MIP_PORT:-8090}"
318
+ ui_port="${UI_PORT:-3333}"
319
+
320
+ # Kill stale listeners by port to avoid bind conflicts on restart.
321
+ if command -v ss >/dev/null 2>&1; then
322
+ for port in "$mip_port" "$ui_port"; do
323
+ pids="$(ss -ltnp "( sport = :$port )" 2>/dev/null | awk -F'pid=' 'NR>1 {split($2,a,","); print a[1]}' | sort -u)"
324
+ if [[ -n "$pids" ]]; then
325
+ kill $pids || true
326
+ sleep 1
327
+ pids_after="$(ss -ltnp "( sport = :$port )" 2>/dev/null | awk -F'pid=' 'NR>1 {split($2,a,","); print a[1]}' | sort -u)"
328
+ if [[ -n "$pids_after" ]]; then
329
+ kill -9 $pids_after || true
330
+ fi
331
+ fi
332
+ done
333
+ fi
334
+
335
+ # Clear Vite caches to avoid stale file metadata and permission drift after sync.
336
+ rm -rf "$REMOTE_DIR/ui/.vite" "$REMOTE_DIR/ui/node_modules/.vite" || true
337
+
338
+ su - deploy -c "cd '$REMOTE_DIR' && bash scripts/agent-playground-setup.sh start"
339
+ su - deploy -c "cd '$REMOTE_DIR' && bash scripts/agent-playground-setup.sh doctor"
340
+ REMOTE
341
+
342
+ if [[ "$SKIP_PROOF_RECREATE" == "1" ]]; then
343
+ echo "[7/9] Skipping proof-server recreate (--skip-proof-recreate)."
344
+ else
345
+ echo "[7/9] Recreate proof-server Docker stack..."
346
+ ssh "${SSH_OPTS[@]}" "root@${HOST}" \
347
+ "BRIDGE_DIR='${BRIDGE_DIR}' bash -s" <<'REMOTE'
348
+ set -euo pipefail
349
+
350
+ if [[ ! -f "$BRIDGE_DIR/docker-compose.yml" ]]; then
351
+ echo "bridge compose not found at $BRIDGE_DIR; skipping proof-server recreate"
352
+ exit 0
353
+ fi
354
+
355
+ cd "$BRIDGE_DIR"
356
+ docker compose pull || true
357
+
358
+ if ! docker compose up -d --force-recreate --pull never; then
359
+ if ! docker image inspect ghcr.io/midnight-ntwrk/proof-server:4.0.0 >/dev/null 2>&1 \
360
+ && docker image inspect midnightnetwork/proof-server:latest >/dev/null 2>&1; then
361
+ docker tag midnightnetwork/proof-server:latest ghcr.io/midnight-ntwrk/proof-server:4.0.0
362
+ fi
363
+ docker compose up -d --force-recreate --pull never
364
+ fi
365
+ REMOTE
366
+ fi
367
+
368
+ if [[ "$SKIP_MASUMI_RECREATE" == "1" ]]; then
369
+ echo "[8/9] Skipping Masumi recreate (--skip-masumi-recreate)."
370
+ else
371
+ echo "[8/9] Recreate Masumi API containers..."
372
+ ssh "${SSH_OPTS[@]}" "root@${HOST}" \
373
+ "MASUMI_DIR='${MASUMI_DIR}' bash -s" <<'REMOTE'
374
+ set -euo pipefail
375
+
376
+ if [[ ! -f "$MASUMI_DIR/docker-compose.yml" ]]; then
377
+ echo "masumi compose not found at $MASUMI_DIR; skipping Masumi recreate"
378
+ exit 0
379
+ fi
380
+
381
+ cd "$MASUMI_DIR"
382
+
383
+ # Keep DB volumes stable; only recreate API containers unless DB services are down.
384
+ docker compose up -d postgres-payment postgres-registry
385
+ docker compose pull payment-service registry-service || true
386
+ docker compose up -d --no-deps --force-recreate payment-service registry-service
387
+
388
+ for i in $(seq 1 90); do
389
+ p="$(curl -fsS -m 2 http://localhost:3001/api/v1/health || true)"
390
+ r="$(curl -fsS -m 2 http://localhost:3000/api/v1/health || true)"
391
+ if [[ -n "$p" && -n "$r" ]]; then
392
+ echo "masumi_payment=$p"
393
+ echo "masumi_registry=$r"
394
+ exit 0
395
+ fi
396
+ sleep 2
397
+ done
398
+
399
+ echo "ERROR: Masumi health check failed after recreate." >&2
400
+ docker compose ps >&2
401
+ docker compose logs --tail 80 payment-service registry-service >&2
402
+ exit 1
403
+ REMOTE
404
+ fi
405
+
406
+ MIP_PORT_CHECK="${MIP_PORT:-8090}"
407
+ UI_PORT_CHECK="${UI_PORT:-3333}"
408
+
409
+ echo "[9/9] Final health checks..."
410
+ run_ssh "\
411
+ set -euo pipefail; \
412
+ ui_enabled='1'; \
413
+ if [[ -f '${REMOTE_DIR}/.agent-playground.env' ]]; then source '${REMOTE_DIR}/.agent-playground.env'; ui_enabled=\${ENABLE_UI:-1}; fi; \
414
+ echo -n 'mip='; curl -fsS http://localhost:${MIP_PORT_CHECK}/availability; echo; \
415
+ if [[ \"\$ui_enabled\" == '1' && -f '${REMOTE_DIR}/ui/package.json' ]]; then \
416
+ ui_code=\$(curl -sS -o /dev/null -w '%{http_code}' http://localhost:${UI_PORT_CHECK}/); \
417
+ echo ui_status=\$ui_code; \
418
+ if [[ \"\$ui_code\" != '200' ]]; then echo 'ERROR: UI health check failed' >&2; exit 1; fi; \
419
+ else \
420
+ echo 'ui_status=skipped'; \
421
+ fi; \
422
+ if [[ '${SKIP_MASUMI_RECREATE}' != '1' && -f '${MASUMI_DIR}/docker-compose.yml' ]]; then \
423
+ echo -n 'payment='; curl -fsS http://localhost:3001/api/v1/health; echo; \
424
+ echo -n 'registry='; curl -fsS http://localhost:3000/api/v1/health; echo; \
425
+ fi; \
426
+ docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}'"
427
+
428
+ echo "Deploy completed successfully."
@@ -0,0 +1,10 @@
1
+ {
2
+ "id": "nightpay",
3
+ "name": "NightPay",
4
+ "description": "Anonymous community bounty pools \u2014 Midnight ZK proofs + Masumi settlement + Cardano finality. Skills auto-loaded from skills/nightpay/.",
5
+ "version": "0.3.11",
6
+ "configSchema": {},
7
+ "skills": [
8
+ "skills/nightpay"
9
+ ]
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nightpay",
3
- "version": "0.3.2",
3
+ "version": "0.3.11",
4
4
  "description": "Anonymous community bounties for AI agents. Midnight ZK proofs + Masumi settlement + Cardano finality.",
5
5
  "keywords": [
6
6
  "bounties",
@@ -21,14 +21,16 @@
21
21
  "type": "git",
22
22
  "url": "git+https://github.com/nightpay/nightpay.git"
23
23
  },
24
- "license": "Apache-2.0",
24
+ "license": "AGPL-3.0-only",
25
25
  "author": "nightpay contributors",
26
26
  "type": "module",
27
27
  "bin": {
28
28
  "nightpay": "bin/cli.js"
29
29
  },
30
30
  "scripts": {
31
- "test": "bash test/smoke.sh"
31
+ "test": "node test/run-quality-gate.mjs",
32
+ "test:quality": "node test/run-quality-gate.mjs",
33
+ "test:smoke": "node test/run-smoke-test.mjs"
32
34
  },
33
35
  "files": [
34
36
  "bin/",
@@ -36,6 +38,14 @@
36
38
  "README.md",
37
39
  "LICENSE",
38
40
  "nightpay_sdk.py",
39
- "scripts/"
40
- ]
41
+ "scripts/",
42
+ "plugin.js",
43
+ "openclaw.plugin.json"
44
+ ],
45
+ "openclaw": {
46
+ "extensions": [
47
+ "./plugin.js"
48
+ ],
49
+ "type": "skillBundle"
50
+ }
41
51
  }