<!--
hoody-daemon Subskill (sdk)
Auto-generated by Hoody Skills Generator
Generated: 2026-05-06T20:06:28.493Z
Model: mimo-v2.5-pro + fixer:mimo-v2.5-pro
Mode: sdk


Tokens: 7695

DO NOT EDIT MANUALLY - Changes will be overwritten on next generation
-->

# hoody-daemon Subskill

## Overview

**Purpose**: Long-running service management for containerized processes. Start, stop, monitor, and configure daemon programs via HTTP API.

**When to Use**:
- Managing custom background services (workers, queues, websocket servers)
- Running scheduled tasks or persistent processes
- Quick-launching temporary programs for one-off jobs
- Monitoring process health and retrieving logs
- Controlling port-range programs for multi-instance services

**Philosophy**: hoody-daemon provides supervisord-backed process management through a clean HTTP interface. Programs are declaratively configured and persist across container restarts. The service handles the full lifecycle: configuration, activation, execution, monitoring, and cleanup.

**Key Concepts**:
- **Programs**: Persistent daemon configurations stored in `programs.json`
- **Ephemeral Programs**: Temporary processes via quick-start that auto-cleanup
- **Port-Range Programs**: Multi-instance programs spanning a port range
- **System Programs**: Pre-configured services (apache2, nginx) managed via `systemctl`

---

## Common Workflows

### 1. Health Check

Verify the daemon service is running and responsive.

```
const health = await client.daemon.health.check();
console.log(health.status); // "ok"
```

### 2. List All Programs

Retrieve all configured programs with optional filters.

```
// List all programs
const programs = await client.daemon.programs.list();

// Filter by enabled status
const enabled = await client.daemon.programs.list({ enabled: 'true' });

// Include runtime status in response
const withStatus = await client.daemon.programs.list({ include_status: 'true' });
```

### 3. Get Program Details

Retrieve full configuration for a specific program.

```
const program = await client.daemon.programs.get({ id: '123' });
console.log(program.name, program.command, program.enabled);
```

### 4. Add a Custom Program

Create a new daemon program with full configuration.

```
const newProgram = await client.daemon.programs.add({
  name: 'my-worker',
  command: 'node /app/worker.js',
  user: 'app',
  port_range: {
    start: 3000,
    end: 3010
  }
});
console.log(newProgram.id); // Use this ID for subsequent operations
```

### 5. Edit an Existing Program

Update program configuration (partial updates supported).

```
const updated = await client.daemon.programs.edit('123', {
  name: 'my-worker-v2',
  command: 'node /app/worker-v2.js',
  user: 'app',
  port_range: {
    start: 3000,
    end: 3010
  }
});
```

### 6. Enable and Start a Program

Activate a disabled program and start it immediately.

```
// Enable the program (registers with supervisord)
await client.daemon.control.enable({ id: '123' });

// Start the program
await client.daemon.control.start({ id: '123' });

// Verify it's running
const status = await client.daemon.status.get({ id: '123' });
console.log(status.state); // "RUNNING"
```

### 7. Stop and Disable a Program

Gracefully stop a running program and disable it.

```
// Stop the program
await client.daemon.control.stop({ id: '123' });

// Disable it (removes from supervisord)
await client.daemon.control.disable({ id: '123' });
```

### 8. Start/Stop Port-Range Instances

Control specific instances within a port-range program.

```
// Start a specific port instance
await client.daemon.control.start('123', { port: 3005 });

// Stop a specific port instance
await client.daemon.control.stop('123', { port: 3005 });

// Stop all instances
await client.daemon.control.stop('123', { all: true });
```



### 10. Quick-Start Ephemeral Programs

Launch temporary programs that auto-cleanup when stopped.

