nebulon-escrow-cli 0.8.4 → 0.8.5

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/README.md CHANGED
@@ -1,14 +1,105 @@
1
1
  # Nebulon CLI
2
2
 
3
- Local development:
3
+ Nebulon CLI is the command-line interface for managing Nebulon escrow contracts,
4
+ wallets, capsules, and hosted profile actions. It supports MagicBlock ephemeral
5
+ rollups for contract operations and provides end-to-end contract workflows,
6
+ including terms, milestones, funding, and claims.
4
7
 
8
+ ## Requirements
9
+
10
+ - Node.js 18 or newer
11
+ - npm 9 or newer
12
+
13
+ ## Installation
14
+
15
+ Global install from npm:
16
+
17
+ ```bash
18
+ npm install -g nebulon-escrow-cli
5
19
  ```
20
+
21
+ From source:
22
+
23
+ ```bash
6
24
  cd CLI
7
25
  npm install
8
26
  npm link
27
+ ```
28
+
29
+ ## Quick start
30
+
31
+ ```bash
9
32
  nebulon init
33
+ nebulon login
34
+ nebulon status
35
+ ```
36
+
37
+ The `init` flow sets up a capsule, wallet, and network defaults. `login` connects
38
+ to hosted services. Use `status` to verify connectivity and balances.
39
+
40
+ ## Command overview
41
+
42
+ Run `nebulon help` for the full list. Common commands:
43
+
44
+ - `nebulon init` - interactive setup (banner shows the CLI version)
45
+ - `nebulon status` - account summary
46
+ - `nebulon login` / `nebulon logout`
47
+ - `nebulon capsule list` / `nebulon capsule use <name>`
48
+ - `nebulon contract ...` - create and manage escrow contracts
49
+ - `nebulon invite list` / `nebulon invite <id>`
50
+ - `nebulon balance` / `nebulon address`
51
+ - `nebulon wallet export`
52
+ - `nebulon config` / `nebulon config <key> <value>`
53
+
54
+ ### Contract commands (examples)
55
+
56
+ ```bash
57
+ nebulon contract list
58
+ nebulon contract <id> details
59
+ nebulon contract <id> add term payment 20
60
+ nebulon contract <id> add term deadline 8d
61
+ nebulon contract <id> add milestone "Draft spec"
62
+ nebulon contract <id> sign
63
+ nebulon contract <id> fund
64
+ nebulon contract <id> milestone 1 ready
65
+ nebulon contract <id> milestone 1 confirm
66
+ nebulon contract <id> claim_funds
67
+ ```
68
+
69
+ ### Verbose diagnostics
70
+
71
+ Use `--verbose` on contract commands to show additional progress and routing
72
+ details:
73
+
74
+ ```bash
75
+ nebulon contract <id> fund --verbose
10
76
  ```
11
77
 
12
- Notes:
13
- - This package is private for now. Do not publish to npm until you are ready.
14
- - Config and wallets are stored under `~/.nebulon/`.
78
+ ## Configuration and storage
79
+
80
+ By default, configuration and wallets are stored under:
81
+
82
+ - Windows: `%USERPROFILE%\\.nebulon\\`
83
+ - macOS/Linux: `~/.nebulon/`
84
+
85
+ Capsules live under `~/.nebulon/capsules/`. You can override the base directory
86
+ with the `NEBULON_HOME` environment variable.
87
+
88
+ Wallet import and export:
89
+
90
+ - Import expects keypair files in `C:\\Nebulon\\Wallets` (Windows).
91
+ - Export writes the active wallet to `C:\\Nebulon\\Wallets`.
92
+
93
+ ## Version
94
+
95
+ ```bash
96
+ nebulon --version
97
+ nebulon -v
98
+ ```
99
+
100
+ ## Troubleshooting
101
+
102
+ - If `login` or hosted actions fail, re-run `nebulon config` and confirm backend
103
+ URLs and network settings.
104
+ - If RPC calls fail, verify the configured `rpcUrl` and `wsUrl`.
105
+ - Use `nebulon contract ... --verbose` for detailed progress output.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nebulon-escrow-cli",
3
- "version": "0.8.4",
3
+ "version": "0.8.5",
4
4
  "description": "Nebulon CLI",
5
5
  "author": "northbyt3 <northbyt3@gmail.com>",
6
6
  "license": "UNLICENSED",
