diff --git a/.biomeignore b/.biomeignore new file mode 100644 index 0000000..ee55869 --- /dev/null +++ b/.biomeignore @@ -0,0 +1,3 @@ +bin/ +ai-generated.js + diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 0000000..5d9ec5d --- /dev/null +++ b/.cursorrules @@ -0,0 +1,350 @@ +# Investec Programmable Banking CLI - Cursor Rules + +## Project Technology Stack + +### Core Technologies +- **Node.js**: >=20.0.0 (ESM modules only) +- **TypeScript**: 5.9.3 with strict mode +- **Module System**: ESM (ES Modules) - all imports use `.js` extension +- **CLI Framework**: Commander.js 14.x +- **Testing**: Vitest 3.x with ESM support +- **Linting/Formatting**: Biome 2.3.x (replaces ESLint/Prettier) +- **Build Tool**: TypeScript compiler (tsc) + +### Key Dependencies +- `commander`: CLI framework for command definitions +- `chalk`: Terminal colors (v5, respects NO_COLOR/FORCE_COLOR) +- `ora`: Spinner/progress indicators +- `cli-table3`: Enhanced table formatting +- `js-yaml`: YAML serialization +- `@inquirer/prompts`: Interactive prompts (confirm, input, password) +- `investec-pb-api`: Programmable Banking API client +- `investec-card-api`: Card API client +- `programmable-card-code-emulator`: Local code execution emulator + +## Project Setup + +### Directory Structure +``` +src/ + ├── index.ts # Entry point, command registration, global options + ├── utils.ts # Shared utilities (2500+ lines of helper functions) + ├── errors.ts # CliError class, ExitCode enum, ERROR_CODES + ├── cmds/ # Command implementations (30+ commands) + │ ├── index.ts # Command exports + │ ├── types.ts # Shared interfaces (CommonOptions, Credentials, etc.) + │ └── *.ts # Individual command files + ├── mock-pb.ts # Mock Programmable Banking API for testing + └── mock-card.ts # Mock Card API for testing + +test/ + ├── __mocks__/ # Mock implementations (ora, utils, external-editor, etc.) + ├── cmds/ # Command tests + ├── helpers.ts # Test helper utilities + └── setup.js # Vitest setup + +bin/ # Compiled output (gitignored, generated) +templates/ # Project templates (default, petro) +``` + +### Build & Development +- **Build**: `npm run build` → TypeScript compiles `src/` to `bin/` +- **Source Maps**: Enabled for debugging +- **Declaration Files**: Generated for library consumers +- **File Copying**: Templates and assets copied to `bin/` during build + +### TypeScript Configuration +- **Target**: ES2022 +- **Module**: NodeNext (ESM) +- **Strict Mode**: Enabled with `noUncheckedIndexedAccess` and `noImplicitOverride` +- **Module Resolution**: NodeNext +- **Import Extensions**: Must use `.js` for ESM imports (TypeScript requirement) + +## Code Patterns & Conventions + +### Error Handling Pattern +```typescript +// 1. Use CliError for user-facing errors +throw new CliError(ERROR_CODES.MISSING_CARD_KEY, 'Card key is required'); + +// 2. Wrap commands with context +export const myCommand = withCommandContext('my-command', async (options) => { + // Command implementation +}); + +// 3. Centralized error handling in main() +catch (error) { + handleCliError(error, { verbose: getVerboseMode(true) }, 'command name'); +} +``` + +### Command Structure Pattern +```typescript +export async function commandName(options: CommonOptions) { + // 1. Validate inputs + const normalizedPath = await validateFilePath(options.filename, ['.js']); + + // 2. Initialize API with retry support + const api = await initializePbApi(credentials, options); + const verbose = getVerboseMode(options.verbose); + + // 3. Show progress (if not piped) + const spinner = createSpinner(!isStdoutPiped(), getSafeText('💳 Processing...')); + + // 4. Make API calls with retry + const result = await withRetry(() => api.someMethod(), { verbose }); + + // 5. Format output + formatOutput(result.data, options); +} +``` + +### Utility Functions Usage +- **Credentials**: `loadCredentialsFile()`, `readCredentialsFile()`, `writeCredentialsFile()` +- **Profiles**: `getActiveProfile()`, `setActiveProfile()`, `readProfile()`, `deleteProfile()` +- **Terminal**: `getSafeText()`, `detectTerminalCapabilities()`, `getTerminalDimensions()` +- **Output**: `formatOutput()`, `printTable()`, `formatFileSize()` +- **Validation**: `validateAmount()`, `validateAccountId()`, `validateFilePath()` +- **API**: `initializeApi()`, `initializePbApi()`, `withRetry()` +- **Security**: `warnAboutSecretUsage()`, `detectSecretUsageFromEnv()` +- **History**: `logCommandHistory()`, `readCommandHistory()` + +### Import Patterns +```typescript +// Node.js built-ins (use node: prefix) +import { promises as fsPromises } from 'node:fs'; +import { homedir } from 'node:os'; + +// External packages +import chalk from 'chalk'; +import { Command } from 'commander'; + +// Internal modules (use .js extension) +import { CliError, ERROR_CODES } from '../errors.js'; +import { formatOutput, getVerboseMode } from '../utils.js'; + +// Type-only imports +import type { CommonOptions, Credentials } from './types.js'; +``` + +## Testing Patterns + +### Mock Setup +```typescript +vi.mock('../../src/utils.ts', async () => { + const actual = await vi.importActual(); + return { + ...actual, + initializeApi: vi.fn(), + createSpinner: vi.fn(() => ({ + start: vi.fn(function() { return this; }), + stop: vi.fn(), + text: '', + })), + }; +}); +``` + +### Test Structure +- Use `describe()` blocks for grouping +- Use `it()` for individual test cases +- Mock external dependencies +- Test both success and error paths +- Use `expect().toThrow(CliError)` for error testing + +## Code Quality Standards + +### Biome Configuration +- **Quote Style**: Single quotes +- **Semicolons**: Always +- **Indentation**: 2 spaces +- **Line Width**: 100 characters +- **Trailing Commas**: ES5 style +- **Arrow Parentheses**: Always + +### Linting Rules +- `noExplicitAny`: Error (use `biome-ignore` comments when necessary, e.g., generic function wrappers) +- `noUnusedVariables`: Error +- `noUnusedImports`: Auto-fixed +- Recommended rules enabled + +### Code Style Requirements +1. **JSDoc Comments**: Required for exported functions +2. **Type Safety**: Avoid `any`, use proper types or `unknown` +3. **Error Handling**: Always use `CliError` for user-facing errors +4. **Async/Await**: Prefer over promises +5. **Terminal Safety**: Use `getSafeText()` for emoji/Unicode text +6. **Progress Indicators**: Use `createSpinner()` for operations +7. **Output Formatting**: Use `formatOutput()` for structured data + +## Environment-Specific Behavior + +### Terminal Detection +- Automatically detects terminal capabilities (Unicode/emoji support) +- Falls back to ASCII when terminal doesn't support emojis +- Respects `TERM`, `NO_COLOR`, `FORCE_COLOR` environment variables +- Detects piped output (`isStdoutPiped()`) to suppress interactive elements + +### Non-Interactive Mode +- Detects CI/CD environments (GitHub Actions, GitLab CI, etc.) +- Shows security warnings when secrets are in environment variables +- Suppresses interactive prompts when appropriate + +### Verbose/Debug Mode +- `--verbose` flag or `DEBUG=1` environment variable +- Shows detailed API calls, rate limit info, retry attempts +- Uses `getVerboseMode()` utility to check both sources + +## Security Considerations + +### Secret Management +- **Preferred**: Store in credential files (`~/.ipb/.credentials.json` or profiles) +- **Warning**: Environment variables are detected and warned about +- **File Permissions**: Credential files use `0o600` (read/write owner only) +- **Atomic Writes**: `writeFileAtomic()` ensures data integrity + +### Input Validation +- Always validate user inputs (amounts, account IDs, file paths) +- Use validation utilities: `validateAmount()`, `validateAccountId()`, `validateFilePath()` +- Provide clear error messages with suggestions + +## Command Development Guidelines + +### Adding New Commands +1. Create file in `src/cmds/` following naming convention (`kebab-case.ts`) +2. Export function following pattern: `export async function commandName(options: Options)` +3. Add to `src/cmds/index.ts` exports +4. Register in `src/index.ts` with `.command()`, `.description()`, `.action()` +5. Add options using `.option()` or `.requiredOption()` +6. Add tests in `test/cmds/` +7. Update shell completion scripts if needed +8. Add to `GENERATED_README.md` (auto-generated via `npm run docs`) + +### Command Options +- Use `CommonOptions` interface for shared options (verbose, json, yaml, output, profile, etc.) +- Add command-specific options to command's `Options` interface +- Use `.requiredOption()` for required inputs +- Provide helpful descriptions and examples + +### Output Formatting +- Support `--json`, `--yaml`, `--output` flags via `formatOutput()` +- Automatically detect piped output and use JSON format +- Use `printTable()` for tabular data (with `cli-table3`) +- Show file sizes in progress indicators using `formatFileSize()` + +### Destructive Operations +- Require confirmation using `confirmDestructiveOperation()` +- Support `--yes` flag to bypass confirmation +- Check for non-interactive mode to auto-confirm when appropriate + +## File Operations + +### File Path Handling +- Use `validateFilePath()` for reading files +- Use `validateFilePathForWrite()` for writing files +- Use `normalizeFilePath()` to expand `~` and resolve paths +- Check file extensions and permissions + +### Atomic Operations +- Use `writeFileAtomic()` for credential files and critical data +- Ensures data integrity with temp file + rename pattern +- Handles permissions and cleanup automatically + +## API Integration + +### API Client Initialization +- Use `initializeApi()` for Card API +- Use `initializePbApi()` for Programmable Banking API +- Both support credential overrides via options +- Both validate credentials before initialization + +### Rate Limiting +- All API calls wrapped in `withRetry()` for automatic retry +- Exponential backoff with jitter +- Detects rate limits from error responses +- Shows rate limit info in verbose mode + +### Error Handling +- API errors caught and converted to `CliError` when appropriate +- Rate limit errors automatically retried +- Network errors provide actionable suggestions +- Validation errors show specific field requirements + +## Testing Guidelines + +### Test File Structure +- One test file per command in `test/cmds/` +- Mock external dependencies (API clients, file system, etc.) +- Test both success and error cases +- Use `vi.hoisted()` for shared mocks + +### Mock Patterns +- Mock `../../src/utils.ts` with actual implementations + overrides +- Mock `../../src/index.ts` for credentials and shared exports +- Mock file system operations +- Mock terminal capabilities for consistent output + +## Common Utilities Reference + +### Credential Management +- `loadCredentialsFile()`: Load credentials with profile support +- `readCredentialsFile()`: Read credentials file async +- `readCredentialsFileSync()`: Read credentials file sync (module init) +- `writeCredentialsFile()`: Write credentials with atomic operation +- `validateCredentialsFile()`: Validate required fields + +### Profile Management +- `getActiveProfile()`: Get currently active profile name +- `setActiveProfile()`: Set active profile (atomic write) +- `readProfile()`: Read profile credentials +- `deleteProfile()`: Delete profile and credentials +- `listProfiles()`: List all available profiles + +### Terminal & Output +- `getSafeText()`: Get terminal-safe text (emoji fallbacks) +- `formatOutput()`: Format data as JSON/YAML/table +- `printTable()`: Print formatted table using cli-table3 +- `createSpinner()`: Create progress spinner (auto-handles pipe mode) +- `formatFileSize()`: Format bytes as human-readable size + +### Validation +- `validateAmount()`: Validate monetary amounts +- `validateAccountId()`: Validate account ID format +- `validateFilePath()`: Validate file path for reading +- `validateFilePathForWrite()`: Validate file path for writing + +### API & Retry +- `initializeApi()`: Initialize Card API client +- `initializePbApi()`: Initialize Programmable Banking API client +- `withRetry()`: Wrap function with retry logic (rate limit handling) +- `detectRateLimit()`: Detect rate limit from error +- `formatRateLimitInfo()`: Format rate limit info for display + +### Security & Environment +- `warnAboutSecretUsage()`: Warn about secrets in environment variables +- `detectSecretUsageFromEnv()`: Detect secrets loaded from env vars +- `isNonInteractiveEnvironment()`: Detect CI/CD/script environments +- `getVerboseMode()`: Get effective verbose mode (flag or DEBUG env) + +## Important Notes + +1. **ESM Only**: All imports must use `.js` extension (TypeScript requirement for ESM) +2. **Type Safety**: Strict TypeScript with `noUncheckedIndexedAccess` +3. **Terminal Safety**: Always use `getSafeText()` for emoji/Unicode output +4. **Error Context**: Use `withCommandContext()` to attach command names to errors +5. **Rate Limiting**: Always use `withRetry()` for API calls +6. **Security**: Prefer credential files over environment variables for secrets +7. **Testing**: Mock terminal capabilities for consistent test output +8. **Documentation**: Generate docs with `npm run docs` (updates GENERATED_README.md) + +## When Writing Code + +- Follow existing patterns in the codebase +- Use provided utility functions instead of reinventing +- Check for terminal capabilities before using advanced features +- Support both interactive and non-interactive (CI/CD) environments +- Provide clear, actionable error messages +- Include JSDoc comments for exported functions +- Write tests for new commands +- Run `npm run lint:fix` before committing + diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index f157f98..557918c 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,11 +1,248 @@ -This project is a command line tool for managing and interacting with Investec banks API services. It provides various commands to perform operations such as getting a list of bank accounts, balances, and transactions. +# Investec Programmable Banking CLI -the service also provides a way to write js snippets that are loaded on to your bank card account and executed when you make a payment. This allows you to automate certain actions or perform custom logic when spending money. +## Project Overview -The project uses commanderjs for the command line interface, and it is designed to be run in a Node.js environment. The code is structured to allow for easy addition of new commands and features. +This is a command-line interface (CLI) tool for managing and interacting with Investec's Programmable Banking API. It allows users to deploy JavaScript code snippets to their programmable bank cards, which execute when transactions are made. The tool also provides commands for managing accounts, balances, transactions, beneficiaries, and more. -Commands can be found in src/cmds and the entry point is in src/index.ts. The project also includes a configuration file for managing settings and options. +## Technology Stack -The project is distributed via npm and can be installed globally or used as a local dependency in other projects. +- **Runtime**: Node.js 20+ (ESM modules) +- **Language**: TypeScript 5.9+ with strict mode enabled +- **CLI Framework**: Commander.js 14.x +- **Testing**: Vitest 3.x +- **Linting/Formatting**: Biome 2.3.x +- **Build**: TypeScript compiler (tsc) +- **Package Manager**: npm -It is compiled using TypeScript, and the source code is organized into modules for better maintainability. The project also includes unit tests to ensure the functionality of the commands and features. this is done via npm run build. +## Project Structure + +```text +src/ + ├── index.ts # Main entry point, command definitions, global options + ├── utils.ts # Shared utilities (error handling, credentials, formatting, etc.) + ├── errors.ts # Custom error classes and error codes + ├── cmds/ # Command implementations + │ ├── index.ts # Command exports + │ ├── types.ts # Shared TypeScript interfaces + │ └── *.ts # Individual command files + ├── mock-pb.ts # Mock API for testing/development + └── mock-card.ts # Mock card API for testing + +test/ + ├── __mocks__/ # Mock implementations for testing + └── cmds/ # Command-specific tests + +bin/ # Compiled output (generated, not committed) +templates/ # Project templates for `ipb new` +``` + +## Key Architectural Patterns + +### Error Handling + +- **Custom Error Class**: `CliError` extends `Error` with error codes +- **Centralized Handler**: `handleCliError()` in `utils.ts` provides consistent error formatting +- **Exit Codes**: `ExitCode` enum for different error types (validation, auth, file, API, etc.) +- **Error Context**: `withCommandContext()` wrapper attaches command names to errors +- **Actionable Messages**: Errors include suggestions for fixing common issues + +```typescript +// Example error usage +throw new CliError(ERROR_CODES.MISSING_CARD_KEY, 'Card key is required'); +``` + +### Command Structure + +Commands follow a consistent pattern: + +1. Validate inputs using utility functions +2. Initialize API clients (with retry/rate limit handling) +3. Show progress indicators (spinners) when appropriate +4. Format output (table, JSON, YAML) based on options +5. Handle errors with context + +```typescript +// Example command structure +export async function accountsCommand(options: CommonOptions) { + const api = await initializePbApi(credentials, options); + const verbose = getVerboseMode(options.verbose); + const result = await withRetry(() => api.getAccounts(), { verbose }); + formatOutput(result.data, options); +} +``` + +### Utility Functions + +Key utilities in `src/utils.ts`: + +- **Credential Management**: Secure file operations with atomic writes +- **Profile Management**: Multiple credential profiles support +- **Terminal Capabilities**: Unicode/emoji detection and ASCII fallbacks +- **Output Formatting**: Table, JSON, YAML with automatic formatting +- **Rate Limiting**: Automatic retry with exponential backoff +- **Progress Indicators**: File size-aware spinners +- **Secret Detection**: Warnings for insecure secret usage +- **Command History**: Logging of executed commands + +### Configuration Management + +- **Credentials**: Stored in `~/.ipb/.credentials.json` with restricted permissions (600) +- **Profiles**: Multiple profiles in `~/.ipb/profiles/.json` +- **Active Profile**: Tracked in `~/.ipb/active-profile.json` +- **Priority Order**: Command args → Profile → Environment vars → Credentials file +- **Security**: Warns when secrets are loaded from environment variables + +### Testing + +- **Framework**: Vitest with ESM support +- **Mocking**: `vi.mock()` for modules, `vi.hoisted()` for shared mocks +- **Test Structure**: Commands tested in `test/cmds/*.test.ts` +- **Mocks**: Located in `test/__mocks__/` for external dependencies +- **Coverage**: Configured in `vitest.config.ts` + +## Coding Standards + +### TypeScript + +- **Strict Mode**: Enabled with `noUncheckedIndexedAccess` and `noImplicitOverride` +- **Module System**: ESM only (`"type": "module"`) +- **Target**: ES2022 +- **Imports**: Use `.js` extension for ESM imports (TypeScript requirement) + +### Linting & Formatting + +- **Tool**: Biome (replaces ESLint/Prettier) +- **Configuration**: `biome.json` +- **Rules**: + - `noExplicitAny`: Error (use `biome-ignore` comments when necessary) + - `noUnusedVariables`: Error + - Recommended rules enabled +- **Auto-fix**: Run `npm run lint:fix` to auto-fix issues + +### Code Style + +- **Quotes**: Single quotes +- **Semicolons**: Always +- **Indentation**: 2 spaces +- **Line Width**: 100 characters +- **Trailing Commas**: ES5 style +- **Arrow Functions**: Always parentheses + +### Import Organization + +- Biome automatically organizes imports when running `lint:fix` +- Grouping: External packages → Node.js built-ins → Internal/relative → Type imports +- Alphabetical within each group + +## Best Practices + +### Error Handling Best Practices + +1. Use `CliError` with error codes from `ERROR_CODES` for user-facing errors +2. Wrap command handlers with `withCommandContext()` to attach command names +3. Use `handleCliError()` in catch blocks for consistent error formatting +4. Provide actionable error messages with suggestions + +### API Calls + +1. Always use `withRetry()` wrapper for API calls (handles rate limiting) +2. Use `initializeApi()` or `initializePbApi()` for API clients +3. Check rate limits and show warnings in verbose mode +4. Use `getVerboseMode()` instead of direct `options.verbose` checks + +### Output Formatting + +1. Use `formatOutput()` for structured data (handles JSON/YAML/table) +2. Use `getSafeText()` for text with emojis (respects terminal capabilities) +3. Use `createSpinner()` for progress indicators (automatically handles pipe mode) +4. Check `isStdoutPiped()` before showing interactive elements + +### Security + +1. Store secrets in credential files, not environment variables +2. Use `writeFileAtomic()` for credential writes (atomic operations) +3. Validate inputs using `validateAmount()`, `validateAccountId()`, etc. +4. Warn about secret usage in non-interactive environments + +### Testing Best Practices + +1. Mock external dependencies in `vi.mock()` blocks +2. Use `vi.hoisted()` for shared mocks +3. Test error paths with `expect().toThrow(CliError)` +4. Mock terminal capabilities for consistent test output + +## Environment Variables + +The CLI respects standard environment variables: + +- `NO_COLOR`: Disable colored output +- `FORCE_COLOR`: Force colored output +- `DEBUG`: Enable verbose/debug mode +- `PAGER`: Pager for long output +- `LINES`/`COLUMNS`: Terminal dimensions +- `TMPDIR`: Temporary directory +- `EDITOR`: Editor for config files +- `TERM`: Terminal type for capability detection + +## Development Workflow + +1. **Build**: `npm run build` - Compiles TypeScript to `bin/` +2. **Test**: `npm test` - Runs Vitest in watch mode +3. **Test (CI)**: `npm run test:run` - Runs tests once +4. **Lint**: `npm run lint` - Checks code quality +5. **Lint Fix**: `npm run lint:fix` - Auto-fixes issues +6. **Format**: `npm run format` - Formats code +7. **Type Check**: `npm run type-check` - TypeScript validation +8. **CI**: `npm run ci` - Full CI pipeline + +## Adding New Commands + +1. Create command file in `src/cmds/` +2. Export command function from `src/cmds/index.ts` +3. Register command in `src/index.ts` with options +4. Add tests in `test/cmds/` +5. Follow existing command patterns for consistency +6. Use `CommonOptions` interface for shared options +7. Add command to shell completion scripts if needed + +## Common Patterns + +### Command with API Call + +```typescript +export async function myCommand(options: CommonOptions) { + const api = await initializePbApi(credentials, options); + const verbose = getVerboseMode(options.verbose); + const result = await withRetry(() => api.someMethod(), { verbose }); + formatOutput(result.data, options); +} +``` + +### Command with File Operations + +```typescript +export async function myCommand(options: Options) { + const normalizedPath = await validateFilePath(options.filename, ['.js']); + const fileSize = await getFileSize(normalizedPath); + const spinner = createSpinner(true, getSafeText(`📖 Reading ${formatFileSize(fileSize)}...`)); + // ... file operations +} +``` + +### Destructive Operation with Confirmation + +```typescript +const confirmed = await confirmDestructiveOperation( + 'This will delete data. Continue?', + { yes: options.yes } +); +if (!confirmed) return; +``` + +## Notes + +- The project follows [clig.dev](https://clig.dev/) guidelines for CLI best practices +- All improvements from clig.dev have been implemented +- Command history is logged to `~/.ipb/history.json` (unless `--no-history` is set) +- Update notifications are checked periodically (24-hour cache) +- Shell completion scripts are generated for bash and zsh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..e0856fc --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,185 @@ +name: Release Binaries + +on: + push: + tags: + - "v*.*.*" + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: macos-latest + platform: macos + arch: x64 + target: node20-macos-x64 + binary-name: ipb-macos-x64 + - os: macos-latest + platform: macos + arch: arm64 + target: node20-macos-arm64 + binary-name: ipb-macos-arm64 + - os: ubuntu-latest + platform: linux + arch: x64 + target: node20-linux-x64 + binary-name: ipb-linux-x64 + - os: ubuntu-latest + platform: linux + arch: arm64 + target: node20-linux-arm64 + binary-name: ipb-linux-arm64 + - os: windows-latest + platform: win + arch: x64 + target: node20-win-x64 + binary-name: ipb-win-x64.exe + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20.x + + - name: Install dependencies + run: npm ci + + - name: Build project + run: npm run build + + - name: Bundle with esbuild + run: npm run bundle + + - name: Package binary + run: npx pkg dist-bundle/index.cjs --targets ${{ matrix.target }} --out-path dist --no-bytecode --public + + - name: Rename binary (macOS/Linux) + if: matrix.platform != 'win' + run: | + cd dist + # pkg creates binaries with package name (investec-ipb-{platform}-{arch}) + # Rename to our desired format + if [ -f "investec-ipb-${{ matrix.platform }}-${{ matrix.arch }}" ]; then + mv investec-ipb-${{ matrix.platform }}-${{ matrix.arch }} ${{ matrix.binary-name }} + elif [ -f "index" ]; then + mv index ${{ matrix.binary-name }} + fi + chmod +x ${{ matrix.binary-name }} + + - name: Rename binary (Windows) + if: matrix.platform == 'win' + run: | + cd dist + # pkg creates binaries with package name + if [ -f "investec-ipb-win-x64.exe" ]; then + mv investec-ipb-win-x64.exe ${{ matrix.binary-name }} + elif [ -f "index.exe" ]; then + mv index.exe ${{ matrix.binary-name }} + fi + + - name: Calculate SHA256 checksum (macOS/Linux) + if: matrix.platform != 'win' + shell: bash + run: | + cd dist + shasum -a 256 ${{ matrix.binary-name }} > ${{ matrix.binary-name }}.sha256 + + - name: Calculate SHA256 checksum (Windows) + if: matrix.platform == 'win' + shell: bash + run: | + cd dist + sha256sum ${{ matrix.binary-name }} > ${{ matrix.binary-name }}.sha256 + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.binary-name }} + path: | + dist/${{ matrix.binary-name }} + dist/${{ matrix.binary-name }}.sha256 + retention-days: 30 + + release: + needs: build + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + + - name: Extract version from tag + id: tag_version + run: | + VERSION=${GITHUB_REF#refs/tags/v} + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Version: $VERSION" + + - name: Prepare release assets + run: | + mkdir -p release-assets + find artifacts -type f -name "ipb-*" ! -name "*.sha256" -exec cp {} release-assets/ \; + find artifacts -type f -name "*.sha256" -exec cp {} release-assets/ \; + + - name: Create checksums file + run: | + cd release-assets + echo "# SHA256 Checksums" > checksums.txt + echo "" >> checksums.txt + for file in *.sha256; do + if [ -f "$file" ]; then + echo "## ${file%.sha256}" >> checksums.txt + cat "$file" >> checksums.txt + echo "" >> checksums.txt + fi + done + + - name: Create GitHub Release + uses: softprops/action-gh-release@v1 + with: + files: release-assets/* + name: Release v${{ steps.tag_version.outputs.version }} + body: | + ## Release v${{ steps.tag_version.outputs.version }} + + ### Installation + + **Homebrew (macOS/Linux):** + ```bash + brew tap devinpearson/ipb + brew install ipb + ``` + + **Direct Download:** + - Download the appropriate binary for your platform + - Make it executable: `chmod +x ipb-*` + - Move to your PATH: `sudo mv ipb-* /usr/local/bin/ipb` + + **npm (requires Node.js):** + ```bash + npm install -g investec-ipb + ``` + + ### Checksums + See checksums.txt for SHA256 checksums of all binaries. + + ### Changes + See [CHANGELOG.md](https://github.com/devinpearson/ipb/blob/main/CHANGELOG.md) for full changelog. + draft: false + prerelease: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + diff --git a/.gitignore b/.gitignore index 43e4f2e..acfbaed 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ published.js env.json gen.js ai-generated.js +/dist +/dist-bundle diff --git a/CI_CD_EXAMPLES.md b/CI_CD_EXAMPLES.md new file mode 100644 index 0000000..fcfff4a --- /dev/null +++ b/CI_CD_EXAMPLES.md @@ -0,0 +1,294 @@ +# CI/CD Pipeline Deployment Examples + +This document provides example code snippets for deploying code using the `ipb` CLI in various CI/CD pipelines. + +## Overview + +The `ipb deploy` command deploys JavaScript code to programmable cards. In CI/CD environments, you'll need to: + +1. **Provide credentials** via environment variables or command-line options +2. **Use the `--yes` flag** to skip confirmation prompts (required in non-interactive environments) +3. **Specify the card key** and code file to deploy + +## Basic Deployment Command + +```bash +ipb deploy --filename main.js --card-key card-123 --yes +``` + +## GitHub Actions Example + +```yaml +name: Deploy to Card + +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install ipb CLI + run: npm install -g investec-ipb + + - name: Deploy code + env: + INVESTEC_CLIENT_ID: ${{ secrets.INVESTEC_CLIENT_ID }} + INVESTEC_CLIENT_SECRET: ${{ secrets.INVESTEC_CLIENT_SECRET }} + INVESTEC_API_KEY: ${{ secrets.INVESTEC_API_KEY }} + INVESTEC_CARD_KEY: ${{ secrets.INVESTEC_CARD_KEY }} + run: | + ipb deploy \ + --filename src/main.js \ + --card-key ${{ secrets.INVESTEC_CARD_KEY }} \ + --env production \ + --yes \ + --verbose +``` + +## GitLab CI Example + +```yaml +stages: + - deploy + +deploy_to_card: + stage: deploy + image: node:20 + + before_script: + - npm install -g investec-ipb + + script: + - | + ipb deploy \ + --filename src/main.js \ + --card-key $INVESTEC_CARD_KEY \ + --env production \ + --yes \ + --verbose + + variables: + INVESTEC_HOST: "https://openapi.investec.com" + + only: + - main +``` + +## Jenkins Pipeline Example + +```groovy +pipeline { + agent any + + environment { + INVESTEC_CLIENT_ID = credentials('investec-client-id') + INVESTEC_CLIENT_SECRET = credentials('investec-client-secret') + INVESTEC_API_KEY = credentials('investec-api-key') + INVESTEC_CARD_KEY = credentials('investec-card-key') + } + + stages { + stage('Deploy') { + steps { + sh ''' + npm install -g investec-ipb + ipb deploy \ + --filename src/main.js \ + --card-key ${INVESTEC_CARD_KEY} \ + --env production \ + --yes \ + --verbose + ''' + } + } + } +} +``` + +## Using Profiles (Alternative Approach) + +Instead of environment variables, you can pre-configure profiles and use them in your pipeline: + +```bash +# Pre-configure profile (run once on CI server or in setup step) +ipb config \ + --profile production \ + --client-id $INVESTEC_CLIENT_ID \ + --client-secret $INVESTEC_CLIENT_SECRET \ + --api-key $INVESTEC_API_KEY + +# Deploy using profile +ipb deploy \ + --profile production \ + --filename src/main.js \ + --card-key card-123 \ + --yes +``` + +## Using Command-Line Options + +You can also pass credentials directly via command-line options (less secure for CI/CD): + +```bash +ipb deploy \ + --filename src/main.js \ + --card-key card-123 \ + --client-id $INVESTEC_CLIENT_ID \ + --client-secret $INVESTEC_CLIENT_SECRET \ + --api-key $INVESTEC_API_KEY \ + --env production \ + --yes +``` + +## Environment Variables + +The CLI supports the following environment variables for credentials: + +- `INVESTEC_HOST` - API host URL (default: `https://openapi.investec.com`) +- `INVESTEC_CLIENT_ID` - OAuth client ID +- `INVESTEC_CLIENT_SECRET` - OAuth client secret +- `INVESTEC_API_KEY` - API key +- `INVESTEC_CARD_KEY` - Card identifier (can also be passed via `--card-key`) + +## Deployment with Environment Variables + +The `--env` option loads environment variables from a `.env.` file: + +```bash +# Deploy with environment variables from .env.production +ipb deploy \ + --filename src/main.js \ + --card-key card-123 \ + --env production \ + --yes +``` + +This will: +1. Read variables from `.env.production` +2. Upload them to the card +3. Upload and publish the code + +## Complete Example: GitHub Actions with Multiple Environments + +```yaml +name: Deploy + +on: + push: + branches: + - main + - staging + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install ipb CLI + run: npm install -g investec-ipb + + - name: Determine environment + id: env + run: | + if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then + echo "env=production" >> $GITHUB_OUTPUT + echo "card_key=${{ secrets.PROD_CARD_KEY }}" >> $GITHUB_OUTPUT + else + echo "env=staging" >> $GITHUB_OUTPUT + echo "card_key=${{ secrets.STAGING_CARD_KEY }}" >> $GITHUB_OUTPUT + fi + + - name: Deploy to environment + env: + INVESTEC_CLIENT_ID: ${{ secrets.INVESTEC_CLIENT_ID }} + INVESTEC_CLIENT_SECRET: ${{ secrets.INVESTEC_CLIENT_SECRET }} + INVESTEC_API_KEY: ${{ secrets.INVESTEC_API_KEY }} + run: | + ipb deploy \ + --filename src/main.js \ + --card-key ${{ steps.env.outputs.card_key }} \ + --env ${{ steps.env.outputs.env }} \ + --yes \ + --verbose +``` + +## Security Best Practices + +1. **Use Secrets Management**: Store credentials in your CI/CD platform's secrets management system (GitHub Secrets, GitLab CI Variables, etc.) + +2. **Avoid Hardcoding**: Never commit credentials to your repository + +3. **Use Profiles**: Consider using profiles for better organization (though they require file system access) + +4. **Environment Variables**: The CLI automatically detects CI/CD environments and will warn about secrets in environment variables, but they're still supported for pipelines + +5. **Least Privilege**: Use separate credentials for different environments (staging, production) + +## Troubleshooting + +### Deployment fails with "Deployment cancelled" +- **Solution**: Add the `--yes` flag to skip confirmation prompts in non-interactive environments + +### Missing credentials error +- **Solution**: Ensure all required environment variables are set: + - `INVESTEC_CLIENT_ID` + - `INVESTEC_CLIENT_SECRET` + - `INVESTEC_API_KEY` + - `INVESTEC_CARD_KEY` (or pass via `--card-key`) + +### Verbose output for debugging +- **Solution**: Add `--verbose` flag or set `DEBUG=1` environment variable + +### Rate limiting +- **Solution**: The CLI automatically retries on rate limit errors with exponential backoff. Use `--verbose` to see retry attempts. + +## Example: Deploy Script + +You can also create a deploy script for easier reuse: + +```bash +#!/bin/bash +# deploy.sh + +set -e + +# Load environment variables +source .env.production + +# Deploy code +ipb deploy \ + --filename src/main.js \ + --card-key "${INVESTEC_CARD_KEY}" \ + --env production \ + --yes \ + --verbose + +echo "Deployment successful!" +``` + +Make it executable and run: +```bash +chmod +x deploy.sh +./deploy.sh +``` + diff --git a/DISTRIBUTION.md b/DISTRIBUTION.md new file mode 100644 index 0000000..9e7ab65 --- /dev/null +++ b/DISTRIBUTION.md @@ -0,0 +1,392 @@ +# Distribution Guide + +This document describes how to distribute the IPB CLI via various package managers and distribution methods. + +## Overview + +The IPB CLI can be distributed in several ways: + +1. **npm** (current) - `npm install -g investec-ipb` +2. **Homebrew** (macOS/Linux) - `brew install ipb` +3. **Direct Downloads** - Standalone binaries for each platform +4. **Other Package Managers** - Scoop (Windows), apt/yum (Linux), etc. + +## Building Binaries + +We use **esbuild** to bundle the application and **@yao-pkg/pkg** to create standalone executables that don't require Node.js to be installed. + +### Prerequisites + +```bash +npm install +``` + +### Build Process + +The build process consists of two steps: + +1. **Bundle with esbuild**: Bundles all code and dependencies into a single CommonJS file +2. **Package with pkg**: Creates platform-specific executables from the bundled file + +### Build Scripts + +```bash +# Bundle only (creates dist-bundle/index.cjs) +npm run bundle + +# Build for current platform only +npm run pkg:build + +# Build for macOS (Intel + Apple Silicon) +npm run pkg:macos + +# Build for Linux (x64 + ARM64) +npm run pkg:linux + +# Build for Windows (x64) +npm run pkg:windows + +# Build for all platforms +npm run pkg:all + +# Clean dist and bundle directories +npm run pkg:clean +``` + +### Output + +Binaries are generated in the `dist/` directory with the following naming convention: +- `ipb-macos-x64` (macOS Intel) +- `ipb-macos-arm64` (macOS Apple Silicon) +- `ipb-linux-x64` (Linux x64) +- `ipb-linux-arm64` (Linux ARM64) +- `ipb-win-x64.exe` (Windows x64) + +**Note:** The binary names use the `ipb` prefix for the installed command name. + +### Build Architecture + +The project uses **esbuild** to bundle ESM code into CommonJS format, which is then packaged with `@yao-pkg/pkg`. This approach: + +1. **Bundles all dependencies** - All npm packages are bundled into a single file, eliminating ESM module resolution issues +2. **Converts to CommonJS** - The bundled output is CommonJS, which `pkg` handles reliably +3. **Includes assets** - Templates, assets, and instructions are copied to the bundle directory +4. **Produces working executables** - The final binaries work without Node.js installed + +The bundling step resolves the ESM limitations that `pkg` has when packaging ESM code directly. + +## Homebrew Distribution + +Homebrew is the most popular package manager for macOS and Linux. There are two approaches: + +### Option 1: Homebrew Tap (Recommended) + +Create a separate repository for your Homebrew formula. + +#### 1. Create a Homebrew Tap Repository + +Create a new GitHub repository named `homebrew-ipb` (or `homebrew-tap` if you want multiple formulas). + +#### 2. Create the Formula + +Create `Formula/ipb.rb`: + +```ruby +class Ipb < Formula + desc "CLI application to manage programmable banking cards" + homepage "https://github.com/devinpearson/ipb" + url "https://github.com/devinpearson/ipb/releases/download/v#{version}/ipb-macos-arm64" + sha256 "" + version "" + + if Hardware::CPU.intel? + url "https://github.com/devinpearson/ipb/releases/download/v#{version}/ipb-macos-x64" + sha256 "" + end + + def install + bin.install "ipb-macos-arm64" => "ipb" + # For Intel builds, use: + # bin.install "ipb-macos-x64" => "ipb" + end + + test do + system "#{bin}/ipb", "--version" + end +end +``` + +#### 3. Multiple Architecture Support + +For better multi-architecture support, use conditional logic: + +```ruby +class Ipb < Formula + desc "CLI application to manage programmable banking cards" + homepage "https://github.com/devinpearson/ipb" + version "0.8.3" + + if Hardware::CPU.arm? + url "https://github.com/devinpearson/ipb/releases/download/v#{version}/ipb-macos-arm64" + sha256 "" + else + url "https://github.com/devinpearson/ipb/releases/download/v#{version}/ipb-macos-x64" + sha256 "" + end + + def install + if Hardware::CPU.arm? + bin.install "ipb-macos-arm64" => "ipb" + else + bin.install "ipb-macos-x64" => "ipb" + end + end + + test do + system "#{bin}/ipb", "--version" + end +end +``` + +#### 4. Install from Tap + +Users can install via: + +```bash +brew tap devinpearson/ipb +brew install ipb +``` + +Or in one command: + +```bash +brew install devinpearson/ipb/ipb +``` + +### Option 2: Homebrew Core (Advanced) + +For broader distribution, you can submit to Homebrew core. This requires: +- Minimum 30 stars on GitHub +- Active maintenance +- Passing audit tests +- See [Homebrew Core Contributing Guide](https://docs.brew.sh/How-To-Open-a-Homebrew-PR) + +## GitHub Releases + +### Automated Releases + +We use GitHub Actions to automatically build and release binaries when tags are pushed. + +See `.github/workflows/release.yml` for the automated release workflow. + +### Manual Release Process + +1. **Build binaries for all platforms:** + ```bash + npm run pkg:all + ``` + +2. **Calculate SHA256 checksums:** + ```bash + shasum -a 256 dist/ipb-* + ``` + +3. **Create a GitHub Release:** + - Go to GitHub Releases + - Click "Draft a new release" + - Tag version: `v0.8.3` + - Release title: `v0.8.3` + - Upload all binaries from `dist/` + - Add release notes + +4. **Update Homebrew Formula:** + - Update version in `Formula/ipb.rb` + - Update SHA256 checksums + - Update URL if needed + +## Windows Distribution + +### Scoop + +Create a Scoop manifest in a separate repository or in the main repo under `scoop/ipb.json`: + +```json +{ + "version": "0.8.3", + "description": "CLI application to manage programmable banking cards", + "homepage": "https://github.com/devinpearson/ipb", + "license": "MIT", + "url": "https://github.com/devinpearson/ipb/releases/download/v0.8.3/ipb-win-x64.exe", + "hash": "SHA256_CHECKSUM", + "bin": ["ipb-win-x64.exe", "ipb.exe"], + "autoupdate": { + "url": "https://github.com/devinpearson/ipb/releases/download/v$version/ipb-win-x64.exe" + } +} +``` + +Install via: +```powershell +scoop bucket add ipb https://github.com/devinpearson/ipb-scoop +scoop install ipb +``` + +### Chocolatey + +Create a Chocolatey package in a separate repository. + +## Linux Distribution + +### Ubuntu/Debian (.deb Package) + +#### Quick Method (Using Script) + +```bash +# Build binary first +npm run pkg:linux + +# Create .deb package +./scripts/create-deb.sh 0.8.3 amd64 + +# Install +sudo dpkg -i ipb_0.8.3_amd64.deb +``` + +#### Manual Method + +```bash +# Install required tools +sudo apt-get install fakeroot dpkg-dev + +# Create package structure +mkdir -p ipb_0.8.3/usr/local/bin +cp dist/ipb-linux-x64 ipb_0.8.3/usr/local/bin/ipb +chmod +x ipb_0.8.3/usr/local/bin/ipb + +# Create control file +mkdir -p ipb_0.8.3/DEBIAN +cat > ipb_0.8.3/DEBIAN/control << EOF +Package: ipb +Version: 0.8.3 +Section: utils +Priority: optional +Architecture: amd64 +Maintainer: Devin Pearson +Description: CLI application to manage programmable banking cards +EOF + +# Build package +dpkg-deb --build ipb_0.8.3 +``` + +### Ubuntu PPA (Personal Package Archive) + +For easy distribution and automatic updates, set up a PPA on Launchpad: + +1. **Create Launchpad account** at +2. **Create PPA** at +3. **Upload packages** using `dput` (see `UBUNTU_DISTRIBUTION.md` for details) + +Users can then install via: +```bash +sudo add-apt-repository ppa:your-launchpad-id/ipb +sudo apt update +sudo apt install ipb +``` + +### Snap Package + +Build a universal Snap package: + +```bash +# Install snapcraft +sudo snap install snapcraft --classic + +# Build snap +snapcraft + +# Install +sudo snap install ipb_0.8.3_amd64.snap --dangerous +``` + +See `snap/snapcraft.yaml` for configuration. + +### AppImage + +Create a portable AppImage: + +```bash +# Build binary +npm run pkg:linux + +# Create AppDir and package (requires appimagetool) +# See UBUNTU_DISTRIBUTION.md for full instructions +``` + +### Red Hat/CentOS (.rpm) + +Create an `.rpm` package using `rpmbuild` or `fpm`. + +For detailed Ubuntu distribution instructions, see [UBUNTU_DISTRIBUTION.md](./UBUNTU_DISTRIBUTION.md). + +## Version Management + +- Update version in `package.json` +- Update version in Homebrew formula +- Update version in other package manifests +- Tag releases with `v` prefix: `v0.8.3` +- GitHub releases should match tag names + +## Testing Distributions + +Before releasing: + +1. **Test binaries locally:** + ```bash + ./dist/ipb-macos-arm64 --version + ./dist/ipb-macos-arm64 --help + ``` + +2. **Test Homebrew formula:** + ```bash + brew install --build-from-source Formula/ipb.rb + brew test ipb + ``` + +3. **Test on clean systems:** + - Use Docker for Linux testing + - Use VMs for cross-platform testing + - Test on fresh installs without Node.js + +## Troubleshooting + +### Binary Not Found Error + +If templates/assets are not found at runtime: +- Ensure `pkg.assets` in package.json includes all necessary files +- Check that paths are relative to the binary location +- Verify assets are included in the snapshot + +### ESM Module Issues + +The project uses esbuild to bundle ESM code into CommonJS before packaging, which resolves ESM compatibility issues. If you encounter module-related errors: + +- Ensure `npm run bundle` completes successfully before packaging +- Check that `dist-bundle/index.cjs` exists and is valid +- Verify all dependencies are being bundled (check bundle size) +- The `--no-bytecode --public` flags are included in build scripts for maximum compatibility + +### File Size + +Binaries are large (~65-80MB) because they include Node.js runtime: +- This is expected and normal +- Users don't need Node.js installed separately +- Consider compression for downloads + +## CI/CD Integration + +See `.github/workflows/release.yml` for automated release workflow that: +- Builds binaries on push of version tags +- Creates GitHub releases +- Uploads binaries as release assets +- Can trigger formula updates (requires separate workflow) diff --git a/Formula/ipb.rb b/Formula/ipb.rb new file mode 100644 index 0000000..65e7b5a --- /dev/null +++ b/Formula/ipb.rb @@ -0,0 +1,67 @@ +# typed: false +# frozen_string_literal: true + +# This file was generated by the Homebrew formula generator. +# To use this formula, create a repository named "homebrew-ipb" on GitHub +# and place this file in Formula/ipb.rb +# +# Installation: +# brew tap devinpearson/ipb +# brew install ipb +# +# Or install directly: +# brew install devinpearson/ipb/ipb + +class Ipb < Formula + desc "CLI application to manage programmable banking cards" + homepage "https://github.com/devinpearson/ipb" + version "0.8.3" + license "MIT" + + on_macos do + if Hardware::CPU.arm? + url "https://github.com/devinpearson/ipb/releases/download/v#{version}/ipb-macos-arm64" + # Calculate SHA256: shasum -a 256 dist/ipb-macos-arm64 + sha256 "REPLACE_WITH_ACTUAL_SHA256_CHECKSUM" + + def install + bin.install "ipb-macos-arm64" => "ipb" + end + end + if Hardware::CPU.intel? + url "https://github.com/devinpearson/ipb/releases/download/v#{version}/ipb-macos-x64" + # Calculate SHA256: shasum -a 256 dist/ipb-macos-x64 + sha256 "REPLACE_WITH_ACTUAL_SHA256_CHECKSUM" + + def install + bin.install "ipb-macos-x64" => "ipb" + end + end + end + + on_linux do + if Hardware::CPU.arm? + url "https://github.com/devinpearson/ipb/releases/download/v#{version}/ipb-linux-arm64" + # Calculate SHA256: shasum -a 256 dist/ipb-linux-arm64 + sha256 "REPLACE_WITH_ACTUAL_SHA256_CHECKSUM" + + def install + bin.install "ipb-linux-arm64" => "ipb" + end + end + if Hardware::CPU.intel? + url "https://github.com/devinpearson/ipb/releases/download/v#{version}/ipb-linux-x64" + # Calculate SHA256: shasum -a 256 dist/ipb-linux-x64 + sha256 "REPLACE_WITH_ACTUAL_SHA256_CHECKSUM" + + def install + bin.install "ipb-linux-x64" => "ipb" + end + end + end + + test do + system "#{bin}/ipb", "--version" + end +end + diff --git a/GENERATED_README.md b/GENERATED_README.md new file mode 100644 index 0000000..63624ba --- /dev/null +++ b/GENERATED_README.md @@ -0,0 +1,453 @@ +# IPB CLI Command Reference + +> This documentation is auto-generated from the CLI command definitions. +> Last generated: 2025-11-05T06:34:44.819Z + +## Table of Contents + +- [cards](#cards) +- [config](#config) +- [deploy](#deploy) +- [logs](#logs) +- [run](#run) +- [fetch](#fetch) +- [upload](#upload) +- [env](#env) +- [env-list](#env-list) +- [upload-env](#upload-env) +- [published](#published) +- [publish](#publish) +- [simulate](#simulate) +- [enable](#enable) +- [disable](#disable) +- [currencies](#currencies) +- [countries](#countries) +- [merchants](#merchants) +- [accounts](#accounts) +- [balances](#balances) +- [transfer](#transfer) +- [pay](#pay) +- [transactions](#transactions) +- [beneficiaries](#beneficiaries) +- [new](#new) +- [ai](#ai) +- [bank](#bank) +- [register](#register) +- [login](#login) +- [completion](#completion) +- [docs](#docs) + +## cards (aliases: c) + +List all programmable cards. Shows card keys, numbers, and activation status for each card. + +**Usage:** `ipb cards` + + +## config (aliases: cfg) + +Configure authentication credentials. Set API keys, client credentials, and card keys for CLI operations. Supports configuration profiles for managing multiple environments. + +**Usage:** `ipb config` + +**Options:** + +- `--card-key ` (required) - Set your card key for the Investec API +- `--openai-key ` (required) - Set your OpenAI API key for AI code generation +- `--sandbox-key ` (required) - Set your sandbox key for AI generation + +**Subcommands:** + +### profile + +Manage configuration profiles + +**Usage:** `ipb profile` + +**Subcommands:** + +#### list (aliases: ls) + +List all available configuration profiles + +**Usage:** `ipb list` + +#### set + +Set the active profile (used when --profile is not specified) + +**Usage:** `ipb set ` + +**Arguments:** + +- `` (required) - Profile name to set as active + +#### show + +Show the currently active profile + +**Usage:** `ipb show` + +#### delete (aliases: rm) + +Delete a configuration profile + +**Usage:** `ipb delete ` + +**Arguments:** + +- `` (required) - Profile name to delete + + +## deploy (aliases: d) + +Deploy JavaScript code to a programmable card. Uploads code and optional environment variables, then publishes it to make it active. + +**Usage:** `ipb deploy` + +**Options:** + +- `-f,--filename ` (required) - JavaScript file to deploy +- `-e,--env ` (required) - Environment name (loads variables from .env. file) +- `-c,--card-key ` (required) - Card identifier to deploy to +- `--yes` - Skip confirmation prompt for destructive operations + + +## logs (aliases: log) + +Fetch execution logs from a card. Retrieves transaction execution logs and saves them to a JSON file. + +**Usage:** `ipb logs` + +**Options:** + +- `-f,--filename ` (required) - Output filename for logs (JSON format) +- `-c,--card-key ` (required) - Card identifier to fetch logs from + + +## run (aliases: r) + +Run card code locally using the emulator. Test JavaScript code with simulated transactions without deploying to a card. + +**Usage:** `ipb run` + +**Options:** + +- `-f,--filename ` (required) - JavaScript file to execute +- `-e,--env ` (required) - Environment file to load (.env.) +- `-a,--amount ` (required) - Transaction amount in cents +- `-u,--currency ` (required) - Currency code (ISO 4217) +- `-z,--mcc ` (required) - Merchant category code +- `-m,--merchant ` (required) - Merchant name +- `-i,--city ` (required) - Merchant city +- `-o,--country ` (required) - Country code (ISO 3166-1 alpha-2) + + +## fetch (aliases: f) + +Fetch saved code from a card. Downloads the code currently saved on a card and saves it to a local file. + +**Usage:** `ipb fetch` + +**Options:** + +- `-f,--filename ` (required) - Local filename to save the code to +- `-c,--card-key ` (required) - Card identifier to fetch code from + + +## upload (aliases: up) + +Upload code to a card without publishing. Saves JavaScript code to a card but does not activate it. Use publish command to activate. + +**Usage:** `ipb upload` + +**Options:** + +- `-f,--filename ` (required) - JavaScript file to upload +- `-c,--card-key ` (required) - Card identifier to upload code to + + +## env + +Download environment variables from a card. Retrieves all environment variables configured on a card and saves them to a JSON file. + +**Usage:** `ipb env` + +**Options:** + +- `-f,--filename ` (required) - Output filename for environment variables (JSON format) +- `-c,--card-key ` (required) - Card identifier to fetch environment from + + +## env-list + +List all supported environment variables. Shows available environment variables with descriptions, usage examples, and default values. + +**Usage:** `ipb env-list` + + +## upload-env + +Upload environment variables to a card. Reads environment variables from a JSON file and uploads them to a card. + +**Usage:** `ipb upload-env` + +**Options:** + +- `-f,--filename ` (required) - JSON file containing environment variables +- `-c,--card-key ` (required) - Card identifier to upload environment to + + +## published + +Download the currently published code from a card. Retrieves the active code currently running on a card and saves it to a local file. + +**Usage:** `ipb published` + +**Options:** + +- `-f,--filename ` (required) - Local filename to save the published code to +- `-c,--card-key ` (required) - Card identifier to fetch published code from + + +## publish (aliases: pub) + +Publish previously uploaded code to make it active. Activates code that was uploaded using the upload command. Requires code ID from upload. + +**Usage:** `ipb publish` + +**Options:** + +- `-f,--filename ` (required) - JavaScript file to publish (must match uploaded code) +- `-i,--code-id ` (required) - Code ID from previous upload command +- `-c,--card-key ` (required) - Card identifier to publish code to +- `--yes` - Skip confirmation prompt for destructive operations + + +## simulate + +Test code using the online simulator. Runs JavaScript code in the Investec cloud environment with simulated transactions. Similar to run but uses cloud infrastructure. + +**Usage:** `ipb simulate` + +**Options:** + +- `-f,--filename ` (required) - JavaScript file to simulate (required) +- `-c,--card-key ` (required) - Card identifier for simulation +- `-e,--env ` (required) - Environment name +- `-a,--amount ` (required) - Transaction amount in cents +- `-u,--currency ` (required) - Currency code (ISO 4217) +- `-z,--mcc ` (required) - Merchant category code +- `-m,--merchant ` (required) - Merchant name +- `-i,--city ` (required) - Merchant city +- `-o,--country ` (required) - Country code (ISO 3166-1 alpha-2) + + +## enable + +Enable programmable code on a card. Activates programmable card functionality. Code must be deployed and published first. + +**Usage:** `ipb enable` + +**Options:** + +- `-c,--card-key ` (required) - Card identifier to enable code on + + +## disable + +Disable programmable code on a card. Deactivates programmable card functionality. Code remains deployed but will not execute on transactions. + +**Usage:** `ipb disable` + +**Options:** + +- `-c,--card-key ` (required) - Card identifier to disable code on +- `--yes` - Skip confirmation prompt for destructive operations + + +## currencies + +List all supported currency codes. Shows ISO 4217 currency codes and names available for use in transaction simulations and operations. + +**Usage:** `ipb currencies` + + +## countries + +List all supported country codes. Shows ISO 3166-1 alpha-2 country codes and names available for use in transaction simulations. + +**Usage:** `ipb countries` + + +## merchants + +List merchant categories and codes. Shows merchant category codes (MCC) with descriptions for use in transaction simulations. + +**Usage:** `ipb merchants` + + +## accounts (aliases: acc) + +List all Investec accounts. Shows account IDs, account numbers, product names, and reference names for all linked accounts. + +**Usage:** `ipb accounts` + + +## balances (aliases: bal) + +Get account balance information. Retrieves current balance, available balance, and budget balance for a specific account. + +**Usage:** `ipb balances ` + +**Arguments:** + +- `` (required) - Account ID to fetch balances for + + +## transfer + +Transfer money between your own accounts. Moves funds from one account to another. Prompts interactively for missing information. + +**Usage:** `ipb transfer ` + +**Arguments:** + +- `` (required) - Account ID to transfer from +- `` (required) - Beneficiary account ID to transfer to +- `` (required) - Amount to transfer in rands (e.g. 100.00) +- `` (required) - Payment reference message + +**Options:** + +- `--yes` - Skip confirmation prompt for destructive operations + + +## pay + +Pay a beneficiary from your account. Makes a payment to a registered beneficiary. Requires confirmation before executing. + +**Usage:** `ipb pay ` + +**Arguments:** + +- `` (required) - Account ID to pay from +- `` (required) - Beneficiary ID to pay to +- `` (required) - Amount to pay in rands (e.g. 100.00) +- `` (required) - Payment reference message + +**Options:** + +- `--yes` - Skip confirmation prompt for destructive operations + + +## transactions (aliases: tx) + +Get transaction history for an account. Retrieves and displays recent transactions with full details including amounts, dates, and merchants. + +**Usage:** `ipb transactions ` + +**Arguments:** + +- `` (required) - Account ID to fetch transactions for + + +## beneficiaries + +List all beneficiaries. Shows all registered beneficiaries linked to your Investec profile with their IDs and details. + +**Usage:** `ipb beneficiaries` + + +## new + +Create a new project with scaffolding. Generates a new project directory with template files and directory structure for card code development. + +**Usage:** `ipb new ` + +**Arguments:** + +- `` (required) - Project name (will create a directory with this name) + +**Options:** + +- `--force` - Overwrite existing project directory if it exists +- `--template