secubox-openwrt/docs/admin-control-center/EXAMPLES.md
CyberMind-FR e258d86eea feat: Admin Control Center with State Management (v0.9.0)
Major feature release implementing comprehensive state management, component registry,
and admin control center with full UI integration.

## Backend Features (secubox-core v0.9.0-1)

State Management System:
-  State database (state-db.json) with 15 states across 4 categories
-  State machine with transition matrix validation
-  secubox-state CLI (8 commands: get, set, history, list, validate, sync, freeze, clear-error)
-  state-machine.sh with atomic transitions using flock
-  State history tracking with timestamps and reasons
-  Error state handling with detailed error info
-  Frozen state support for system-critical components

Component Registry System:
-  Component registry database (component-registry.json)
-  secubox-component CLI (7 commands: list, get, register, unregister, tree, affected, set-setting)
-  Component types: app, module, widget, service, composite
-  Dependency tracking (required/optional)
-  Recursive dependency tree resolution
-  Reverse dependency tracking
-  Component settings management
-  Profile tagging and filtering

Auto-Sync System:
-  secubox-sync-registry CLI for catalog synchronization
-  Auto-populate from catalog.json
-  Plugin catalog directory scanning
-  Installed package detection
-  Automatic state initialization

RPC Backend (luci.secubox):
-  6 state management RPC methods
-  5 component registry RPC methods
-  Bulk operations support
-  State validation endpoints

## Frontend Features (luci-app-secubox-admin v1.0.0-16)

UI Components:
-  state-utils.js: 20+ utility functions, state config, transition validation
-  StateIndicator.js: 5 rendering modes (badge, compact, pill, dot, statistics)
-  StateTimeline.js: 4 visualization modes (vertical, horizontal, compact, transition diagram)
-  state-management.css: 600+ lines with animations, responsive design, accessibility

Admin Control Center Dashboard:
-  System overview panel with health metrics
-  Component state summary with statistics
-  Recent state transitions timeline
-  Alerts panel for warnings and errors
-  Quick actions panel
-  Real-time updates (5-second polling)
-  Metric cards with hover effects
-  State distribution by category

API Integration (api.js):
-  11 RPC method declarations
-  Enhanced methods: getComponentWithState(), getAllComponentsWithStates()
-  Bulk operations: bulkSetComponentState()
-  State statistics: getStateStatistics()
-  Retry logic with exponential backoff
-  Promise-based async operations

## Documentation

Comprehensive Documentation:
-  API-REFERENCE.md (1,200+ lines): Complete API docs for RPC, CLI, JS
-  EXAMPLES.md (800+ lines): 30+ usage examples, shell scripts, integration patterns
-  State definitions table (15 states)
-  State transition matrix
-  Component metadata schemas
-  Error codes reference
-  Testing examples

## State Definitions

15 States Across 4 Categories:
- Persistent: available, installed, active, disabled, frozen
- Transient: installing, configuring, activating, starting, stopping, uninstalling
- Runtime: running, stopped
- Error: error (with subtypes)

State Transition Flow:
available → installing → installed → configuring → configured →
activating → active → starting → running → stopping → stopped

## Technical Details

Files Created (10 backend + 8 frontend):
Backend:
- /usr/sbin/secubox-state (12KB, 8 commands)
- /usr/sbin/secubox-component (12KB, 7 commands)
- /usr/sbin/secubox-sync-registry (8.4KB)
- /usr/share/secubox/state-machine.sh (5.2KB)
- /var/lib/secubox/state-db.json (schema)
- /var/lib/secubox/component-registry.json (schema)

Frontend:
- resources/secubox-admin/state-utils.js (~400 lines)
- resources/secubox-admin/components/StateIndicator.js (~350 lines)
- resources/secubox-admin/components/StateTimeline.js (~450 lines)
- resources/secubox-admin/state-management.css (~600 lines)
- resources/view/secubox-admin/control-center.js (~550 lines)
- resources/secubox-admin/api.js (+145 lines)

Documentation:
- docs/admin-control-center/API-REFERENCE.md (1,200+ lines)
- docs/admin-control-center/EXAMPLES.md (800+ lines)

Files Modified (3):
- package/secubox/secubox-core/Makefile (v0.8.0 → v0.9.0-1)
- package/secubox/luci-app-secubox-admin/Makefile (release 15 → 16)
- package/secubox/secubox-core/root/usr/libexec/rpcd/luci.secubox (+157 lines)

