ADR-006: CLI as Machine-to-Machine (M2M) API

Status

Proposed

Context

The idempiere-cli can serve as a programmatic interface similar to a REST API. This enables:

This ADR documents the CLI's M2M communication contract.

CLI as API Architecture

┌─────────────────────────────────────────────────────────────────┐
│                    M2M Communication Flow                        │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌──────────────┐     ┌─────────────────┐     ┌──────────────┐  │
│  │   Caller     │     │  idempiere-cli  │     │  iDempiere   │  │
│  │ (Script/CI)  │────▶│   (CLI/API)     │────▶│  REST API    │  │
│  └──────────────┘     └─────────────────┘     └──────────────┘  │
│         │                     │                      │          │
│         │                     │                      │          │
│    Arguments             Exit Code              JSON/XML        │
│    + Options             + stdout               Response        │
│    + stdin               + stderr                               │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

API Contract

1. Exit Codes

Code Meaning Use Case
0 Success Operation completed successfully
1 Error Operation failed (check stderr for details)

Example:

idempiere-cli add column XX_Field --table C_Order --type string
if [ $? -eq 0 ]; then
    echo "Column created successfully"
else
    echo "Column creation failed"
fi

2. Output Streams

Stream Content Format
stdout Success output, data Human-readable (default) or JSON (--json)
stderr Error messages, warnings Human-readable

Example:

# Capture output
OUTPUT=$(idempiere-cli export table XX_MyTable -f sql 2>/dev/null)

# Capture errors
idempiere-cli add column XX_Bad --table NonExistent 2>&1

3. Input Methods

Method Use Case Example
CLI Arguments Simple values --table C_Order --type string
Options Flags and configuration --dry-run --verbose
Environment Variables Secrets, configuration IDEMPIERE_API_TOKEN
Config File Persistent settings ~/.idempiere-cli/config.yaml

Command Reference for M2M

Configuration Commands

# Configure API connection (store credentials)
idempiere-cli config \
  --url https://idempiere.example.com \
  --token "$IDEMPIERE_TOKEN"

# Test connection (exit 0 = connected)
idempiere-cli config --test

Application Dictionary Commands

# Add column (returns exit 0 on success)
idempiere-cli add column XX_MyField \
  --table C_BPartner \
  --type string \
  --length 100 \
  --mandatory

# Add table with columns
idempiere-cli add table XX_MyTable \
  --description "Custom table" \
  --columns standard,document

# Dry run (preview without changes)
idempiere-cli add column XX_Test \
  --table C_Order \
  --type yesno \
  --dry-run

Sync Commands

# Sync table to database
idempiere-cli sync table XX_MyTable

# Sync specific column
idempiere-cli sync column XX_MyTable.XX_MyColumn

Export Commands

# Export to 2Pack XML (stdout)
idempiere-cli export table XX_MyTable -f pack

# Export to SQL files
idempiere-cli export table XX_MyTable -f sql -o ./migration/

# Export to specific file
idempiere-cli export table XX_MyTable -o META-INF/2Pack.zip

Plugin Scaffolding

# Create plugin (non-interactive)
idempiere-cli init org.mycompany.myplugin \
  --name "My Plugin" \
  --version 1.0.0 \
  --with-callout \
  --with-process \
  -i 12 \
  -o ./plugins/

M2M Integration Patterns

Pattern 1: CI/CD Pipeline Integration

# GitHub Actions example
jobs:
  create-table:
    runs-on: ubuntu-latest
    steps:
      - name: Configure CLI
        run: |
          idempiere-cli config \
            --url ${{ secrets.IDEMPIERE_URL }} \
            --token ${{ secrets.IDEMPIERE_TOKEN }}

      - name: Create Table
        run: |
          idempiere-cli add table XX_AuditLog \
            --description "Audit logging table" \
            --columns standard

      - name: Sync to Database
        run: |
          idempiere-cli sync table XX_AuditLog

      - name: Export 2Pack
        run: |
          idempiere-cli export table XX_AuditLog \
            -o META-INF/2Pack.zip

Pattern 2: Scripting Multiple Operations

#!/bin/bash
# create-invoice-customization.sh

set -e  # Exit on any error

TABLE="XX_InvoiceExt"

# Step 1: Create table
echo "Creating table $TABLE..."
idempiere-cli add table $TABLE \
  --description "Invoice Extension" \
  --columns standard

