Skip to content

Commit a1c687a

Browse files
committed
feat: enhance repair CLI with session key management and improve wallet commands
- Added new commands for session key management: `repair session-key approve` and `repair session-key revoke`, including options for expiration and origin. - Updated `repair wallet balance`, `deposit`, and `withdraw` commands to return transaction hashes and improve user feedback with interactive prompts. - Enhanced dataset commands to include a payer address option for better flexibility in dataset management. - Refactored database operations to track transaction hashes instead of results for improved clarity and functionality.
1 parent 6242e94 commit a1c687a

14 files changed

Lines changed: 374 additions & 58 deletions

File tree

packages/repair-cli/readme.md

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,11 @@ This command only works on Calibration. It claims faucet tokens, waits for the t
7373

7474
### `repair wallet balance`
7575

76-
Shows wallet and payment account balances.
76+
Shows wallet and payment account balances for the configured wallet, or for a provided address.
7777

7878
```bash
7979
repair wallet balance
80+
repair wallet balance --address 0x1234567890123456789012345678901234567890
8081
```
8182

8283
The output includes the wallet address, FIL balance, USDFC balance, and Filecoin Pay account summary fields such as funds, available funds, debt, lockup rates, lockup totals, runway, and current epoch.
@@ -89,7 +90,7 @@ Deposits USDFC from the configured wallet into the wallet's Filecoin Pay account
8990
repair wallet deposit 100
9091
```
9192

92-
`amount` is a positive USDFC amount. The command submits the deposit and approval transaction, then waits for it to be mined.
93+
`amount` is a positive USDFC amount. The command submits the deposit and approval transaction, waits for it to be mined, and returns the amount and transaction hash when structured output is requested.
9394

9495
### `repair wallet withdraw <amount>`
9596

@@ -99,7 +100,39 @@ Withdraws USDFC from the wallet's Filecoin Pay account.
99100
repair wallet withdraw 25
100101
```
101102

102-
`amount` is a positive USDFC amount. The command submits the withdraw transaction and waits for it to be mined.
103+
`amount` is a positive USDFC amount. The command submits the withdraw transaction, waits for it to be mined, and returns the amount and transaction hash when structured output is requested.
104+
105+
### `repair session-key approve <address>`
106+
107+
Approves a session key for storage operations using the configured wallet.
108+
109+
```bash
110+
repair session-key approve 0x1234567890123456789012345678901234567890
111+
```
112+
113+
By default, the session key is approved for 100 days. Use `--expires-in-days` to choose a different duration, and `--origin` to override the origin string recorded on-chain.
114+
115+
```bash
116+
repair session-key approve 0x1234567890123456789012345678901234567890 --expires-in-days 30 --origin early-repair
117+
```
118+
119+
The command waits for the approval transaction to be mined and returns the session key address, expiry timestamp, and transaction hash when structured output is requested.
120+
121+
### `repair session-key revoke <address>`
122+
123+
Revokes a session key for storage operations using the configured wallet.
124+
125+
```bash
126+
repair session-key revoke 0x1234567890123456789012345678901234567890
127+
```
128+
129+
Use `--origin` to override the origin string recorded on-chain.
130+
131+
```bash
132+
repair session-key revoke 0x1234567890123456789012345678901234567890 --origin early-repair
133+
```
134+
135+
The command waits for the revoke transaction to be mined and returns the session key address and transaction hash when structured output is requested.
103136

104137
### `repair providers list`
105138

@@ -208,11 +241,13 @@ Options:
208241

209242
- `--concurrency <number>` controls how many pull batches run at once. Defaults to `4`.
210243
- `--batch-size <number>` controls the maximum number of `add_piece` operations per batch. Defaults to `40`.
244+
- `--payer <address>` overrides the payer address used when creating or finding the target repair dataset. Defaults to the configured wallet address.
211245

212246
Example:
213247

214248
```bash
215249
repair repair run 1 --concurrency 8 --batch-size 40
250+
repair repair run 1 --payer 0x1234567890123456789012345678901234567890
216251
```
217252

218253
### `repair repair delete <repairId>`
@@ -270,11 +305,13 @@ Options:
270305

271306
- `--concurrency <number>` controls how many pull batches run at once. Defaults to `4`.
272307
- `--batch-size <number>` controls the maximum number of `add_piece` operations per batch. Defaults to `40`.
308+
- `--payer <address>` overrides the payer address used when creating the target replication dataset. Defaults to the configured wallet address.
273309

274310
Example:
275311

276312
```bash
277313
repair replicate run 1 --concurrency 8 --batch-size 40
314+
repair replicate run 1 --payer 0x1234567890123456789012345678901234567890
278315
```
279316

280317
### `repair replicate delete <replicateId>`

