@reppo/cli 0.1.0
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 +21 -0
- package/README.md +84 -0
- package/dist/api/platform.d.ts +39 -0
- package/dist/api/platform.d.ts.map +1 -0
- package/dist/api/platform.js +148 -0
- package/dist/api/platform.js.map +1 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +60 -0
- package/dist/bin.js.map +1 -0
- package/dist/chain/abis.d.ts +576 -0
- package/dist/chain/abis.d.ts.map +1 -0
- package/dist/chain/abis.js +66 -0
- package/dist/chain/abis.js.map +1 -0
- package/dist/chain/addresses.d.ts +27 -0
- package/dist/chain/addresses.d.ts.map +1 -0
- package/dist/chain/addresses.js +39 -0
- package/dist/chain/addresses.js.map +1 -0
- package/dist/chain/clients.d.ts +46 -0
- package/dist/chain/clients.d.ts.map +1 -0
- package/dist/chain/clients.js +48 -0
- package/dist/chain/clients.js.map +1 -0
- package/dist/chain/contracts.d.ts +28 -0
- package/dist/chain/contracts.d.ts.map +1 -0
- package/dist/chain/contracts.js +70 -0
- package/dist/chain/contracts.js.map +1 -0
- package/dist/chain/errors.d.ts +14 -0
- package/dist/chain/errors.d.ts.map +1 -0
- package/dist/chain/errors.js +72 -0
- package/dist/chain/errors.js.map +1 -0
- package/dist/commands/_base.d.ts +19 -0
- package/dist/commands/_base.d.ts.map +1 -0
- package/dist/commands/_base.js +40 -0
- package/dist/commands/_base.js.map +1 -0
- package/dist/commands/auth.d.ts +9 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +93 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/claim-emissions.d.ts +11 -0
- package/dist/commands/claim-emissions.d.ts.map +1 -0
- package/dist/commands/claim-emissions.js +192 -0
- package/dist/commands/claim-emissions.js.map +1 -0
- package/dist/commands/extend-lock.d.ts +11 -0
- package/dist/commands/extend-lock.d.ts.map +1 -0
- package/dist/commands/extend-lock.js +196 -0
- package/dist/commands/extend-lock.js.map +1 -0
- package/dist/commands/grant-access.d.ts +11 -0
- package/dist/commands/grant-access.d.ts.map +1 -0
- package/dist/commands/grant-access.js +191 -0
- package/dist/commands/grant-access.js.map +1 -0
- package/dist/commands/lock.d.ts +11 -0
- package/dist/commands/lock.d.ts.map +1 -0
- package/dist/commands/lock.js +192 -0
- package/dist/commands/lock.js.map +1 -0
- package/dist/commands/mint-pod.d.ts +15 -0
- package/dist/commands/mint-pod.d.ts.map +1 -0
- package/dist/commands/mint-pod.js +286 -0
- package/dist/commands/mint-pod.js.map +1 -0
- package/dist/commands/query/balance.d.ts +9 -0
- package/dist/commands/query/balance.d.ts.map +1 -0
- package/dist/commands/query/balance.js +91 -0
- package/dist/commands/query/balance.js.map +1 -0
- package/dist/commands/query/datanet.d.ts +10 -0
- package/dist/commands/query/datanet.d.ts.map +1 -0
- package/dist/commands/query/datanet.js +128 -0
- package/dist/commands/query/datanet.js.map +1 -0
- package/dist/commands/query/emissions-due.d.ts +18 -0
- package/dist/commands/query/emissions-due.d.ts.map +1 -0
- package/dist/commands/query/emissions-due.js +170 -0
- package/dist/commands/query/emissions-due.js.map +1 -0
- package/dist/commands/query/pod.d.ts +8 -0
- package/dist/commands/query/pod.d.ts.map +1 -0
- package/dist/commands/query/pod.js +88 -0
- package/dist/commands/query/pod.js.map +1 -0
- package/dist/commands/query/voting-power.d.ts +9 -0
- package/dist/commands/query/voting-power.d.ts.map +1 -0
- package/dist/commands/query/voting-power.js +82 -0
- package/dist/commands/query/voting-power.js.map +1 -0
- package/dist/commands/register-agent.d.ts +9 -0
- package/dist/commands/register-agent.d.ts.map +1 -0
- package/dist/commands/register-agent.js +115 -0
- package/dist/commands/register-agent.js.map +1 -0
- package/dist/commands/unlock.d.ts +11 -0
- package/dist/commands/unlock.d.ts.map +1 -0
- package/dist/commands/unlock.js +181 -0
- package/dist/commands/unlock.js.map +1 -0
- package/dist/commands/vote.d.ts +13 -0
- package/dist/commands/vote.d.ts.map +1 -0
- package/dist/commands/vote.js +174 -0
- package/dist/commands/vote.js.map +1 -0
- package/dist/config/load.d.ts +19 -0
- package/dist/config/load.d.ts.map +1 -0
- package/dist/config/load.js +66 -0
- package/dist/config/load.js.map +1 -0
- package/dist/output/format.d.ts +43 -0
- package/dist/output/format.d.ts.map +1 -0
- package/dist/output/format.js +58 -0
- package/dist/output/format.js.map +1 -0
- package/dist/state/db.d.ts +47 -0
- package/dist/state/db.d.ts.map +1 -0
- package/dist/state/db.js +169 -0
- package/dist/state/db.js.map +1 -0
- package/dist/state/idempotency.d.ts +66 -0
- package/dist/state/idempotency.d.ts.map +1 -0
- package/dist/state/idempotency.js +173 -0
- package/dist/state/idempotency.js.map +1 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Reppo Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# @reppo/cli
|
|
2
|
+
|
|
3
|
+
Command-line interface for [Reppo](https://reppo.ai) — mint pods, vote, lock REPPO, manage datanets. Built for **AI agents** as the primary user, but humans can use it too.
|
|
4
|
+
|
|
5
|
+
> **Status:** v0.1.0. `auth`, `query balance`, `query datanet`, `query emissions-due`, `query pod`, `query voting-power`, `claim-emissions`, `extend-lock`, `grant-access`, `lock`, `mint-pod`, `register-agent`, `unlock`, and `vote` ship in this release. The remaining 2 commands (`create-datanet`, `swap`) are scaffolded but not yet wired.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm i -g @reppo/cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Requires Node ≥20.
|
|
14
|
+
|
|
15
|
+
## Authentication
|
|
16
|
+
|
|
17
|
+
The CLI is non-interactive — all credentials come from environment variables:
|
|
18
|
+
|
|
19
|
+
| Variable | Required for | Description |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| `REPPO_PRIVATE_KEY` | All write commands | EOA private key (32-byte hex) |
|
|
22
|
+
| `REPPO_VOTER_PRIVATE_KEY` | `vote` (optional) | Separate EOA for voting (publishers cannot vote on their own pods) |
|
|
23
|
+
| `REPPO_NETWORK` | All commands (optional) | `mainnet` (default) or `testnet` |
|
|
24
|
+
| `REPPO_RPC_URL` | All commands (optional) | Override RPC endpoint |
|
|
25
|
+
| `REPPO_API_URL` | Platform-API commands (optional) | Override Reppo API base |
|
|
26
|
+
| `REPPO_API_KEY` | `register-agent`, `create-datanet` | Reppo platform API key |
|
|
27
|
+
|
|
28
|
+
Network can also be set per-call via `--network mainnet|testnet`.
|
|
29
|
+
|
|
30
|
+
## Output
|
|
31
|
+
|
|
32
|
+
All commands run in human-readable mode by default. Pass `--json` to emit a single JSON object per command on stdout — agents should always pass this.
|
|
33
|
+
|
|
34
|
+
Errors **always** emit JSON on stderr regardless of mode, with a stable `code` field:
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{ "error": { "code": "INSUFFICIENT_VOTING_POWER", "message": "...", "hint": "Run `reppo lock <amount> --duration 7200` first." } }
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Commands
|
|
41
|
+
|
|
42
|
+
### Read
|
|
43
|
+
|
|
44
|
+
- `reppo query balance [address]` — ETH + REPPO + veREPPO + USDC
|
|
45
|
+
- `reppo query voting-power [address]` — veREPPO voting power + lockup count
|
|
46
|
+
- `reppo query pod <podId>` — pod existence + owner address
|
|
47
|
+
- `reppo query datanet <datanetId> [--for <addr>]` — validity + REPPO access fee, optionally check access for an address
|
|
48
|
+
- `reppo query emissions-due` — list unclaimed REPPO emissions across all pods owned by the configured wallet (uses platform API)
|
|
49
|
+
|
|
50
|
+
### Write
|
|
51
|
+
|
|
52
|
+
- `reppo vote --pod <id> --subnet <id> --like|--dislike` — cast an on-chain vote
|
|
53
|
+
- `reppo mint-pod` — mint a pod NFT. Mainnet: `--share <0-100>`. Testnet: `--datanet <id> [--token reppo|primary]`. Optional `--to <addr>`.
|
|
54
|
+
- `reppo lock <amount> --duration <seconds>` — lock REPPO into veREPPO for voting power
|
|
55
|
+
- `reppo unlock <lockupId> [--to <addr>]` — withdraw an expired veREPPO lockup, returning the locked REPPO
|
|
56
|
+
- `reppo extend-lock <lockupId> --duration <seconds>` — extend an existing veREPPO lockup
|
|
57
|
+
- `reppo grant-access --datanet <id> [--to <addr>]` — pay the REPPO access fee and grant `--to` access to a datanet
|
|
58
|
+
- `reppo claim-emissions --pod <id> --epoch <n>` — claim a pod's emissions for an epoch
|
|
59
|
+
- `reppo register-agent --name <s> --description <s>` — register a new agent identity on the Reppo platform; returns id + accessToken + a server-provisioned (unfunded) Base wallet address. Send ETH + REPPO to it before on-chain mints.
|
|
60
|
+
- `reppo auth [--force]` — sign in to the platform API (api.reppo.xyz); caches a 24h Bearer token used by `query emissions-due` and other platform-API commands
|
|
61
|
+
- `reppo create-datanet ...` *(planned — currently dashboard-only; the REST endpoint requires a Privy session cookie which is browser auth)*
|
|
62
|
+
- `reppo swap <from> <to> --amount <n>` *(planned, mainnet only)*
|
|
63
|
+
|
|
64
|
+
## Idempotency
|
|
65
|
+
|
|
66
|
+
Every write command accepts `--idempotency-key <stable-string>`. The CLI caches the result (in `~/.reppo/cli-state.json`); repeat calls with the same key skip signing and return the prior tx hash. Critical for agent retry loops.
|
|
67
|
+
|
|
68
|
+
## Dry run
|
|
69
|
+
|
|
70
|
+
Every write command accepts `--dry-run`. Simulates via `eth_call`, decodes custom errors, returns gas estimate. Exits 0 if would succeed, non-zero with a structured error if would revert.
|
|
71
|
+
|
|
72
|
+
## Claude Code skill
|
|
73
|
+
|
|
74
|
+
Also available as a Claude Code skill that teaches agents how to invoke this CLI:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
claude plugin install reppo
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The skill ships with the same v0.1.0 alpha feature subset.
|
|
81
|
+
|
|
82
|
+
## License
|
|
83
|
+
|
|
84
|
+
MIT — see [LICENSE](./LICENSE).
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reppo platform API helpers (api.reppo.xyz).
|
|
3
|
+
*
|
|
4
|
+
* Two-step EIP-191 wallet auth:
|
|
5
|
+
* 1. POST /auth/nonce { walletAddress } → { nonce, message }
|
|
6
|
+
* 2. POST /auth/verify { walletAddress, signature, nonce } → { token, ... }
|
|
7
|
+
*
|
|
8
|
+
* The returned token is a 24h Bearer credential. Cached in
|
|
9
|
+
* state/db.ts under sessions[`${network}:reppo-platform`].
|
|
10
|
+
*
|
|
11
|
+
* NB: this module deliberately does NOT touch reppo.ai/api/v1/agents/*,
|
|
12
|
+
* which is a separate auth surface (persistent accessToken from
|
|
13
|
+
* register-agent, not a session). Two distinct services.
|
|
14
|
+
*/
|
|
15
|
+
import type { PrivateKeyAccount } from 'viem/accounts';
|
|
16
|
+
import { type SessionEntry } from '../state/db.js';
|
|
17
|
+
import type { Network } from '../chain/addresses.js';
|
|
18
|
+
/**
|
|
19
|
+
* Re-authenticate with the platform API: hit /auth/nonce, sign the
|
|
20
|
+
* returned message with the caller's account, hit /auth/verify, return
|
|
21
|
+
* the new session.
|
|
22
|
+
*
|
|
23
|
+
* Caller responsible for caching via `saveSession`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function signInWithEthereum(apiUrl: string, account: PrivateKeyAccount): Promise<SessionEntry>;
|
|
26
|
+
/**
|
|
27
|
+
* Return a valid platform-API session, refreshing if expired or missing.
|
|
28
|
+
* Caches the new session in state/db.ts.
|
|
29
|
+
*
|
|
30
|
+
* Refresh-margin: 60s — refresh proactively if the cached token expires
|
|
31
|
+
* in the next minute, to avoid a stale-token revert mid-command.
|
|
32
|
+
*/
|
|
33
|
+
export declare function getOrRefreshSession(network: Network, apiUrl: string | undefined, privateKey: `0x${string}`): Promise<SessionEntry>;
|
|
34
|
+
/**
|
|
35
|
+
* Authenticated GET against the platform API. Returns parsed JSON or
|
|
36
|
+
* throws PLATFORM_API_ERROR on non-2xx.
|
|
37
|
+
*/
|
|
38
|
+
export declare function platformGet<T>(apiUrl: string | undefined, path: string, token: string): Promise<T>;
|
|
39
|
+
//# sourceMappingURL=platform.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../../src/api/platform.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAGvD,OAAO,EAA2B,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC5E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AA0BrD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,YAAY,CAAC,CAsFvB;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,UAAU,EAAE,KAAK,MAAM,EAAE,GACxB,OAAO,CAAC,YAAY,CAAC,CAcvB;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,CAAC,EACjC,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,CAAC,CAAC,CAiCZ"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
2
|
+
import { cliError } from '../output/format.js';
|
|
3
|
+
import { getSession, saveSession } from '../state/db.js';
|
|
4
|
+
const SESSION_KEY = 'reppo-platform';
|
|
5
|
+
/**
|
|
6
|
+
* Resolve the platform-API base URL or throw PLATFORM_API_NOT_CONFIGURED.
|
|
7
|
+
*
|
|
8
|
+
* `cfg.apiUrl` is defaulted to https://api.reppo.xyz on mainnet by
|
|
9
|
+
* `config/load.ts`. On testnet it is intentionally undefined (no stable
|
|
10
|
+
* hosted endpoint), so reaching this helper with `apiUrl=undefined`
|
|
11
|
+
* means the caller is on testnet without `REPPO_API_URL` set. Falling
|
|
12
|
+
* back to mainnet here would silently sign in / query against mainnet
|
|
13
|
+
* and cache the resulting token under the `testnet:reppo-platform`
|
|
14
|
+
* session key — the silent-wrong-network class of bug. Fail loud.
|
|
15
|
+
*/
|
|
16
|
+
function requireApiUrl(apiUrl) {
|
|
17
|
+
if (!apiUrl) {
|
|
18
|
+
throw cliError('PLATFORM_API_NOT_CONFIGURED', 'No platform API URL configured for this network.', 'Mainnet defaults to https://api.reppo.xyz. On testnet, set REPPO_API_URL to your platform API endpoint.');
|
|
19
|
+
}
|
|
20
|
+
return apiUrl;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Re-authenticate with the platform API: hit /auth/nonce, sign the
|
|
24
|
+
* returned message with the caller's account, hit /auth/verify, return
|
|
25
|
+
* the new session.
|
|
26
|
+
*
|
|
27
|
+
* Caller responsible for caching via `saveSession`.
|
|
28
|
+
*/
|
|
29
|
+
export async function signInWithEthereum(apiUrl, account) {
|
|
30
|
+
const baseUrl = apiUrl.replace(/\/+$/, '');
|
|
31
|
+
// Step 1: request a nonce + message to sign.
|
|
32
|
+
let nonceResp;
|
|
33
|
+
try {
|
|
34
|
+
nonceResp = await fetch(`${baseUrl}/auth/nonce`, {
|
|
35
|
+
method: 'POST',
|
|
36
|
+
headers: { 'Content-Type': 'application/json' },
|
|
37
|
+
body: JSON.stringify({ walletAddress: account.address }),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
throw cliError('PLATFORM_API_UNREACHABLE', `Could not reach the Reppo platform at ${baseUrl}: ${e.message}.`, 'Check your internet connection. If the platform is down, try again in a few minutes.');
|
|
42
|
+
}
|
|
43
|
+
if (!nonceResp.ok) {
|
|
44
|
+
const body = await safeJson(nonceResp);
|
|
45
|
+
throw cliError('PLATFORM_API_AUTH_FAILED', `/auth/nonce returned HTTP ${nonceResp.status}: ${pickErr(body) ?? 'no body'}.`, `If the error persists, the platform may be rejecting your wallet address. Verify REPPO_PRIVATE_KEY derives the address you expect.`);
|
|
46
|
+
}
|
|
47
|
+
const nonceData = (await nonceResp.json());
|
|
48
|
+
if (!nonceData.nonce || !nonceData.message) {
|
|
49
|
+
throw cliError('PLATFORM_API_INVALID_RESPONSE', `/auth/nonce returned 200 but the response is missing nonce or message fields.`);
|
|
50
|
+
}
|
|
51
|
+
// Step 2: sign the message and verify.
|
|
52
|
+
const signature = await account.signMessage({ message: nonceData.message });
|
|
53
|
+
let verifyResp;
|
|
54
|
+
try {
|
|
55
|
+
verifyResp = await fetch(`${baseUrl}/auth/verify`, {
|
|
56
|
+
method: 'POST',
|
|
57
|
+
headers: { 'Content-Type': 'application/json' },
|
|
58
|
+
body: JSON.stringify({
|
|
59
|
+
walletAddress: account.address,
|
|
60
|
+
signature,
|
|
61
|
+
nonce: nonceData.nonce,
|
|
62
|
+
}),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
throw cliError('PLATFORM_API_UNREACHABLE', `Could not reach ${baseUrl}/auth/verify: ${e.message}.`);
|
|
67
|
+
}
|
|
68
|
+
if (!verifyResp.ok) {
|
|
69
|
+
const body = await safeJson(verifyResp);
|
|
70
|
+
throw cliError('PLATFORM_API_AUTH_FAILED', `/auth/verify returned HTTP ${verifyResp.status}: ${pickErr(body) ?? 'no body'}.`, 'The platform rejected the signature. Verify REPPO_PRIVATE_KEY is the same wallet that requested the nonce.');
|
|
71
|
+
}
|
|
72
|
+
// Verify response shape varies; handle both flat and nested envelopes.
|
|
73
|
+
const verifyBody = (await verifyResp.json());
|
|
74
|
+
const data = (verifyBody.data && typeof verifyBody.data === 'object' ? verifyBody.data : verifyBody);
|
|
75
|
+
const token = (data.token ?? data.accessToken);
|
|
76
|
+
const expiresInSec = (data.expiresIn ?? 86400); // default 24h per docs
|
|
77
|
+
if (!token) {
|
|
78
|
+
throw cliError('PLATFORM_API_INVALID_RESPONSE', `/auth/verify returned 200 but no token field.`);
|
|
79
|
+
}
|
|
80
|
+
const now = Date.now();
|
|
81
|
+
return {
|
|
82
|
+
accessToken: token,
|
|
83
|
+
walletAddress: account.address,
|
|
84
|
+
expiresAt: now + expiresInSec * 1000,
|
|
85
|
+
createdAt: now,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Return a valid platform-API session, refreshing if expired or missing.
|
|
90
|
+
* Caches the new session in state/db.ts.
|
|
91
|
+
*
|
|
92
|
+
* Refresh-margin: 60s — refresh proactively if the cached token expires
|
|
93
|
+
* in the next minute, to avoid a stale-token revert mid-command.
|
|
94
|
+
*/
|
|
95
|
+
export async function getOrRefreshSession(network, apiUrl, privateKey) {
|
|
96
|
+
const url = requireApiUrl(apiUrl);
|
|
97
|
+
const account = privateKeyToAccount(privateKey);
|
|
98
|
+
const cached = await getSession(network, SESSION_KEY);
|
|
99
|
+
if (cached && cached.walletAddress?.toLowerCase() === account.address.toLowerCase()) {
|
|
100
|
+
const REFRESH_MARGIN_MS = 60_000;
|
|
101
|
+
const stillValid = cached.expiresAt !== undefined && Date.now() + REFRESH_MARGIN_MS < cached.expiresAt;
|
|
102
|
+
if (stillValid)
|
|
103
|
+
return cached;
|
|
104
|
+
}
|
|
105
|
+
const fresh = await signInWithEthereum(url, account);
|
|
106
|
+
await saveSession(network, SESSION_KEY, fresh);
|
|
107
|
+
return fresh;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Authenticated GET against the platform API. Returns parsed JSON or
|
|
111
|
+
* throws PLATFORM_API_ERROR on non-2xx.
|
|
112
|
+
*/
|
|
113
|
+
export async function platformGet(apiUrl, path, token) {
|
|
114
|
+
const baseUrl = requireApiUrl(apiUrl).replace(/\/+$/, '');
|
|
115
|
+
const url = `${baseUrl}${path}`;
|
|
116
|
+
let response;
|
|
117
|
+
try {
|
|
118
|
+
response = await fetch(url, {
|
|
119
|
+
headers: { 'Authorization': `Bearer ${token}` },
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
throw cliError('PLATFORM_API_UNREACHABLE', `Could not reach ${url}: ${e.message}.`);
|
|
124
|
+
}
|
|
125
|
+
if (response.status === 401) {
|
|
126
|
+
throw cliError('PLATFORM_API_UNAUTHORIZED', `Platform rejected the session token (HTTP 401) at ${path}.`, 'Run `reppo auth --force` to mint a fresh token.');
|
|
127
|
+
}
|
|
128
|
+
if (!response.ok) {
|
|
129
|
+
const body = await safeJson(response);
|
|
130
|
+
throw cliError('PLATFORM_API_ERROR', `${path} returned HTTP ${response.status}: ${pickErr(body) ?? 'no body'}.`);
|
|
131
|
+
}
|
|
132
|
+
return response.json();
|
|
133
|
+
}
|
|
134
|
+
// ── Internal helpers ───────────────────────────────────────────────────
|
|
135
|
+
async function safeJson(resp) {
|
|
136
|
+
try {
|
|
137
|
+
return (await resp.json());
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function pickErr(body) {
|
|
144
|
+
if (!body)
|
|
145
|
+
return undefined;
|
|
146
|
+
return (body.error ?? body.message ?? body.detail);
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=platform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../../src/api/platform.ts"],"names":[],"mappings":"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"}
|
package/dist/bin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":""}
|
package/dist/bin.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @reppo/cli entry point. Registers every command class and dispatches
|
|
4
|
+
* via clipanion's runExit.
|
|
5
|
+
*/
|
|
6
|
+
import { Cli, Builtins } from 'clipanion';
|
|
7
|
+
import { AuthCommand } from './commands/auth.js';
|
|
8
|
+
import { ClaimEmissionsCommand } from './commands/claim-emissions.js';
|
|
9
|
+
import { ExtendLockCommand } from './commands/extend-lock.js';
|
|
10
|
+
import { GrantAccessCommand } from './commands/grant-access.js';
|
|
11
|
+
import { LockCommand } from './commands/lock.js';
|
|
12
|
+
import { MintPodCommand } from './commands/mint-pod.js';
|
|
13
|
+
import { QueryBalanceCommand } from './commands/query/balance.js';
|
|
14
|
+
import { QueryDatanetCommand } from './commands/query/datanet.js';
|
|
15
|
+
import { QueryEmissionsDueCommand } from './commands/query/emissions-due.js';
|
|
16
|
+
import { QueryPodCommand } from './commands/query/pod.js';
|
|
17
|
+
import { QueryVotingPowerCommand } from './commands/query/voting-power.js';
|
|
18
|
+
import { RegisterAgentCommand } from './commands/register-agent.js';
|
|
19
|
+
import { UnlockCommand } from './commands/unlock.js';
|
|
20
|
+
import { VoteCommand } from './commands/vote.js';
|
|
21
|
+
const cli = new Cli({
|
|
22
|
+
binaryLabel: 'Reppo CLI',
|
|
23
|
+
binaryName: 'reppo',
|
|
24
|
+
binaryVersion: '0.1.0',
|
|
25
|
+
enableCapture: false,
|
|
26
|
+
});
|
|
27
|
+
cli.register(Builtins.HelpCommand);
|
|
28
|
+
cli.register(Builtins.VersionCommand);
|
|
29
|
+
cli.register(AuthCommand);
|
|
30
|
+
cli.register(ClaimEmissionsCommand);
|
|
31
|
+
cli.register(ExtendLockCommand);
|
|
32
|
+
cli.register(GrantAccessCommand);
|
|
33
|
+
cli.register(LockCommand);
|
|
34
|
+
cli.register(MintPodCommand);
|
|
35
|
+
cli.register(QueryBalanceCommand);
|
|
36
|
+
cli.register(QueryDatanetCommand);
|
|
37
|
+
cli.register(QueryEmissionsDueCommand);
|
|
38
|
+
cli.register(QueryPodCommand);
|
|
39
|
+
cli.register(QueryVotingPowerCommand);
|
|
40
|
+
cli.register(RegisterAgentCommand);
|
|
41
|
+
cli.register(UnlockCommand);
|
|
42
|
+
cli.register(VoteCommand);
|
|
43
|
+
// TODO: 2 commands still pending:
|
|
44
|
+
// create-datanet (browser-only — blocked: Privy session cookie auth)
|
|
45
|
+
// swap (deferred-by-scope: Uniswap V3 multi-tx flow)
|
|
46
|
+
// Wrap clipanion's runExit so any synchronous throw during command
|
|
47
|
+
// registration / arg-parsing flows through the structured `fail()`
|
|
48
|
+
// instead of leaking a raw stack to stderr (agents would lose the
|
|
49
|
+
// `code` field they key off).
|
|
50
|
+
import { fail } from './output/format.js';
|
|
51
|
+
try {
|
|
52
|
+
await cli.runExit(process.argv.slice(2));
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
fail({
|
|
56
|
+
code: 'CLI_INIT_ERROR',
|
|
57
|
+
message: err instanceof Error ? err.message : String(err),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=bin.js.map
|
package/dist/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;;GAGG;AACH,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC;IAClB,WAAW,EAAE,WAAW;IACxB,UAAU,EAAE,OAAO;IACnB,aAAa,EAAE,OAAO;IACtB,aAAa,EAAE,KAAK;CACrB,CAAC,CAAC;AAEH,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AACnC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;AAEtC,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAC1B,GAAG,CAAC,QAAQ,CAAC,qBAAqB,CAAC,CAAC;AACpC,GAAG,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC;AAChC,GAAG,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;AACjC,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAC1B,GAAG,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;AAC7B,GAAG,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC;AAClC,GAAG,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC;AAClC,GAAG,CAAC,QAAQ,CAAC,wBAAwB,CAAC,CAAC;AACvC,GAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;AAC9B,GAAG,CAAC,QAAQ,CAAC,uBAAuB,CAAC,CAAC;AACtC,GAAG,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC;AACnC,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;AAC5B,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAE1B,kCAAkC;AAClC,uEAAuE;AACvE,uDAAuD;AAEvD,mEAAmE;AACnE,mEAAmE;AACnE,kEAAkE;AAClE,8BAA8B;AAC9B,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAE1C,IAAI,CAAC;IACH,MAAM,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAC3C,CAAC;AAAC,OAAO,GAAG,EAAE,CAAC;IACb,IAAI,CAAC;QACH,IAAI,EAAE,gBAAgB;QACtB,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;KAC1D,CAAC,CAAC;AACL,CAAC"}
|