## Installation & Migration

Makefile Updates:
- Added 3 new CLI tools to install section
- Added state-machine.sh to scripts
- Updated package description
- Enhanced postinst to initialize databases
- Auto-sync registry on first install

Postinst Features:
- Automatic state-db.json initialization
- Automatic component-registry.json initialization
- Catalog sync on install
- Version announcement with new features

## Performance & Security

Performance:
- File locking (flock) for atomic state transitions
- State history limited to 100 entries per component
- RPC retry logic with exponential backoff
- Bulk operations use Promise.all for parallel execution
- Component list caching (30 seconds)

Security:
- Frozen state prevents unauthorized modifications
- All state changes logged with timestamp and reason
- System-critical components have additional safeguards
- Proper authentication required for state transitions

## Testing & Validation

Features:
- State transition validation
- Component dependency resolution
- Circular dependency detection
- State consistency checker
- Integration test scripts included in docs

## Breaking Changes

None - Backward Compatible:
- Existing RPC methods remain functional
- State-aware methods are additive
- Components without state default to 'available'
- Migration is automatic on install

## Statistics

Total Implementation:
- Lines of Code: ~4,000
  - Backend: ~1,800 (Bash + JSON)
  - Frontend: ~2,200 (JavaScript + CSS)
  - Documentation: ~2,000 (Markdown)
- Functions/Commands: 40+
- RPC Methods: 11
- CLI Commands: 22
- UI Components: 5
- Documentation Pages: 2

## Next Phase

Remaining from Plan:
- Phase 4: System Hub integration
- Phase 5: Migration script (secubox-migrate-state)
- Phase 6: Additional documentation (ARCHITECTURE.md, STATE-MANAGEMENT.md, etc.)
- Phase 7: Additional UI views (components.js, state-manager.js, debug-panel.js)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-05 10:05:32 +01:00

22 KiB

SecuBox Admin Control Center - Usage Examples

Comprehensive examples for state management and component registry operations.


Table of Contents

  1. CLI Examples
  2. Shell Script Examples
  3. JavaScript Frontend Examples
  4. Integration Examples

CLI Examples

State Management CLI

Basic State Operations

# Get current state of a component
secubox-state get luci-app-auth-guardian

# Set component state
secubox-state set luci-app-auth-guardian starting user_request

# View state history
secubox-state history luci-app-auth-guardian 20

# List all running components
secubox-state list --state=running

# List all apps
secubox-state list --type=app

# Validate state consistency
secubox-state validate luci-app-auth-guardian

# Sync state DB with system
secubox-state sync

Error Handling

# Clear error state
secubox-state clear-error luci-app-vpn-client

# Check component after clearing error
secubox-state get luci-app-vpn-client

Freeze/Unfreeze Components

# Freeze a critical component
secubox-state freeze luci-app-firewall system_critical

# Check frozen state
secubox-state get luci-app-firewall

# Unfreeze (transition back to active)
secubox-state set luci-app-firewall active admin_unfreeze

Component Registry CLI

Component Registration

# Register a new app component
secubox-component register my-custom-app app '{
  "name": "My Custom App",
  "packages": ["my-custom-app", "dependency-pkg"],
  "capabilities": ["custom-feature"],
  "dependencies": {
    "required": ["luci-base"],
    "optional": []
  },
  "managed_services": ["my-service"]
}'

# Register a module
secubox-component register my-module module '{
  "name": "My Module",
  "packages": ["my-module-pkg"]
}'

# Register a widget
secubox-component register my-widget widget '{
  "name": "My Dashboard Widget",
  "packages": ["luci-app-widget-provider"]
}'

Component Queries

# Get component details
secubox-component get luci-app-auth-guardian

# List all apps
secubox-component list --type=app

# List all running components
secubox-component list --state=running

# List components in a profile
secubox-component list --profile=home-security

# Show dependency tree
secubox-component tree luci-app-auth-guardian

# Show what depends on a component (reverse dependencies)
secubox-component affected luci-base

Component Management

# Update component setting
secubox-component set-setting luci-app-auth-guardian enabled true

# Unregister a component
secubox-component unregister my-old-app

Common Workflows CLI

Installing an App (Full Workflow)

#!/bin/bash

APP_ID="luci-app-vpn-client"