packages/repair-cli/src/cli.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { datasets } from './commands/datasets.ts'
44
import { providers } from './commands/providers.ts'
55
import { repair } from './commands/repair.ts'
66
import { replicate } from './commands/replicate.ts'
7+
import { sessionKey } from './commands/session-key.ts'
78
import { setup } from './commands/setup.ts'
89
import { wallet } from './commands/wallet.ts'
910
import { version } from './utils.ts'
@@ -19,4 +20,5 @@ cli.command(repair)
1920
cli.command(replicate)
2021
cli.command(datasets)
2122
cli.command(providers)
23+
cli.command(sessionKey)
2224
cli.serve()

packages/repair-cli/src/commands/datasets.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import * as SP from '@filoz/synapse-core/sp'
33
import { getPdpDataSet } from '@filoz/synapse-core/warm-storage'
44
import { and, asc, eq } from 'drizzle-orm'
55
import { Cli, z } from 'incur'
6+
import { isAddress } from 'viem'
67
import { contextMiddleware, contextSchema } from '../middleware.ts'
78
import { globalOptions, hashLink } from '../utils.ts'
9+
810
export const datasets = Cli.create('datasets', {
911
description: 'Dataset commands',
1012
options: globalOptions,
@@ -15,15 +17,15 @@ datasets.command('list', {
1517
description: 'List all datasets owned by the repair wallet',
1618
options: globalOptions.extend({
1719
providerId: z.coerce.bigint().optional().describe('Filter datasets by provider ID'),
20+
payer: z.string().refine(isAddress, 'Invalid address').optional().describe('Filter datasets by payer address'),
1821
}),
1922
middleware: [contextMiddleware],
2023
run: async (c) => {
2124
try {
2225
const schema = c.var.indexerDb._.fullSchema
23-
const conditions = [
24-
eq(schema.dataSets.deleted, false),
25-
eq(schema.dataSets.payer, c.var.client.account.address.toLowerCase()),
26-
]
26+
const payer = c.options.payer?.toLowerCase() ?? c.var.client.account.address.toLowerCase()
27+
const conditions = [eq(schema.dataSets.deleted, false), eq(schema.dataSets.payer, payer)]
28+
2729
if (c.options.providerId != null) {
2830
conditions.push(eq(schema.dataSets.providerId, c.options.providerId))
2931
}
@@ -76,7 +78,7 @@ datasets.command('terminate', {
7678
middleware: [contextMiddleware],
7779
outputPolicy: 'agent-only',
7880
run: async (c) => {
79-
const isInteractive = !c.agent && !c.formatExplicit
81+
const { isInteractive } = c.var
8082
const spinner = p.spinner()
8183
try {
8284
if (isInteractive) {

packages/repair-cli/src/commands/repair.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { and, desc, eq, inArray, isNull } from 'drizzle-orm'
22
import { Cli, z } from 'incur'
3+
import { isAddress } from 'viem'
34
import { repairCreate } from '../db/repair-create.ts'
45
import { repairDelete } from '../db/repair-delete.ts'
56
import { contextMiddleware, contextSchema } from '../middleware.ts'
@@ -135,11 +136,13 @@ repair.command('run', {
135136
options: globalOptions.extend({
136137
concurrency: z.coerce.number().min(1).max(10).default(4).describe('Concurrency level'),
137138
batchSize: z.coerce.number().min(1).max(40).default(40).describe('Max pieces per batch'),
139+
payer: z.string().refine(isAddress, 'Invalid address').optional().describe('Payer address'),
138140
}),
139141
middleware: [contextMiddleware],
140142
run: async (c) => {
141143
try {
142144
const schema = c.var.localDb._.fullSchema
145+
const payer = c.options.payer ?? c.var.client.account.address
143146
const repair = await c.var.localDb.query.repairs.findFirst({
144147
where: and(
145148
eq(schema.repairs.id, c.args.repairId),
@@ -158,6 +161,7 @@ repair.command('run', {
158161
await ensureRepairDataset({
159162
...c.var,
160163
repair,
164+
payer,
161165
})
162166

163167
await runAddPieces({

packages/repair-cli/src/commands/replicate.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { and, desc, eq, inArray, isNotNull } from 'drizzle-orm'
22
import { Cli, z } from 'incur'
3+
import { isAddress } from 'viem'
34
import { repairDelete } from '../db/repair-delete.ts'
45
import { replicateCreate } from '../db/replicate-create.ts'
56
import { contextMiddleware, contextSchema } from '../middleware.ts'
@@ -136,11 +137,13 @@ replicate.command('run', {
136137
options: globalOptions.extend({
137138
concurrency: z.coerce.number().min(1).max(10).default(4).describe('Concurrency level'),
138139
batchSize: z.coerce.number().min(1).max(40).default(40).describe('Max pieces per batch'),
140+
payer: z.string().refine(isAddress, 'Invalid address').optional().describe('Payer address'),
139141
}),
140142
middleware: [contextMiddleware],
141143
run: async (c) => {
142144
try {
143145
const schema = c.var.localDb._.fullSchema
146+
const payer = c.options.payer ?? c.var.client.account.address
144147
const repair = await c.var.localDb.query.repairs.findFirst({
145148
where: and(
146149
eq(schema.repairs.id, c.args.replicateId),
@@ -159,6 +162,7 @@ replicate.command('run', {
159162
await ensureReplicateDataset({
160163
...c.var,
161164
repair,
165+
payer,
162166
source: c.var.source,
163167
})
164168

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import * as p from '@clack/prompts'
2+
import * as SessionKey from '@filoz/synapse-core/session-key'
3+
import { Cli, z } from 'incur'
4+
import { type Address, isAddress } from 'viem'
5+
import { contextMiddleware, contextSchema } from '../middleware.ts'
6+
import { globalOptions, hashLink } from '../utils.ts'
7+
8+
const sessionKeyAddressArgs = z.object({
9+
address: z.string().refine(isAddress, 'Invalid session key address').describe('Session key address'),
10+
})
11+
12+
export const sessionKey = Cli.create('session-key', {
13+
description: 'Session key commands',
14+
vars: contextSchema,
15+
})
16+
17+
sessionKey.command('approve', {
18+
description: 'Approve a session key for storage operations',
19+
args: sessionKeyAddressArgs,
20+
options: globalOptions.extend({
21+
expiresInDays: z.coerce.number().gt(0).default(100).describe('Session key expiry duration in days'),
22+
origin: z.string().optional().describe('Origin recorded on-chain for the authorization'),
23+
}),
24+
middleware: [contextMiddleware],
25+
outputPolicy: 'agent-only',
26+
run: async (c) => {
27+
const { client, chain, source, isInteractive } = c.var
28+
const address = c.args.address as Address
29+
const expiresAt = BigInt(Math.floor(Date.now() / 1000 + c.options.expiresInDays * 24 * 60 * 60))
30+
const spinner = p.spinner()
31+
32+
try {
33+
if (isInteractive) {
34+
spinner.start(`Approving session key ${address}...`)
35+
}
36+
const result = await SessionKey.loginSync(client, {
37+
address,
38+
expiresAt,
39+
origin: c.options.origin ?? source,
40+
onHash: (hash) => {
41+
if (isInteractive) {
42+
spinner.message(`Waiting for tx ${hashLink(hash, chain)} to be mined...`)
43+
}
44+
},
45+
})
46+
47+
if (isInteractive) {
48+
spinner.stop(`Session key ${address} approved successfully.`)
49+
}
50+
51+
return c.ok({
52+
address,
53+
expiresAt: expiresAt.toString(),
54+
transactionHash: result.receipt.transactionHash,
55+
})
56+
} catch (error) {
57+
const msg = error instanceof Error ? error.message : 'Failed to approve session key'
58+
if (isInteractive) {
59+
spinner.error(msg)
60+
}
61+
if (c.options.debug) {
62+
console.error(error)
63+
}
64+
return c.error({
65+
code: 'FAILED_TO_APPROVE_SESSION_KEY',
66+
message: msg,
67+
})
68+
}
69+
},
70+
})
71+
72+
sessionKey.command('revoke', {
73+
description: 'Revoke a session key for storage operations',
74+
args: sessionKeyAddressArgs,
75+
options: globalOptions.extend({
76+
origin: z.string().optional().describe('Origin recorded on-chain for the revocation'),
77+
}),
78+
middleware: [contextMiddleware],
79+
outputPolicy: 'agent-only',
80+
run: async (c) => {
81+
const { client, chain, source, isInteractive } = c.var
82+
const address = c.args.address as Address
83+
const spinner = p.spinner()
84+
85+
try {
86+
if (isInteractive) {
87+
spinner.start(`Revoking session key ${address}...`)
88+
}
89+
const result = await SessionKey.revokeSync(client, {
90+
address,
91+
origin: c.options.origin ?? source,
92+
onHash: (hash) => {
93+
if (isInteractive) {
94+
spinner.message(`Waiting for tx ${hashLink(hash, chain)} to be mined...`)
95+
}
96+
},
97+
})
98+
99+
if (isInteractive) {
100+
spinner.stop(`Session key ${address} revoked successfully.`)
101+
}
102+
103+
return c.ok({
104+
address,
105+
transactionHash: result.receipt.transactionHash,
106+
})
107+
} catch (error) {
108+
const msg = error instanceof Error ? error.message : 'Failed to revoke session key'
109+
if (isInteractive) {
110+
spinner.error(msg)
111+
}
112+
if (c.options.debug) {
113+
console.error(error)
114+
}
115+
return c.error({
116+
code: 'FAILED_TO_REVOKE_SESSION_KEY',
117+
message: msg,
118+
})
119+
}
120+
},
121+
})

0 commit comments

Comments
 (0)