```
// Launch an ephemeral program
const ephemeral = await client.daemon.quickStart.launch({
  command: 'python /tmp/script.py --arg value',
  user: 'app'
});
console.log(ephemeral.temporary_id); // Use this for subsequent calls

// Check its status
const status = await client.daemon.quickStart.getStatus(ephemeral.temporary_id);

// Get logs
const logs = await client.daemon.quickStart.getEphemeralLogs(ephemeral.temporary_id, {
  type: 'stdout',
  lines: 50
});

// Stop and cleanup
await client.daemon.quickStart.stop(ephemeral.temporary_id);
```

### 11. List Ephemeral Programs

View all currently tracked temporary programs.

```
const ephemeralList = await client.daemon.quickStart.list();
console.log(ephemeralList.programs); // Array of running/pending ephemeral programs
```

### 12. Remove a Program

Permanently delete a program configuration.

```
// Program must be stopped first
await client.daemon.control.stop({ id: '123' });

// Remove permanently
const result = await client.daemon.programs.remove({ id: '123' });
console.log(result.success); // true
```

### 13. Reset to Defaults

Restore all programs to the initial container configuration.

```
// WARNING: Stops all programs and resets to defaults
const result = await client.daemon.programs.reset();
console.log(result.message); // Confirmation message
```

---

## Advanced Operations

### Multi-Instance Worker Deployment

Deploy multiple worker instances across a port range and monitor them.

```
// 1. Add a port-range program
const program = await client.daemon.programs.add({
  name: 'api-workers',
  command: 'node /app/server.js',
  user: 'app',
  port_range: {
    start: 4000,
    end: 4010
  }
});

// 2. Enable the program
await client.daemon.control.enable(program.id);

// 3. Start specific instances
for (let port = 4000; port <= 4005; port++) {
  await client.daemon.control.start(program.id, { port });
}

// 4. Check all instances status
const status = await client.daemon.status.get(program.id);
console.log(status.instances); // Array of running instances with ports

// 5. Scale down - stop some instances
await client.daemon.control.stop(program.id, { port: 4004 });
await client.daemon.control.stop(program.id, { port: 4005 });
```

### Error Recovery Pattern

Handle failed programs with automatic restart logic.

```
async function ensureProgramRunning(programId: string, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    const status = await client.daemon.status.get(programId);
    
    if (status.state === 'RUNNING') {
      console.log(`Program ${programId} is running`);
      return true;
    }
    
    console.log(`Attempt ${attempt}: Starting program ${programId}`);
    
    try {
      await client.daemon.control.start(programId);
      
      // Wait and verify
      await new Promise(resolve => setTimeout(resolve, 2000));
      const verifyStatus = await client.daemon.status.get(programId);
      
      if (verifyStatus.state === 'RUNNING') {
        return true;
      }
    } catch (error) {
      console.error(`Start attempt ${attempt} failed:`, error.message);
    }
  }
  
  console.error(`Failed to start program ${programId} after ${maxRetries} attempts`);
  return false;
}
```

### Batch Status Monitoring

Monitor all programs at once for dashboard or alerting.

```
async function getSystemHealth() {
  // Get all program statuses
  const allStatus = await client.daemon.status.getAll();
  
  const summary = {
    total: 0,
    running: 0,
    stopped: 0,
    errored: 0,
    programs: []
  };
  
  for (const [id, status] of Object.entries(allStatus.programs)) {
    summary.total++;
    
    if (status.state === 'RUNNING') {
      summary.running++;
    } else if (status.state === 'STOPPED') {
      summary.stopped++;
    } else {
      summary.errored++;
    }
    
    summary.programs.push({
      id,
      name: status.name,
      state: status.state,
      uptime: status.uptime
    });
  }
  
  return summary;
}
```

### Ephemeral Program for One-Off Tasks

Run a temporary task and collect results before cleanup.