# 1. Check if component is registered
if ! secubox-component get "$APP_ID" > /dev/null 2>&1; then
    echo "Component not registered, syncing registry..."
    secubox-sync-registry apps
fi

# 2. Set state to installing
secubox-state set "$APP_ID" installing user_install

# 3. Perform actual installation (this would be done by secubox-appstore)
# opkg install luci-app-vpn-client

# 4. On success, set to installed
secubox-state set "$APP_ID" installed install_success

# 5. Configure the app
secubox-state set "$APP_ID" configuring user_config

# 6. Mark as configured
secubox-state set "$APP_ID" configured config_complete

# 7. Activate
secubox-state set "$APP_ID" activating user_activate
secubox-state set "$APP_ID" active activation_complete

# 8. Start the service
secubox-state set "$APP_ID" starting user_start

# 9. Mark as running
secubox-state set "$APP_ID" running start_success

Bulk State Change

#!/bin/bash

# Stop all running apps
for app_id in $(secubox-state list --state=running --type=app | jq -r '.[].id'); do
    echo "Stopping $app_id..."
    secubox-state set "$app_id" stopping bulk_shutdown
    secubox-state set "$app_id" stopped shutdown_complete
done

Health Check Script

#!/bin/bash

echo "=== SecuBox Component Health Check ==="
echo

# Get all components
components=$(secubox-component list)

# Count by state
echo "Component Distribution:"
echo "  Running:     $(echo "$components" | jq '[.[] | select(.current_state=="running")] | length')"
echo "  Stopped:     $(echo "$components" | jq '[.[] | select(.current_state=="stopped")] | length')"
echo "  Error:       $(echo "$components" | jq '[.[] | select(.current_state=="error")] | length')"
echo "  Frozen:      $(echo "$components" | jq '[.[] | select(.current_state=="frozen")] | length')"
echo "  Disabled:    $(echo "$components" | jq '[.[] | select(.current_state=="disabled")] | length')"
echo

# Show error components
error_count=$(echo "$components" | jq '[.[] | select(.current_state=="error")] | length')
if [ "$error_count" -gt 0 ]; then
    echo "Components in ERROR state:"
    echo "$components" | jq -r '.[] | select(.current_state=="error") | "  - \(.name) (\(.id))"'
    echo
fi

# Show frozen components
frozen_count=$(echo "$components" | jq '[.[] | select(.current_state=="frozen")] | length')
if [ "$frozen_count" -gt 0 ]; then
    echo "Components in FROZEN state:"
    echo "$components" | jq -r '.[] | select(.current_state=="frozen") | "  - \(.name) (\(.id))"'
    echo
fi

# Validate all component states
echo "Validating component states..."
invalid_count=0
for comp_id in $(echo "$components" | jq -r '.[].id'); do
    if ! secubox-state validate "$comp_id" > /dev/null 2>&1; then
        echo "  ⚠ Invalid state: $comp_id"
        invalid_count=$((invalid_count + 1))
    fi
done

if [ "$invalid_count" -eq 0 ]; then
    echo "  ✓ All component states are valid"
else
    echo "  ✗ Found $invalid_count invalid states"
fi

Shell Script Examples

Example: Auto-Start All Apps on Boot

#!/bin/bash
# /etc/init.d/secubox-autostart

START=99
STOP=10

start() {
    echo "Starting SecuBox components..."

    # Get all active components
    components=$(secubox-component list --state=active --type=app)

    for app_id in $(echo "$components" | jq -r '.[].id'); do
        # Check if auto_start is enabled
        auto_start=$(secubox-component get "$app_id" | jq -r '.settings.auto_start // false')

        if [ "$auto_start" = "true" ]; then
            echo "  Starting $app_id..."
            secubox-state set "$app_id" starting boot_autostart

            # Start managed services
            services=$(secubox-component get "$app_id" | jq -r '.managed_services[]')
            for service in $services; do
                /etc/init.d/"$service" start
            done

            secubox-state set "$app_id" running start_success
        fi
    done
}

stop() {
    echo "Stopping SecuBox components..."

    # Get all running components
    components=$(secubox-state list --state=running --type=app)

    for app_id in $(echo "$components" | jq -r '.[].id'); do
        echo "  Stopping $app_id..."
        secubox-state set "$app_id" stopping shutdown

        # Stop managed services
        services=$(secubox-component get "$app_id" | jq -r '.managed_services[]')
        for service in $services; do
            /etc/init.d/"$service" stop
        done

        secubox-state set "$app_id" stopped stop_success
    done
}

