nightpay 0.1.0 → 0.4.4
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/LICENSE +666 -21
- package/README.md +371 -125
- package/bin/cli.js +527 -24
- package/nightpay_sdk.py +398 -0
- package/openclaw.plugin.json +10 -0
- package/package.json +18 -7
- package/plugin.js +712 -0
- package/skills/nightpay/AGENTS.md +302 -0
- package/skills/nightpay/HEARTBEAT.md +55 -0
- package/skills/nightpay/SKILL.md +420 -61
- package/skills/nightpay/contracts/receipt.compact +358 -97
- package/skills/nightpay/contracts/receipt.stub.compact +55 -0
- package/skills/nightpay/ontology/context.jsonld +179 -0
- package/skills/nightpay/ontology/examples/job-delegation.example.jsonld +50 -0
- package/skills/nightpay/ontology/examples/pool-funded.example.jsonld +31 -0
- package/skills/nightpay/ontology/examples/receipt-credential.example.jsonld +33 -0
- package/skills/nightpay/ontology/ontology.jsonld +396 -0
- package/skills/nightpay/ontology/ontology.md +243 -0
- package/skills/nightpay/openclaw-fragment.json +16 -33
- package/skills/nightpay/rules/content-safety.md +15 -99
- package/skills/nightpay/rules/escrow-safety.md +62 -0
- package/skills/nightpay/rules/privacy-first.md +21 -0
- package/skills/nightpay/scripts/gateway.sh +1007 -133
- package/skills/nightpay/scripts/mip003-server.sh +4739 -93
package/nightpay_sdk.py
ADDED
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
nightpay_sdk.py — Python SDK for NightPay agent integration.
|
|
4
|
+
|
|
5
|
+
Handles setup, env validation, gateway calls, health checks, and self-healing.
|
|
6
|
+
Works with any agent platform or standalone scripts.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
from nightpay_sdk import NightPay
|
|
10
|
+
|
|
11
|
+
np = NightPay() # auto-discovers skill location
|
|
12
|
+
np.validate() # check env + connectivity
|
|
13
|
+
np.stats() # get contract stats
|
|
14
|
+
np.post_bounty("Review PR", 5000) # post a bounty
|
|
15
|
+
|
|
16
|
+
CLI usage:
|
|
17
|
+
python3 nightpay_sdk.py validate # validate env + health
|
|
18
|
+
python3 nightpay_sdk.py stats # get contract stats
|
|
19
|
+
python3 nightpay_sdk.py setup # run full setup
|
|
20
|
+
python3 nightpay_sdk.py doctor # diagnose and fix issues
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import os
|
|
24
|
+
import sys
|
|
25
|
+
import json
|
|
26
|
+
import shutil
|
|
27
|
+
import subprocess
|
|
28
|
+
import urllib.request
|
|
29
|
+
import urllib.error
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
from dataclasses import dataclass, field
|
|
32
|
+
from typing import Optional
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class ValidationResult:
|
|
37
|
+
"""Result of a validation check."""
|
|
38
|
+
name: str
|
|
39
|
+
passed: bool
|
|
40
|
+
message: str
|
|
41
|
+
severity: str = "error" # error, warning, info
|
|
42
|
+
fix_hint: Optional[str] = None
|
|
43
|
+
|
|
44
|
+
def __str__(self):
|
|
45
|
+
icon = "OK" if self.passed else ("WARN" if self.severity == "warning" else "FAIL")
|
|
46
|
+
s = f" [{icon}] {self.name}: {self.message}"
|
|
47
|
+
if not self.passed and self.fix_hint:
|
|
48
|
+
s += f"\n Fix: {self.fix_hint}"
|
|
49
|
+
return s
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass
|
|
53
|
+
class HealthReport:
|
|
54
|
+
"""Complete health check report."""
|
|
55
|
+
checks: list = field(default_factory=list)
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def passed(self) -> bool:
|
|
59
|
+
return all(c.passed for c in self.checks if c.severity == "error")
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def errors(self) -> int:
|
|
63
|
+
return sum(1 for c in self.checks if not c.passed and c.severity == "error")
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def warnings(self) -> int:
|
|
67
|
+
return sum(1 for c in self.checks if not c.passed and c.severity == "warning")
|
|
68
|
+
|
|
69
|
+
def add(self, name, passed, message, severity="error", fix_hint=None):
|
|
70
|
+
self.checks.append(ValidationResult(name, passed, message, severity, fix_hint))
|
|
71
|
+
return self
|
|
72
|
+
|
|
73
|
+
def __str__(self):
|
|
74
|
+
lines = [str(c) for c in self.checks]
|
|
75
|
+
if self.passed:
|
|
76
|
+
lines.append(f"\n NightPay is healthy ({len(self.checks)} checks passed)")
|
|
77
|
+
else:
|
|
78
|
+
lines.append(f"\n Issues: {self.errors} error(s), {self.warnings} warning(s)")
|
|
79
|
+
return "\n".join(lines)
|
|
80
|
+
|
|
81
|
+
def to_dict(self):
|
|
82
|
+
return {
|
|
83
|
+
"healthy": self.passed,
|
|
84
|
+
"errors": self.errors,
|
|
85
|
+
"warnings": self.warnings,
|
|
86
|
+
"checks": [
|
|
87
|
+
{"name": c.name, "passed": c.passed, "message": c.message,
|
|
88
|
+
"severity": c.severity, "fix_hint": c.fix_hint}
|
|
89
|
+
for c in self.checks
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class NightPay:
|
|
95
|
+
"""NightPay SDK — setup, validate, and interact with NightPay."""
|
|
96
|
+
|
|
97
|
+
REQUIRED_ENV = {
|
|
98
|
+
"MASUMI_API_KEY": "Masumi payment API key",
|
|
99
|
+
"OPERATOR_ADDRESS": "64-char hex Midnight operator shielded address",
|
|
100
|
+
"NIGHTPAY_API_URL": "Deployed MIP-003 API base URL",
|
|
101
|
+
"BRIDGE_URL": "Midnight bridge URL",
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
REQUIRED_BINS = ["bash", "curl", "openssl", "sqlite3"]
|
|
105
|
+
|
|
106
|
+
def __init__(self, skill_path: Optional[str] = None):
|
|
107
|
+
"""Initialize NightPay SDK.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
skill_path: Path to nightpay skill directory.
|
|
111
|
+
Auto-discovers if not provided.
|
|
112
|
+
"""
|
|
113
|
+
self.skill_path = Path(skill_path) if skill_path else self._discover_skill()
|
|
114
|
+
self.gateway = self.skill_path / "scripts" / "gateway.sh" if self.skill_path else None
|
|
115
|
+
|
|
116
|
+
def _discover_skill(self) -> Optional[Path]:
|
|
117
|
+
"""Auto-discover skill location."""
|
|
118
|
+
candidates = [
|
|
119
|
+
Path.cwd() / "skills" / "nightpay",
|
|
120
|
+
Path.home() / ".openclaw" / "workspace-nightpay" / "skills" / "nightpay",
|
|
121
|
+
Path(__file__).parent,
|
|
122
|
+
Path(__file__).parent.parent / "skills" / "nightpay",
|
|
123
|
+
]
|
|
124
|
+
for p in candidates:
|
|
125
|
+
if (p / "SKILL.md").exists():
|
|
126
|
+
return p
|
|
127
|
+
# Check for nested structure
|
|
128
|
+
if (p / "skills" / "nightpay" / "SKILL.md").exists():
|
|
129
|
+
return p / "skills" / "nightpay"
|
|
130
|
+
return None
|
|
131
|
+
|
|
132
|
+
# ─── Validation ───────────────────────────────────────────────────────
|
|
133
|
+
|
|
134
|
+
def validate(self, verbose: bool = True) -> HealthReport:
|
|
135
|
+
"""Run full validation: prerequisites, env, connectivity, skill files."""
|
|
136
|
+
report = HealthReport()
|
|
137
|
+
|
|
138
|
+
# Prerequisites
|
|
139
|
+
for b in self.REQUIRED_BINS:
|
|
140
|
+
found = shutil.which(b) is not None
|
|
141
|
+
report.add(f"bin:{b}", found,
|
|
142
|
+
f"{b} found" if found else f"{b} not found",
|
|
143
|
+
fix_hint=f"Install {b} via your package manager")
|
|
144
|
+
|
|
145
|
+
# sha256sum or shasum
|
|
146
|
+
has_hash = shutil.which("sha256sum") or shutil.which("shasum")
|
|
147
|
+
report.add("bin:sha256sum", bool(has_hash),
|
|
148
|
+
"sha256sum/shasum found" if has_hash else "neither found",
|
|
149
|
+
fix_hint="Install coreutils (Linux) or use shasum (macOS)")
|
|
150
|
+
|
|
151
|
+
# Env vars
|
|
152
|
+
for var, desc in self.REQUIRED_ENV.items():
|
|
153
|
+
val = os.environ.get(var, "")
|
|
154
|
+
if not val:
|
|
155
|
+
report.add(f"env:{var}", False, f"not set — {desc}",
|
|
156
|
+
fix_hint=f"export {var}='your-value'")
|
|
157
|
+
elif val == var:
|
|
158
|
+
report.add(f"env:{var}", False,
|
|
159
|
+
f"set to placeholder '{var}' — replace with real value",
|
|
160
|
+
fix_hint=f"export {var}='actual-value-here'")
|
|
161
|
+
else:
|
|
162
|
+
# Extra validation
|
|
163
|
+
if var == "OPERATOR_ADDRESS":
|
|
164
|
+
if len(val) != 64 or not all(c in "0123456789abcdefABCDEF" for c in val):
|
|
165
|
+
report.add(f"env:{var}", False,
|
|
166
|
+
f"doesn't look like 64-char hex (got {len(val)} chars)",
|
|
167
|
+
severity="warning")
|
|
168
|
+
else:
|
|
169
|
+
report.add(f"env:{var}", True,
|
|
170
|
+
f"set ({val[:8]}...{val[-4:]})")
|
|
171
|
+
elif var in ("NIGHTPAY_API_URL", "BRIDGE_URL"):
|
|
172
|
+
if "localhost" in val:
|
|
173
|
+
report.add(f"env:{var}", True, f"set ({val})",
|
|
174
|
+
severity="warning",
|
|
175
|
+
fix_hint="localhost only works if stack runs locally")
|
|
176
|
+
else:
|
|
177
|
+
report.add(f"env:{var}", True, f"set ({val})")
|
|
178
|
+
else:
|
|
179
|
+
report.add(f"env:{var}", True, f"set ({val[:8]}...)")
|
|
180
|
+
|
|
181
|
+
# Skill files
|
|
182
|
+
if self.skill_path and (self.skill_path / "SKILL.md").exists():
|
|
183
|
+
report.add("skill:SKILL.md", True, f"found at {self.skill_path}")
|
|
184
|
+
else:
|
|
185
|
+
report.add("skill:SKILL.md", False, "not found",
|
|
186
|
+
fix_hint="Run: npx nightpay init (or bash setup.sh)")
|
|
187
|
+
|
|
188
|
+
if self.gateway and self.gateway.exists():
|
|
189
|
+
is_exec = os.access(str(self.gateway), os.X_OK)
|
|
190
|
+
report.add("skill:gateway.sh", True,
|
|
191
|
+
f"found {'(executable)' if is_exec else '(NOT executable)'}")
|
|
192
|
+
if not is_exec:
|
|
193
|
+
report.add("skill:gateway.sh:exec", False,
|
|
194
|
+
"gateway.sh is not executable",
|
|
195
|
+
fix_hint=f"chmod +x {self.gateway}")
|
|
196
|
+
else:
|
|
197
|
+
report.add("skill:gateway.sh", False, "not found",
|
|
198
|
+
fix_hint="Reinstall skill files")
|
|
199
|
+
|
|
200
|
+
# Connectivity
|
|
201
|
+
api_url = os.environ.get("NIGHTPAY_API_URL", "")
|
|
202
|
+
if api_url and api_url != "NIGHTPAY_API_URL":
|
|
203
|
+
try:
|
|
204
|
+
req = urllib.request.Request(f"{api_url}/availability",
|
|
205
|
+
method="GET")
|
|
206
|
+
with urllib.request.urlopen(req, timeout=10) as resp:
|
|
207
|
+
report.add("connectivity:api", True,
|
|
208
|
+
f"API reachable ({resp.status})")
|
|
209
|
+
except Exception as e:
|
|
210
|
+
report.add("connectivity:api", False,
|
|
211
|
+
f"API unreachable: {e}",
|
|
212
|
+
severity="warning",
|
|
213
|
+
fix_hint="Check NIGHTPAY_API_URL and ensure stack is running")
|
|
214
|
+
|
|
215
|
+
bridge_url = os.environ.get("BRIDGE_URL", "")
|
|
216
|
+
if bridge_url and bridge_url != "BRIDGE_URL":
|
|
217
|
+
try:
|
|
218
|
+
req = urllib.request.Request(f"{bridge_url}/health",
|
|
219
|
+
method="GET")
|
|
220
|
+
with urllib.request.urlopen(req, timeout=10) as resp:
|
|
221
|
+
report.add("connectivity:bridge", True,
|
|
222
|
+
f"Bridge reachable ({resp.status})")
|
|
223
|
+
except Exception as e:
|
|
224
|
+
report.add("connectivity:bridge", False,
|
|
225
|
+
f"Bridge unreachable: {e}",
|
|
226
|
+
severity="warning",
|
|
227
|
+
fix_hint="Check BRIDGE_URL — on-chain mode may not work")
|
|
228
|
+
|
|
229
|
+
if verbose:
|
|
230
|
+
print(report)
|
|
231
|
+
|
|
232
|
+
return report
|
|
233
|
+
|
|
234
|
+
# ─── Self-healing doctor ──────────────────────────────────────────────
|
|
235
|
+
|
|
236
|
+
def doctor(self, auto_fix: bool = False) -> HealthReport:
|
|
237
|
+
"""Diagnose issues and optionally auto-fix them."""
|
|
238
|
+
report = self.validate(verbose=False)
|
|
239
|
+
|
|
240
|
+
fixes_applied = 0
|
|
241
|
+
|
|
242
|
+
for check in report.checks:
|
|
243
|
+
if check.passed:
|
|
244
|
+
continue
|
|
245
|
+
|
|
246
|
+
if auto_fix:
|
|
247
|
+
# Auto-fix permissions
|
|
248
|
+
if check.name == "skill:gateway.sh:exec" and self.gateway:
|
|
249
|
+
os.chmod(str(self.gateway), 0o755)
|
|
250
|
+
check.passed = True
|
|
251
|
+
check.message = "fixed: chmod +x applied"
|
|
252
|
+
fixes_applied += 1
|
|
253
|
+
|
|
254
|
+
# Auto-fix nested SKILL.md
|
|
255
|
+
if check.name == "skill:SKILL.md" and self.skill_path:
|
|
256
|
+
nested = self.skill_path / "skills" / "nightpay" / "SKILL.md"
|
|
257
|
+
if nested.exists():
|
|
258
|
+
import shutil as sh
|
|
259
|
+
nested_dir = self.skill_path / "skills" / "nightpay"
|
|
260
|
+
for item in nested_dir.iterdir():
|
|
261
|
+
dest = self.skill_path / item.name
|
|
262
|
+
if item.is_dir():
|
|
263
|
+
sh.copytree(str(item), str(dest),
|
|
264
|
+
dirs_exist_ok=True)
|
|
265
|
+
else:
|
|
266
|
+
sh.copy2(str(item), str(dest))
|
|
267
|
+
check.passed = True
|
|
268
|
+
check.message = "fixed: flattened nested skill directory"
|
|
269
|
+
fixes_applied += 1
|
|
270
|
+
|
|
271
|
+
print(report)
|
|
272
|
+
if fixes_applied:
|
|
273
|
+
print(f"\n Auto-fixed {fixes_applied} issue(s)")
|
|
274
|
+
|
|
275
|
+
return report
|
|
276
|
+
|
|
277
|
+
# ─── Gateway commands ─────────────────────────────────────────────────
|
|
278
|
+
|
|
279
|
+
def _run_gateway(self, *args) -> subprocess.CompletedProcess:
|
|
280
|
+
"""Run a gateway.sh command."""
|
|
281
|
+
if not self.gateway or not self.gateway.exists():
|
|
282
|
+
raise FileNotFoundError(
|
|
283
|
+
"gateway.sh not found. Run setup first: bash setup.sh")
|
|
284
|
+
return subprocess.run(
|
|
285
|
+
["bash", str(self.gateway)] + list(args),
|
|
286
|
+
capture_output=True, text=True, timeout=30
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
def stats(self) -> dict:
|
|
290
|
+
"""Get contract statistics."""
|
|
291
|
+
result = self._run_gateway("stats")
|
|
292
|
+
if result.returncode != 0:
|
|
293
|
+
raise RuntimeError(f"stats failed: {result.stderr}")
|
|
294
|
+
try:
|
|
295
|
+
return json.loads(result.stdout)
|
|
296
|
+
except json.JSONDecodeError:
|
|
297
|
+
return {"raw_output": result.stdout.strip()}
|
|
298
|
+
|
|
299
|
+
def post_bounty(self, description: str, amount_specks: int) -> dict:
|
|
300
|
+
"""Post a simple bounty."""
|
|
301
|
+
result = self._run_gateway("post-bounty", description,
|
|
302
|
+
str(amount_specks))
|
|
303
|
+
if result.returncode != 0:
|
|
304
|
+
raise RuntimeError(f"post-bounty failed: {result.stderr}")
|
|
305
|
+
try:
|
|
306
|
+
return json.loads(result.stdout)
|
|
307
|
+
except json.JSONDecodeError:
|
|
308
|
+
return {"raw_output": result.stdout.strip()}
|
|
309
|
+
|
|
310
|
+
def create_pool(self, description: str, contribution_specks: int,
|
|
311
|
+
funding_goal_specks: int) -> dict:
|
|
312
|
+
"""Create a bounty pool."""
|
|
313
|
+
result = self._run_gateway("create-pool", description,
|
|
314
|
+
str(contribution_specks),
|
|
315
|
+
str(funding_goal_specks))
|
|
316
|
+
if result.returncode != 0:
|
|
317
|
+
raise RuntimeError(f"create-pool failed: {result.stderr}")
|
|
318
|
+
try:
|
|
319
|
+
return json.loads(result.stdout)
|
|
320
|
+
except json.JSONDecodeError:
|
|
321
|
+
return {"raw_output": result.stdout.strip()}
|
|
322
|
+
|
|
323
|
+
def find_agent(self, capability: str) -> dict:
|
|
324
|
+
"""Find agents matching a capability query."""
|
|
325
|
+
result = self._run_gateway("find-agent", capability)
|
|
326
|
+
if result.returncode != 0:
|
|
327
|
+
raise RuntimeError(f"find-agent failed: {result.stderr}")
|
|
328
|
+
try:
|
|
329
|
+
return json.loads(result.stdout)
|
|
330
|
+
except json.JSONDecodeError:
|
|
331
|
+
return {"raw_output": result.stdout.strip()}
|
|
332
|
+
|
|
333
|
+
def pool_status(self, pool_commitment: str) -> dict:
|
|
334
|
+
"""Check pool status."""
|
|
335
|
+
result = self._run_gateway("pool-status", pool_commitment)
|
|
336
|
+
if result.returncode != 0:
|
|
337
|
+
raise RuntimeError(f"pool-status failed: {result.stderr}")
|
|
338
|
+
try:
|
|
339
|
+
return json.loads(result.stdout)
|
|
340
|
+
except json.JSONDecodeError:
|
|
341
|
+
return {"raw_output": result.stdout.strip()}
|
|
342
|
+
|
|
343
|
+
def health_check(self) -> dict:
|
|
344
|
+
"""Quick health check — returns JSON-safe result."""
|
|
345
|
+
report = self.validate(verbose=False)
|
|
346
|
+
return report.to_dict()
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
# ─── CLI ──────────────────────────────────────────────────────────────────────
|
|
350
|
+
def main():
|
|
351
|
+
if len(sys.argv) < 2:
|
|
352
|
+
print("Usage: python3 nightpay_sdk.py <command>")
|
|
353
|
+
print("Commands: validate, stats, setup, doctor, health")
|
|
354
|
+
sys.exit(1)
|
|
355
|
+
|
|
356
|
+
cmd = sys.argv[1]
|
|
357
|
+
np = NightPay()
|
|
358
|
+
|
|
359
|
+
if cmd == "validate":
|
|
360
|
+
report = np.validate()
|
|
361
|
+
sys.exit(0 if report.passed else 1)
|
|
362
|
+
|
|
363
|
+
elif cmd == "doctor":
|
|
364
|
+
auto = "--auto-fix" in sys.argv
|
|
365
|
+
report = np.doctor(auto_fix=auto)
|
|
366
|
+
sys.exit(0 if report.passed else 1)
|
|
367
|
+
|
|
368
|
+
elif cmd == "health":
|
|
369
|
+
result = np.health_check()
|
|
370
|
+
print(json.dumps(result, indent=2))
|
|
371
|
+
sys.exit(0 if result["healthy"] else 1)
|
|
372
|
+
|
|
373
|
+
elif cmd == "stats":
|
|
374
|
+
try:
|
|
375
|
+
result = np.stats()
|
|
376
|
+
print(json.dumps(result, indent=2))
|
|
377
|
+
except Exception as e:
|
|
378
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
379
|
+
sys.exit(1)
|
|
380
|
+
|
|
381
|
+
elif cmd == "setup":
|
|
382
|
+
# Run bash setup.sh if available
|
|
383
|
+
setup_sh = Path(__file__).parent / "scripts" / "setup.sh"
|
|
384
|
+
if not setup_sh.exists():
|
|
385
|
+
setup_sh = Path(__file__).parent.parent / "scripts" / "setup.sh"
|
|
386
|
+
if setup_sh.exists():
|
|
387
|
+
os.execvp("bash", ["bash", str(setup_sh)] + sys.argv[2:])
|
|
388
|
+
else:
|
|
389
|
+
print("setup.sh not found — run validate instead")
|
|
390
|
+
np.validate()
|
|
391
|
+
|
|
392
|
+
else:
|
|
393
|
+
print(f"Unknown command: {cmd}")
|
|
394
|
+
sys.exit(1)
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
if __name__ == "__main__":
|
|
398
|
+
main()
|
|
@@ -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.4.1",
|
|
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
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "Anonymous community bounties for AI agents. Midnight ZK proofs + Masumi settlement + Cardano finality.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bounties",
|
|
@@ -16,24 +16,35 @@
|
|
|
16
16
|
"agent-skills",
|
|
17
17
|
"clawhub"
|
|
18
18
|
],
|
|
19
|
-
"homepage": "https://
|
|
19
|
+
"homepage": "https://nightpay.dev",
|
|
20
20
|
"repository": {
|
|
21
21
|
"type": "git",
|
|
22
22
|
"url": "git+https://github.com/nightpay/nightpay.git"
|
|
23
23
|
},
|
|
24
|
-
"license": "
|
|
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": "
|
|
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
|
-
"bin/",
|
|
36
|
+
"bin/cli.js",
|
|
35
37
|
"skills/",
|
|
36
38
|
"README.md",
|
|
37
|
-
"LICENSE"
|
|
38
|
-
|
|
39
|
+
"LICENSE",
|
|
40
|
+
"nightpay_sdk.py",
|
|
41
|
+
"plugin.js",
|
|
42
|
+
"openclaw.plugin.json"
|
|
43
|
+
],
|
|
44
|
+
"openclaw": {
|
|
45
|
+
"extensions": [
|
|
46
|
+
"./plugin.js"
|
|
47
|
+
],
|
|
48
|
+
"type": "skillBundle"
|
|
49
|
+
}
|
|
39
50
|
}
|