# Step 2: Add custom columns
echo "Adding columns..."
idempiere-cli add column XX_ApprovalDate \
  --table $TABLE \
  --type date

idempiere-cli add column XX_ApprovedBy_ID \
  --table $TABLE \
  --type table \
  --reference-table AD_User

idempiere-cli add column XX_Notes \
  --table $TABLE \
  --type memo

# Step 3: Sync
echo "Syncing to database..."
idempiere-cli sync table $TABLE

# Step 4: Export
echo "Exporting 2Pack..."
idempiere-cli export table $TABLE -o "$TABLE-2pack.zip"

echo "Done! Created $TABLE with custom columns."

Pattern 3: IDE Integration

// VS Code tasks.json
{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "iDempiere: Add Column",
      "type": "shell",
      "command": "idempiere-cli",
      "args": [
        "add", "column", "${input:columnName}",
        "--table", "${input:tableName}",
        "--type", "${input:columnType}"
      ],
      "problemMatcher": []
    },
    {
      "label": "iDempiere: Sync Table",
      "type": "shell",
      "command": "idempiere-cli",
      "args": ["sync", "table", "${input:tableName}"],
      "problemMatcher": []
    }
  ],
  "inputs": [
    {
      "id": "tableName",
      "type": "promptString",
      "description": "Table name"
    },
    {
      "id": "columnName",
      "type": "promptString",
      "description": "Column name"
    },
    {
      "id": "columnType",
      "type": "pickString",
      "description": "Column type",
      "options": ["string", "integer", "date", "yesno", "amount", "table"]
    }
  ]
}

Pattern 4: Build Tool Integration (Maven)

<!-- pom.xml -->
<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>exec-maven-plugin</artifactId>
  <version>3.1.0</version>
  <executions>
    <execution>
      <id>export-2pack</id>
      <phase>package</phase>
      <goals>
        <goal>exec</goal>
      </goals>
      <configuration>
        <executable>idempiere-cli</executable>
        <arguments>
          <argument>export</argument>
          <argument>table</argument>
          <argument>${project.artifactId}</argument>
          <argument>-o</argument>
          <argument>${project.build.directory}/META-INF/2Pack.zip</argument>
        </arguments>
      </configuration>
    </execution>
  </executions>
</plugin>

Environment Variables

Variable Purpose Example
IDEMPIERE_API_URL iDempiere REST API URL https://idempiere.example.com
IDEMPIERE_API_TOKEN Authentication token eyJhbGc...
ANTHROPIC_API_KEY Claude AI API key (for ai command) sk-ant-...
IDEMPIERE_CLI_CONFIG Config file path ~/.idempiere-cli/config.yaml

Comparison: CLI vs REST API

Aspect CLI (M2M) iDempiere REST API
Authentication Token in config file or env var Bearer token in header
Input Format CLI arguments JSON body
Output Format Text (human) or structured (file) JSON
Batching Shell scripts Single request
Error Handling Exit codes + stderr HTTP status codes
Offline Operations Plugin scaffolding, DDL preview Not supported
Best For Automation, CI/CD, scripting Direct API integration

When to Use CLI

When to Use REST API Directly


Future Enhancements (Roadmap)

Phase 1: JSON Output Mode (Proposed)

# Machine-readable output
idempiere-cli add column XX_Field --table C_Order --type string --json

# Output:
{
  "success": true,
  "operation": "add_column",
  "table": "C_Order",
  "column": "XX_Field",
  "columnId": 1000123
}

Phase 2: Batch Operations (Proposed)

# Execute multiple operations from file
idempiere-cli batch ./operations.yaml

# operations.yaml
operations:
  - type: add_column
    table: C_Order
    column: XX_Field1
    dataType: string
  - type: add_column
    table: C_Order
    column: XX_Field2
    dataType: integer

Phase 3: Watch Mode (Proposed)

# Watch for changes and auto-sync
idempiere-cli watch ./ad-definitions/

Decision

  1. Document CLI as M2M API with clear contracts
  2. Maintain consistent exit codes (0=success, 1=error)
  3. Plan JSON output mode for structured responses
  4. Enable CI/CD and scripting use cases

Consequences

Positive

Negative

Neutral

References

Path: /docs/developers/architecture/idempiere-hub/006-cli-as-m2m-api