Example: Component Dependency Resolver

#!/bin/bash

resolve_dependencies() {
    local component_id="$1"
    local resolved=()
    local seen=()

    resolve_recursive() {
        local comp_id="$1"

        # Check if already seen (circular dependency)
        for s in "${seen[@]}"; do
            if [ "$s" = "$comp_id" ]; then
                echo "Error: Circular dependency detected: $comp_id" >&2
                return 1
            fi
        done

        seen+=("$comp_id")

        # Get required dependencies
        local deps=$(secubox-component get "$comp_id" | jq -r '.dependencies.required[]')

        for dep in $deps; do
            resolve_recursive "$dep"
        done

        # Add to resolved list
        resolved+=("$comp_id")
    }

    resolve_recursive "$component_id"

    # Print in installation order
    printf '%s\n' "${resolved[@]}"
}

# Usage
echo "Install order for luci-app-auth-guardian:"
resolve_dependencies "luci-app-auth-guardian"

Example: State Transition Watcher

#!/bin/bash

watch_state_transitions() {
    local component_id="$1"
    local last_state=""

    echo "Watching state transitions for: $component_id"
    echo "Press Ctrl+C to stop"
    echo

    while true; do
        current_state=$(secubox-state get "$component_id" | jq -r '.current_state')

        if [ "$current_state" != "$last_state" ]; then
            timestamp=$(date "+%Y-%m-%d %H:%M:%S")
            echo "[$timestamp] State changed: $last_state -> $current_state"
            last_state="$current_state"
        fi

        sleep 1
    done
}

# Usage
watch_state_transitions "luci-app-vpn-client"

JavaScript Frontend Examples

Example: Component Dashboard

'use strict';
'require view';
'require secubox-admin.api as api';
'require secubox-admin.components.StateIndicator as StateIndicator';

return view.extend({
    load: function() {
        return api.getAllComponentsWithStates({ type: 'app' });
    },

    render: function(components) {
        var container = E('div', { 'class': 'component-dashboard' });

        components.forEach(function(comp) {
            var card = E('div', {
                'class': 'component-card',
                'style': 'padding: 1rem; margin-bottom: 1rem; border: 1px solid #e5e7eb; border-radius: 0.5rem;'
            });

            // Component name
            var name = E('h3', {}, comp.name);
            card.appendChild(name);

            // State indicator
            var state = comp.state_info ? comp.state_info.current_state : 'unknown';
            var stateIndicator = StateIndicator.render(state, {
                showIcon: true,
                showLabel: true
            });
            card.appendChild(stateIndicator);

            // Action buttons
            var actions = E('div', { 'style': 'margin-top: 1rem; display: flex; gap: 0.5rem;' });

            if (state === 'stopped') {
                var startBtn = E('button', {
                    'class': 'btn cbi-button-action',
                    'click': function() {
                        api.setComponentState(comp.id, 'starting', 'user_action')
                            .then(function() {
                                location.reload();
                            });
                    }
                }, 'Start');
                actions.appendChild(startBtn);
            } else if (state === 'running') {
                var stopBtn = E('button', {
                    'class': 'btn cbi-button-negative',
                    'click': function() {
                        api.setComponentState(comp.id, 'stopping', 'user_action')
                            .then(function() {
                                location.reload();
                            });
                    }
                }, 'Stop');
                actions.appendChild(stopBtn);
            }

            card.appendChild(actions);
            container.appendChild(card);
        });

        return container;
    }
});

Example: State Transition Handler

function handleStateTransition(componentId, newState) {
    // Show loading indicator
    ui.showModal(_('Changing State'), [
        E('p', { 'class': 'spinning' }, _('Updating component state...'))
    ]);

    // Validate transition
    return api.getComponentState(componentId).then(function(stateInfo) {
        var currentState = stateInfo.current_state;

        if (!stateUtils.canTransition(currentState, newState)) {
            ui.hideModal();
            ui.addNotification(null,
                E('p', _('Invalid state transition: %s -> %s').format(currentState, newState)),
                'error'
            );
            return Promise.reject('Invalid transition');
        }

        // Execute transition
        return api.setComponentState(componentId, newState, 'user_action');
    }).then(function(result) {
        ui.hideModal();

        if (result.success) {
            ui.addNotification(null,
                E('p', _('State changed successfully')),
                'success'
            );

            // Reload component data
            return api.getComponentWithState(componentId);
        } else {
            throw new Error(result.message || 'State change failed');
        }
    }).catch(function(error) {
        ui.hideModal();
        ui.addNotification(null,
            E('p', _('Error: %s').format(error.message || error)),
            'error'
        );
    });
}

