Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions src/add/types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import type { ProviderInfo } from '@filoz/synapse-sdk'
import type { CLIAuthOptions } from '../utils/cli-auth.js'

export interface AddOptions {
export interface AddOptions extends CLIAuthOptions {
filePath: string
privateKey?: string
rpcUrl?: string
bare?: boolean
/** Auto-fund: automatically ensure minimum 10 days of runway */
autoFund?: boolean
Expand Down
9 changes: 4 additions & 5 deletions src/commands/add.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
import { RPC_URLS } from '@filoz/synapse-sdk'
import { Command } from 'commander'
import { runAdd } from '../add/add.js'
import type { AddOptions } from '../add/types.js'
import { MIN_RUNWAY_DAYS } from '../common/constants.js'
import { addAuthOptions } from '../utils/cli-options.js'

export const addCommand = new Command('add')
.description('Add a file or directory to Filecoin via Synapse (creates UnixFS CAR)')
.argument('<path>', 'Path to the file or directory to add')
.option('--private-key <key>', 'Private key for Synapse (or use PRIVATE_KEY env var)')
.option('--rpc-url <url>', 'RPC URL for Filecoin network', RPC_URLS.calibration.websocket)
.option('--bare', 'Add file without directory wrapper (files only, not supported for directories)')
.option('--auto-fund', `Automatically ensure minimum ${MIN_RUNWAY_DAYS} days of runway before upload`)
.action(async (path: string, options) => {
try {
const addOptions: AddOptions = {
...options,
filePath: path,
privateKey: options.privateKey || process.env.PRIVATE_KEY,
rpcUrl: options.rpcUrl || process.env.RPC_URL,
bare: options.bare,
autoFund: options.autoFund,
}
Expand All @@ -27,3 +24,5 @@ export const addCommand = new Command('add')
process.exit(1)
}
})

addAuthOptions(addCommand)
9 changes: 4 additions & 5 deletions src/commands/data-set.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
import { RPC_URLS } from '@filoz/synapse-sdk'
import { Command } from 'commander'
import { runDataSetCommand } from '../data-set/run.js'
import type { DataSetCommandOptions } from '../data-set/types.js'
import { addAuthOptions } from '../utils/cli-options.js'

export const dataSetCommand = new Command('data-set')
.description('Inspect data sets managed through Filecoin Onchain Cloud')
.argument('[dataSetId]', 'Optional data set ID to inspect')
.option('--ls', 'List all data sets for the configured account')
.option('--private-key <key>', 'Private key (or PRIVATE_KEY env)')
.option('--rpc-url <url>', 'RPC endpoint (or RPC_URL env)', RPC_URLS.calibration.websocket)
.action(async (dataSetId: string | undefined, options) => {
try {
const commandOptions: DataSetCommandOptions = {
...options,
ls: options.ls,
privateKey: options.privateKey || process.env.PRIVATE_KEY,
rpcUrl: options.rpcUrl || process.env.RPC_URL,
}

await runDataSetCommand(dataSetId, commandOptions)
Expand All @@ -23,3 +20,5 @@ export const dataSetCommand = new Command('data-set')
process.exit(1)
}
})

addAuthOptions(dataSetCommand)
9 changes: 4 additions & 5 deletions src/commands/import.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
import { RPC_URLS } from '@filoz/synapse-sdk'
import { Command } from 'commander'
import { MIN_RUNWAY_DAYS } from '../common/constants.js'
import { runCarImport } from '../import/import.js'
import type { ImportOptions } from '../import/types.js'
import { addAuthOptions } from '../utils/cli-options.js'

export const importCommand = new Command('import')
.description('Import an existing CAR file to Filecoin via Synapse')
.argument('<file>', 'Path to the CAR file to import')
.option('--private-key <key>', 'Private key for Synapse (or use PRIVATE_KEY env var)')
.option('--rpc-url <url>', 'RPC URL for Filecoin network', RPC_URLS.calibration.websocket)
.option('--auto-fund', `Automatically ensure minimum ${MIN_RUNWAY_DAYS} days of runway before upload`)
.action(async (file: string, options) => {
try {
const importOptions: ImportOptions = {
...options,
filePath: file,
privateKey: options.privateKey || process.env.PRIVATE_KEY,
rpcUrl: options.rpcUrl || process.env.RPC_URL,
autoFund: options.autoFund,
}

Expand All @@ -25,3 +22,5 @@ export const importCommand = new Command('import')
process.exit(1)
}
})

addAuthOptions(importCommand)
65 changes: 31 additions & 34 deletions src/commands/payments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ import { runInteractiveSetup } from '../payments/interactive.js'
import { showPaymentStatus } from '../payments/status.js'
import type { FundOptions, PaymentSetupOptions } from '../payments/types.js'
import { runWithdraw } from '../payments/withdraw.js'
import { addAuthOptions } from '../utils/cli-options.js'

export const paymentsCommand = new Command('payments').description('Manage payment setup for Filecoin Onchain Cloud')

paymentsCommand
.command('setup')
// Setup command
const setupCommand = new Command('setup')
.description('Setup payment approvals for Filecoin Onchain Cloud storage')
.option('--auto', 'Run in automatic mode with defaults')
.option('--private-key <key>', 'Private key (can also use PRIVATE_KEY env)')
.option('--rpc-url <url>', 'RPC endpoint (can also use RPC_URL env)')
.option('--deposit <amount>', 'USDFC amount to deposit in Filecoin Pay (default: 1)')
.option(
'--rate-allowance <amount>',
Expand All @@ -23,9 +22,8 @@ paymentsCommand
.action(async (options) => {
try {
const setupOptions: PaymentSetupOptions = {
...options,
auto: options.auto || false,
privateKey: options.privateKey,
rpcUrl: options.rpcUrl || process.env.RPC_URL,
deposit: options.deposit || '1',
rateAllowance: options.rateAllowance || '1TiB/month',
}
Expand All @@ -41,12 +39,12 @@ paymentsCommand
}
})

// Adjust funds to an exact runway or deposited total
paymentsCommand
.command('fund')
addAuthOptions(setupCommand)
paymentsCommand.addCommand(setupCommand)

// Fund command - adjust funds to an exact runway or deposited total
const fundCommand = new Command('fund')
.description('Adjust funds to an exact runway (days) or total deposit')
.option('--private-key <key>', 'Private key (can also use PRIVATE_KEY env)')
.option('--rpc-url <url>', 'RPC endpoint (can also use RPC_URL env)')
.option('--days <n>', 'Set final runway to exactly N days (deposit or withdraw as needed)')
.option('--amount <usdfc>', 'Set final deposited total to exactly this USDFC amount (deposit or withdraw)')
.option(
Expand All @@ -56,8 +54,7 @@ paymentsCommand
.action(async (options) => {
try {
const fundOptions: FundOptions = {
privateKey: options.privateKey,
rpcUrl: options.rpcUrl || process.env.RPC_URL,
...options,
amount: options.amount,
mode: options.mode,
}
Expand All @@ -69,18 +66,17 @@ paymentsCommand
}
})

// Withdraw funds from the payments contract
paymentsCommand
.command('withdraw')
addAuthOptions(fundCommand)
paymentsCommand.addCommand(fundCommand)

// Withdraw command
const withdrawCommand = new Command('withdraw')
.description('Withdraw funds from Filecoin Pay to your wallet')
.option('--private-key <key>', 'Private key (can also use PRIVATE_KEY env)')
.option('--rpc-url <url>', 'RPC endpoint (can also use RPC_URL env)')
.requiredOption('--amount <usdfc>', 'USDFC amount to withdraw (e.g., 5)')
.action(async (options) => {
try {
await runWithdraw({
privateKey: options.privateKey,
rpcUrl: options.rpcUrl || process.env.RPC_URL,
...options,
amount: options.amount,
})
} catch (error) {
Expand All @@ -89,37 +85,35 @@ paymentsCommand
}
})

// Add a status subcommand for checking current payment status
paymentsCommand
.command('status')
addAuthOptions(withdrawCommand)
paymentsCommand.addCommand(withdrawCommand)

// Status command
const statusCommand = new Command('status')
.description('Check current payment setup status')
.option('--private-key <key>', 'Private key (can also use PRIVATE_KEY env)')
.option('--rpc-url <url>', 'RPC endpoint (can also use RPC_URL env)')
.action(async (options) => {
try {
await showPaymentStatus({
privateKey: options.privateKey,
rpcUrl: options.rpcUrl || process.env.RPC_URL,
...options,
})
} catch (error) {
console.error('Failed to get payment status:', error instanceof Error ? error.message : error)
process.exit(1)
}
})

// Add a deposit/top-up subcommand
paymentsCommand
.command('deposit')
addAuthOptions(statusCommand)
paymentsCommand.addCommand(statusCommand)

// Deposit command
const depositCommand = new Command('deposit')
.description('Deposit or top-up funds in Filecoin Pay')
.option('--private-key <key>', 'Private key (can also use PRIVATE_KEY env)')
.option('--rpc-url <url>', 'RPC endpoint (can also use RPC_URL env)')
.option('--amount <usdfc>', 'USDFC amount to deposit (e.g., 10.5)')
.option('--days <n>', 'Fund enough to keep current spend alive for N days')
.action(async (options) => {
try {
await runDeposit({
privateKey: options.privateKey,
rpcUrl: options.rpcUrl || process.env.RPC_URL,
...options,
amount: options.amount,
days: options.days != null ? Number(options.days) : undefined, // Only pass days if explicitly provided
})
Expand All @@ -128,3 +122,6 @@ paymentsCommand
process.exit(1)
}
})

addAuthOptions(depositCommand)
paymentsCommand.addCommand(depositCommand)
51 changes: 15 additions & 36 deletions src/data-set/run.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { EnhancedDataSetInfo, ProviderInfo } from '@filoz/synapse-sdk'
import { PDPServer, PDPVerifier, RPC_URLS, Synapse, WarmStorageService } from '@filoz/synapse-sdk'
import type { EnhancedDataSetInfo, ProviderInfo, Synapse } from '@filoz/synapse-sdk'
import { PDPServer, PDPVerifier, WarmStorageService } from '@filoz/synapse-sdk'
import pc from 'picocolors'
import { cleanupProvider } from '../core/synapse/index.js'
import { cleanupSynapseService, initializeSynapse } from '../core/synapse/index.js'
import { getCLILogger, parseCLIAuth } from '../utils/cli-auth.js'
import { cancel, createSpinner, intro, outro } from '../utils/cli-helpers.js'
import { log } from '../utils/cli-logger.js'
import { displayDataSetList, displayDataSetStatus } from './inspect.js'
Expand Down Expand Up @@ -158,29 +159,6 @@ async function loadDetailedDataSet(detail: DataSetDetail, synapse: Synapse): Pro
return result
}

/**
* Resolve the private key from CLI options or environment variables.
*
* @throws Never returns when the key cannot be resolved; exits the process instead.
*/
async function ensurePrivateKey(options: DataSetCommandOptions): Promise<string> {
const privateKey = options.privateKey ?? process.env.PRIVATE_KEY
if (!privateKey) {
log.line(pc.red('Error: Private key required via --private-key or PRIVATE_KEY env'))
log.flush()
cancel('Data set inspection cancelled')
process.exit(1)
}
return privateKey
}

/**
* Resolve the RPC endpoint, preferring CLI options over environment defaults.
*/
function resolveRpcUrl(options: DataSetCommandOptions): string {
return options.rpcUrl ?? process.env.RPC_URL ?? RPC_URLS.calibration.websocket
}

/**
* Entry point invoked by the Commander command.
*
Expand All @@ -195,26 +173,27 @@ export async function runDataSetCommand(
const hasDataSetId = dataSetIdInput != null
const shouldList = options.ls === true || !hasDataSetId

const privateKey = await ensurePrivateKey(options)
const rpcUrl = resolveRpcUrl(options)

intro(pc.bold('Filecoin Onchain Cloud Data Sets'))
const spinner = createSpinner()
spinner.start('Connecting to Synapse...')

let synapse: Synapse | null = null
let provider: any = null

try {
synapse = await Synapse.create({ privateKey, rpcURL: rpcUrl })
// Parse and validate authentication
const authConfig = parseCLIAuth({
privateKey: options.privateKey,
walletAddress: options.walletAddress,
sessionKey: options.sessionKey,
rpcUrl: options.rpcUrl,
})

const logger = getCLILogger()
synapse = await initializeSynapse(authConfig, logger)
const network = synapse.getNetwork()
const signer = synapse.getSigner()
const address = await signer.getAddress()

if (/^wss?:\/\//i.test(rpcUrl)) {
provider = synapse.getProvider()
}

spinner.message('Fetching data set information...')

const [dataSets, storageInfo] = await Promise.all([
Expand Down Expand Up @@ -311,6 +290,6 @@ export async function runDataSetCommand(
cancel('Inspection failed')
process.exitCode = 1
} finally {
await cleanupProvider(provider)
await cleanupSynapseService()
}
}
5 changes: 2 additions & 3 deletions src/data-set/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { EnhancedDataSetInfo, ProviderInfo } from '@filoz/synapse-sdk'
import type { CLIAuthOptions } from '../utils/cli-auth.js'

export interface PieceDetail {
pieceId: number
Expand All @@ -22,8 +23,6 @@ export interface DataSetInspectionContext {
dataSets: DataSetDetail[]
}

export interface DataSetCommandOptions {
export interface DataSetCommandOptions extends CLIAuthOptions {
ls?: boolean
privateKey?: string
rpcUrl?: string
}
5 changes: 2 additions & 3 deletions src/import/types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import type { ProviderInfo } from '@filoz/synapse-sdk'
import type { CLIAuthOptions } from '../utils/cli-auth.js'

export interface ImportOptions {
export interface ImportOptions extends CLIAuthOptions {
filePath: string
privateKey?: string
rpcUrl?: string
/** Auto-fund: automatically ensure minimum 10 days of runway */
autoFund?: boolean
}
Expand Down
Loading