@@ -165,6 +165,9 @@ const getEphemeralProgram = async (config, signerKeypair, options = {}) => {
165
165
  const getDelegationValidator = async (config) => {
166
166
  const endpoint = (config.ephemeralProviderUrl || "").toLowerCase();
167
167
  const wsEndpoint = (config.ephemeralWsUrl || "").toLowerCase();
168
+ if (config && config.__verbose) {
169
+ console.log("DEBUG: delegation resolver endpoints", { endpoint, wsEndpoint });
170
+ }
168
171
  if (endpoint.includes("localhost") || endpoint.includes("127.0.0.1")) {
169
172
  return LOCAL_VALIDATOR_IDENTITY;
170
173
  }
@@ -185,6 +188,9 @@ const getDelegationValidator = async (config) => {
185
188
  if (shouldUseRouter && config.ephemeralProviderUrl) {
186
189
  const cacheKey = `${endpoint}|${wsEndpoint}`;
187
190
  if (cachedValidator && cachedValidatorEndpoint === cacheKey) {
191
+ if (config && config.__verbose) {
192
+ console.log("DEBUG: using cached validator", cachedValidator.toBase58());
193
+ }
188
194
  return cachedValidator;
189
195
  }
190
196
  try {
@@ -193,7 +199,7 @@ const getDelegationValidator = async (config) => {
193
199
  });
194
200
  const closest = await router.getClosestValidator();
195
201
  if (config && config.__verbose) {
196
- console.log("Router closest:", JSON.stringify(closest));
202
+ console.log("DEBUG: router closest validator:", closest);
197
203
  }
198
204
  const identity =
199
205
  closest?.validatorIdentity || closest?.identity || closest?.validator;
@@ -209,8 +215,14 @@ const getDelegationValidator = async (config) => {
209
215
  }
210
216
  const fallback = safePublicKey(config.ephemeralValidatorIdentity);
211
217
  if (fallback) {
218
+ if (config && config.__verbose) {
219
+ console.log("DEBUG: using configured validator identity", fallback.toBase58());
220
+ }
212
221
  return fallback;
213
222
  }
223
+ if (config && config.__verbose) {
224
+ console.warn("DEBUG: no validator identity resolved.");
225
+ }
214
226
  return null;
215
227
  };
216
228
 
@@ -1067,6 +1079,13 @@ const ensureDelegatedAccount = async (
1067
1079
  options
1068
1080
  ) => {
1069
1081
  const info = await program.provider.connection.getAccountInfo(pda, "confirmed");
1082
+ if (isVerbose(options)) {
1083
+ console.log("DEBUG: delegation check", {
1084
+ pda: pda.toBase58(),
1085
+ owner: info?.owner?.toBase58?.(),
1086
+ delegated: Boolean(info && info.owner.equals(DELEGATION_PROGRAM_ID)),
1087
+ });
1088
+ }
1070
1089
  if (info && info.owner.equals(DELEGATION_PROGRAM_ID)) {
1071
1090
  if (isVerbose(options)) {
1072
1091
  console.log(`Delegation already active for ${pda.toBase58()}; skipping.`);
@@ -1124,6 +1143,13 @@ const ensureDelegatedEscrow = async (
1124
1143
  escrowPda,
1125
1144
  "confirmed"
1126
1145
  );
1146
+ if (isVerbose(options)) {
1147
+ console.log("DEBUG: escrow delegation check", {
1148
+ escrow: escrowPda.toBase58(),
1149
+ owner: info?.owner?.toBase58?.(),
1150
+ delegated: Boolean(info && info.owner.equals(DELEGATION_PROGRAM_ID)),
1151
+ });
1152
+ }
1127
1153
  if (info && info.owner.equals(DELEGATION_PROGRAM_ID)) {
1128
1154
  if (isVerbose(options)) {
1129
1155
  console.log(`Delegation already active for ${escrowPda.toBase58()}; skipping.`);
@@ -2210,6 +2236,27 @@ const runSignContract = async (config, contract, options) => {
2210
2236
  }
2211
2237
 
2212
2238
  console.log("Processing.");
2239
+ if (isVerbose(options)) {
2240
+ console.log("DEBUG: PER config", {
2241
+ ephemeralProviderUrl: config.ephemeralProviderUrl,
2242
+ ephemeralWsUrl: config.ephemeralWsUrl,
2243
+ ephemeralValidatorIdentity: config.ephemeralValidatorIdentity,
2244
+ rpcUrl: config.rpcUrl,
2245
+ wsUrl: config.wsUrl,
2246
+ network: config.network,
2247
+ });
2248
+ try {
2249
+ if (config.ephemeralProviderUrl) {
2250
+ const router = new ConnectionMagicRouter(config.ephemeralProviderUrl, {
2251
+ wsEndpoint: config.ephemeralWsUrl || undefined,
2252
+ });
2253
+ const closest = await router.getClosestValidator();
2254
+ console.log("DEBUG: router closest validator (sign flow):", closest);
2255
+ }
2256
+ } catch (error) {
2257
+ console.log("DEBUG: router closest validator lookup failed:", error?.message || error);
2258
+ }
2259
+ }
2213
2260
  logProgress("Submitting signature on ER");
2214
2261
  let signSig = null;
2215
2262
  let commitSig = null;
@@ -2228,17 +2275,26 @@ const runSignContract = async (config, contract, options) => {
2228
2275
  programId,
2229
2276
  program.provider
2230
2277
  );
2231
- signSig = await erProgram.methods
2232
- .signPrivateTerms(new anchor.BN(escrowId.toString()))
2233
- .accounts({
2234
- user: keypair.publicKey,
2235
- payer: sessionSigner.publicKey,
2236
- sessionToken: sessionPda,
2237
- escrow: escrowPda,
2238
- terms: termsPda,
2239
- })
2240
- .signers([sessionSigner])
2241
- .rpc({ skipPreflight: true });
2278
+ try {
2279
+ signSig = await erProgram.methods
2280
+ .signPrivateTerms(new anchor.BN(escrowId.toString()))
2281
+ .accounts({
2282
+ user: keypair.publicKey,
2283
+ payer: sessionSigner.publicKey,
2284
+ sessionToken: sessionPda,
2285
+ escrow: escrowPda,
2286
+ terms: termsPda,
2287
+ })
2288
+ .signers([sessionSigner])
2289
+ .rpc({ skipPreflight: true });
2290
+ } catch (error) {
2291
+ console.error("ERROR: signPrivateTerms failed:", error?.message || error);
2292
+ const msg = (error?.message || "").toLowerCase();
2293
+ if (msg.includes("invalidwritableaccount")) {
2294
+ console.error("HINT: InvalidWritableAccount usually means validator mismatch. Run with --verbose and compare router validator vs delegation.");
2295
+ }
2296
+ throw error;
2297
+ }
2242
2298
  logTx("Signature submitted.", signSig, true);
2243
2299
  try {
2244
2300
  logProgress("Committing terms on ER");
@@ -140,21 +140,15 @@ const runInit = async (options = {}, capsuleName) => {
140
140
  let backendAnswer = { backendUrl: config.backendUrl };
141
141
  let backendSource = config.backendSource || "custom";
142
142
  let backendNetwork = null;
143
- let serverChoice = null;
144
- try {
145
- await getConfig((config.backendUrl || "http://174.138.42.117:3333").trim());
146
- serverChoice = await prompt({
147
- type: "select",
148
- name: "server",
149
- message: "Use official server or custom?",
150
- choices: [
151
- { name: "official", message: "Official server" },
152
- { name: "custom", message: "Custom server" },
153
- ],
154
- });
155
- } catch (error) {
156
- serverChoice = { server: "custom" };
157
- }
143
+ const serverChoice = await prompt({
144
+ type: "select",
145
+ name: "server",
146
+ message: "Use official server or custom?",
147
+ choices: [
148
+ { name: "official", message: "Official server" },
149
+ { name: "custom", message: "Custom server" },
150
+ ],
151
+ });
158
152
 
159
153
  if (serverChoice.server === "custom") {
160
154
  backendAnswer = await prompt({
package/src/constants.js CHANGED
@@ -26,7 +26,7 @@ const DEFAULT_CONFIG = {
26
26
  rpcUrl: NETWORK_PRESETS.localnet.rpcUrl,
27
27
  wsUrl: NETWORK_PRESETS.localnet.wsUrl,
28
28
  programId: PROGRAM_ID_DEFAULT,
29
- backendUrl: "http://localhost:3333",
29
+ backendUrl: "http://174.138.42.117:3333",
30
30
  usdcMint: NETWORK_PRESETS.localnet.usdcMint,
31
31
  programIdSource: "custom",
32
32
  usdcMintSource: "custom",