// Usage
handleStateTransition('luci-app-vpn-client', 'starting');

Example: Real-time State Monitor

var StateMonitor = baseclass.extend({
    __init__: function(componentId) {
        this.componentId = componentId;
        this.pollInterval = 2000; // 2 seconds
        this.callbacks = [];
    },

    start: function() {
        var self = this;
        this.lastState = null;

        this.pollId = poll.add(function() {
            return api.getComponentState(self.componentId).then(function(stateInfo) {
                var currentState = stateInfo.current_state;

                if (currentState !== self.lastState) {
                    self.notifyChange(self.lastState, currentState, stateInfo);
                    self.lastState = currentState;
                }
            });
        }, this.pollInterval / 1000);
    },

    stop: function() {
        if (this.pollId) {
            poll.remove(this.pollId);
            this.pollId = null;
        }
    },

    onChange: function(callback) {
        this.callbacks.push(callback);
    },

    notifyChange: function(oldState, newState, stateInfo) {
        this.callbacks.forEach(function(callback) {
            callback(oldState, newState, stateInfo);
        });
    }
});

// Usage
var monitor = new StateMonitor('luci-app-vpn-client');

monitor.onChange(function(oldState, newState, stateInfo) {
    console.log('State changed:', oldState, '->', newState);

    // Update UI
    var indicator = document.getElementById('state-indicator');
    if (indicator) {
        var newIndicator = StateIndicator.render(newState);
        indicator.replaceWith(newIndicator);
    }
});

monitor.start();

Example: Bulk Operations

function bulkStartComponents(componentIds) {
    ui.showModal(_('Starting Components'), [
        E('p', {}, _('Starting %d components...').format(componentIds.length)),
        E('div', { 'id': 'bulk-progress' })
    ]);

    var progressDiv = document.getElementById('bulk-progress');
    var completed = 0;
    var failed = 0;

    // Start all components in parallel
    return api.bulkSetComponentState(componentIds, 'starting', 'bulk_start')
        .then(function(results) {
            results.forEach(function(result, index) {
                var componentId = componentIds[index];

                if (result.success) {
                    completed++;
                    progressDiv.appendChild(
                        E('div', { 'style': 'color: #10b981;' },
                            '✓ ' + componentId
                        )
                    );
                } else {
                    failed++;
                    progressDiv.appendChild(
                        E('div', { 'style': 'color: #ef4444;' },
                            '✗ ' + componentId + ': ' + (result.error || 'Unknown error')
                        )
                    );
                }
            });

            setTimeout(function() {
                ui.hideModal();

                var message = _('Completed: %d, Failed: %d').format(completed, failed);
                ui.addNotification(null, E('p', message),
                    failed > 0 ? 'warning' : 'success'
                );
            }, 2000);
        });
}

// Usage
var appsToStart = ['luci-app-vpn-client', 'luci-app-firewall', 'luci-app-ddns'];
bulkStartComponents(appsToStart);

Integration Examples

Example: LuCI Form with State Awareness

var form = new form.Map('myapp', _('My Application'));

var section = form.section(form.TypedSection, 'config');

// Add state indicator to section
section.load = function() {
    var self = this;

    return Promise.all([
        form.TypedSection.prototype.load.call(this),
        api.getComponentState('my-app')
    ]).then(function(results) {
        var stateInfo = results[1];

        // Add state info to section title
        var stateIndicator = StateIndicator.render(stateInfo.current_state);
        var titleNode = self.titleFn ? document.querySelector('.cbi-section-node h3') : null;
        if (titleNode) {
            titleNode.appendChild(document.createTextNode(' '));
            titleNode.appendChild(stateIndicator);
        }

        return results[0];
    });
};

// Add state-aware option
var stateOption = section.option(form.DummyValue, '_state', _('Service State'));
stateOption.cfgvalue = function() {
    return api.getComponentState('my-app').then(function(stateInfo) {
        return StateIndicator.render(stateInfo.current_state);
    });
};