```
async function runEphemeralTask(command: string) {
  // Launch the task
  const task = await client.daemon.quickStart.launch({
    command,
    user: 'app'
  });
  
  const taskId = task.temporary_id;
  console.log(`Task ${taskId} started`);
  
  // Poll until completion
  let status;
  do {
    await new Promise(resolve => setTimeout(resolve, 1000));
    status = await client.daemon.quickStart.getStatus(taskId);
    console.log(`Task state: ${status.state}`);
  } while (status.state === 'RUNNING' || status.state === 'STARTING');
  
  // Collect logs
  const logs = await client.daemon.quickStart.getEphemeralLogs(taskId, {
    type: 'stdout',
    lines: 1000
  });
  
  // Cleanup
  await client.daemon.quickStart.stop(taskId);
  
  return {
    exitCode: status.exit_code,
    output: logs.lines
  };
}
```

### Performance Considerations

- **Batch Operations**: Use `getAll()` status endpoint instead of individual `get()` calls
- **Log Retrieval**: Limit `lines` parameter to avoid large payloads (default: 100)
- **Port Ranges**: Maximum 1000 ports per program; use smaller ranges for faster startup
- **Quick-Start Cleanup**: Always stop ephemeral programs when done to free resources
- **Status Polling**: Use 1-2 second intervals; avoid tight loops

---

## Quick Reference

### Essential Endpoints

| Operation | SDK Method | HTTP |
|-----------|------------|------|
| Health check | `client.daemon.health.check()` | GET `/api/v1/daemon/health` |
| List programs | `client.daemon.programs.list()` | GET `/api/v1/daemon/programs` |
| Get program | `client.daemon.programs.get(id)` | GET `/api/v1/daemon/programs/{id}` |
| Add program | `client.daemon.programs.add(data)` | POST `/api/v1/daemon/programs/add` |
| Edit program | `client.daemon.programs.edit(id, data)` | POST `/api/v1/daemon/programs/edit/{id}` |
| Remove program | `client.daemon.programs.remove(id)` | POST `/api/v1/daemon/programs/remove/{id}` |
| Enable | `client.daemon.control.enable(id)` | POST `/api/v1/daemon/programs/{id}/enable` |
| Disable | `client.daemon.control.disable(id)` | POST `/api/v1/daemon/programs/{id}/disable` |
| Start | `client.daemon.control.start(id)` | POST `/api/v1/daemon/programs/{id}/start` |
| Stop | `client.daemon.control.stop(id)` | POST `/api/v1/daemon/programs/{id}/stop` |
| All statuses | `client.daemon.status.getAll()` | GET `/api/v1/daemon/status` |
| Program status | `client.daemon.status.get(id)` | GET `/api/v1/daemon/status/{id}` |

| Quick-start | `client.daemon.quickStart.launch(data)` | POST `/api/v1/daemon/quick-start` |
| Ephemeral status | `client.daemon.quickStart.getStatus(id)` | GET `/api/v1/daemon/quick-start/{id}/status` |
| Ephemeral logs | `client.daemon.quickStart.getEphemeralLogs(id)` | GET `/api/v1/daemon/quick-start/{id}/logs` |
| Stop ephemeral | `client.daemon.quickStart.stop(id)` | POST `/api/v1/daemon/quick-start/{id}/stop` |

### Required Fields

**Add/Edit Program**:
- `name`: string (unique, no quotes)
- `command`: string (full command with arguments)
- `user`: string (existing system user)
- `port_range.start`: integer
- `port_range.end`: integer (max 1000 ports)

**Quick-Start**:
- `command`: string
- `user`: string

### Typical Response Formats

**Program Object**:
```
{
  "id": 123,
  "name": "my-worker",
  "command": "node /app/worker.js",
  "user": "app",
  "enabled": true,
  "port_range": {
    "start": 3000,
    "end": 3010
  }
}
```

**Status Object**:
```
{
  "state": "RUNNING",
  "pid": 12345,
  "uptime": 3600,
  "exit_code": null
}
```

**Health Response**:
```
{
  "status": "ok",
  "timestamp": "2025-01-15T10:30:00Z"
}
```