// Add control buttons
var controlOption = section.option(form.Button, '_control', _('Service Control'));
controlOption.inputtitle = _('Start');
controlOption.onclick = function() {
    return handleStateTransition('my-app', 'starting');
};

Example: WebSocket State Updates

// Note: Requires WebSocket support in backend

var StateWebSocket = baseclass.extend({
    __init__: function(url) {
        this.url = url || 'ws://localhost:8080/state-updates';
        this.ws = null;
        this.callbacks = {};
    },

    connect: function() {
        var self = this;

        this.ws = new WebSocket(this.url);

        this.ws.onopen = function() {
            console.log('State WebSocket connected');
        };

        this.ws.onmessage = function(event) {
            var data = JSON.parse(event.data);

            if (data.type === 'state_change') {
                self.handleStateChange(data);
            }
        };

        this.ws.onerror = function(error) {
            console.error('WebSocket error:', error);
        };

        this.ws.onclose = function() {
            console.log('WebSocket closed, reconnecting...');
            setTimeout(function() {
                self.connect();
            }, 5000);
        };
    },

    subscribe: function(componentId, callback) {
        if (!this.callbacks[componentId]) {
            this.callbacks[componentId] = [];
        }
        this.callbacks[componentId].push(callback);

        // Send subscribe message
        this.send({
            type: 'subscribe',
            component_id: componentId
        });
    },

    handleStateChange: function(data) {
        var componentId = data.component_id;
        var callbacks = this.callbacks[componentId] || [];

        callbacks.forEach(function(callback) {
            callback(data.old_state, data.new_state, data.state_info);
        });
    },

    send: function(data) {
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
            this.ws.send(JSON.stringify(data));
        }
    }
});

// Usage
var ws = new StateWebSocket();
ws.connect();

ws.subscribe('luci-app-vpn-client', function(oldState, newState, stateInfo) {
    console.log('Real-time update:', oldState, '->', newState);
    // Update UI immediately
});

Testing Examples

Example: Unit Test for State Transitions

describe('State Transitions', function() {
    it('should allow valid transitions', function() {
        expect(stateUtils.canTransition('stopped', 'starting')).toBe(true);
        expect(stateUtils.canTransition('starting', 'running')).toBe(true);
        expect(stateUtils.canTransition('running', 'stopping')).toBe(true);
    });

    it('should reject invalid transitions', function() {
        expect(stateUtils.canTransition('stopped', 'running')).toBe(false);
        expect(stateUtils.canTransition('available', 'running')).toBe(false);
    });

    it('should handle error transitions', function() {
        expect(stateUtils.canTransition('installing', 'error')).toBe(true);
        expect(stateUtils.canTransition('error', 'available')).toBe(true);
    });
});

Example: Integration Test

#!/bin/bash

test_component_lifecycle() {
    local app_id="test-app"

    echo "Testing component lifecycle for: $app_id"

    # 1. Register component
    echo "  1. Registering component..."
    secubox-component register "$app_id" app '{"name":"Test App","packages":["test-pkg"]}'

    # 2. Initialize state
    echo "  2. Initializing state..."
    secubox-state set "$app_id" available init

    # 3. Install
    echo "  3. Installing..."
    secubox-state set "$app_id" installing test
    secubox-state set "$app_id" installed test

    # 4. Activate
    echo "  4. Activating..."
    secubox-state set "$app_id" configuring test
    secubox-state set "$app_id" configured test
    secubox-state set "$app_id" activating test
    secubox-state set "$app_id" active test

    # 5. Start
    echo "  5. Starting..."
    secubox-state set "$app_id" starting test
    secubox-state set "$app_id" running test

    # 6. Stop
    echo "  6. Stopping..."
    secubox-state set "$app_id" stopping test
    secubox-state set "$app_id" stopped test

    # 7. Uninstall
    echo "  7. Uninstalling..."
    secubox-state set "$app_id" uninstalling test
    secubox-state set "$app_id" available test

    # 8. Cleanup
    echo "  8. Cleaning up..."
    secubox-component unregister "$app_id"

    echo "✓ Lifecycle test completed successfully"
}

test_component_lifecycle

See Also:


Version: 1.0 Last Updated: 2026-01-05