<!--
hoody-cron Subskill (sdk)
Auto-generated by Hoody Skills Generator
Generated: 2026-06-20T00:04:11.465Z
Model: mimo-v2.5-pro + fixer:z-ai/glm-5.1
Mode: sdk


Tokens: 5562

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

# hoody-cron Subskill

## Overview

`hoody-cron` provides managed cron job scheduling for Hoody projects. It handles job creation, scheduling, enable/disable toggling, and automatic expiration — eliminating the need to manage crontab files or system-level schedulers directly.

### When to Use

- Schedule recurring tasks (backups, cleanup, notifications)
- Run commands on a predictable cadence (every minute, hourly, daily)
- Manage per-user cron jobs with isolated namespaces
- Enable or disable scheduled jobs without deleting them
- Let jobs auto-expire after a defined period

### How It Fits Hoody Philosophy

hoody-cron follows the Hoody Kit pattern: a containerized microservice with automatic routing, zero DNS configuration, and built-in authentication. Each project gets an isolated cron service instance. Jobs are namespaced per user, supporting multi-tenant scheduling without interference.

### Key Concepts

| Concept | Description |
|---------|-------------|
| **Entry** | A single scheduled job with a command and cron schedule |
| **Crontab** | A user's full crontab configuration (raw crontab text) |
| **Auto-expiration** | Entries can be configured to expire automatically |
| **Enable/disable** | Toggle entries without removing them |

### Service Access

```
import { HoodyClient } from '@hoody-ai/hoody-sdk'

const client = new HoodyClient({
  baseURL: 'https://api.hoody.com',
  token: 'YOUR_TOKEN'
})

// All cron calls use client.cron.*
```

> **Note:** The SDK communicates through the Hoody API proxy. You do not need to construct the Kit service URL directly. See the core SKILL.md for container discovery if direct access is required.

---

## Common Workflows

### 1. Create a Scheduled Job

Create an entry that runs a command on a cron schedule.

```
const entry = await client.cron.entries.create('alice', {
  command: '/usr/bin/backup.sh --incremental',
  schedule: '0 2 * * *'
})

console.log(entry.id)
```

**Response shape:**

```
{
  "id": "abc123",
  "user": "alice",
  "command": "/usr/bin/backup.sh --incremental",
  "schedule": "0 2 * * *",
  "enabled": true,
  "createdAt": "2025-01-15T10:00:00Z"
}
```

**Verify:** Fetch the entry to confirm creation.

```
const verify = await client.cron.entries.get('alice', entry.id)
if (verify.schedule !== '0 2 * * *') throw new Error('Entry creation failed verification')
```

### 2. List All Entries for a User

Retrieve every scheduled job for a user.

```
const entries = await client.cron.entries.list({ user: 'alice' })

for (const e of entries.items) {
  console.log(`${e.id}: ${e.schedule} → ${e.command}`)
}
```

**Paginated collection (all pages):**

```
const allEntries = await client.cron.entries.listAll({ user: 'alice' })

for (const page of allEntries) {
  for (const e of page.items) {
    console.log(e.id)
  }
}
```

**Async iterator (memory-efficient):**

```
const iterator = client.cron.entries.listIterator({ user: 'alice' })

for await (const page of iterator) {
  for (const e of page.items) {
    console.log(e.id)
  }
}
```

### 3. Update an Existing Entry

Modify the schedule or command of an entry.

```
const updated = await client.cron.entries.update('alice', 'abc123', {
  schedule: '0 3 * * *',
  command: '/usr/bin/backup.sh --full'
})

console.log(updated.schedule) // "0 3 * * *"
```

### 4. Delete an Entry

Remove a scheduled job permanently.

```
const result = await client.cron.entries.delete({ user: 'alice', id: 'abc123' })

console.log(result.deleted) // true
```

**Verify deletion:**

```
try {
  await client.cron.entries.get({ user: 'alice', id: 'abc123' })
} catch (err) {
  console.log('Entry confirmed deleted')
}
```

### 5. Manage Raw Crontab

Retrieve or replace a user's entire crontab.

**Get crontab:**

```
const crontab = await client.cron.crontab.get({ user: 'alice' })

console.log(crontab.crontab)
// "0 2 * * * /usr/bin/backup.sh\n0 6 * * 1 /usr/bin/report.sh"
```

**Replace crontab (full overwrite):**

```
const updated = await client.cron.crontab.put('alice', {
  crontab: '0 2 * * * /usr/bin/backup.sh --incremental\n0 18 * * 5 /usr/bin/report.sh'
})

console.log(updated.updated) // true
```

### 6. List All Crontabs Across Users

Global view of every crontab in the project.

```
const allCrontabs = await client.cron.crontab.listGlobal()

for (const c of allCrontabs.items) {
  console.log(c.user)
}
```

**Async iterator for large datasets:**

```
const iterator = client.cron.crontab.listGlobalIterator()

for await (const page of iterator) {
  for (const c of page.items) {
    console.log(c.user)
  }
}
```

### 7. Health Check

Verify the cron service is running.

```
const health = await client.cron.health.check()

console.log(health.status) // "ok"
```

---

## Advanced Operations

### Workflow: Job Lifecycle Management

A complete pattern for creating, verifying, updating, and cleaning up a job.

```
const user = 'bob'

// Step 1: Create
const entry = await client.cron.entries.create(user, {
  command: '/usr/bin/cleanup.sh --temp',
  schedule: '*/15 * * * *'
})

// Step 2: Verify creation
const verify = await client.cron.entries.get(user, entry.id)
if (verify.schedule !== '*/15 * * * *') {
  throw new Error('Entry creation failed verification')
}

// Step 3: Update schedule to less frequent
const updated = await client.cron.entries.update(user, entry.id, {
  schedule: '0 */2 * * *'
})

// Step 4: Cleanup when done
await client.cron.entries.delete(user, entry.id)
```

### Workflow: Bulk Entry Migration

Replace all entries for a user with a new set.

```
const user = 'charlie'

// Step 1: Collect all existing entries
const existing = await client.cron.entries.listAll(user)
const entryIds = existing.flatMap(page => page.items.map(e => e.id))

// Step 2: Delete all existing entries
for (const id of entryIds) {
  await client.cron.entries.delete(user, id)
}

// Step 3: Create new entries
const newJobs = [
  { command: '/usr/bin/backup.sh', schedule: '0 2 * * *' },
  { command: '/usr/bin/report.sh', schedule: '0 6 * * 1' },
  { command: '/usr/bin/cleanup.sh', schedule: '0 0 1 * *' }
]

for (const job of newJobs) {
  await client.cron.entries.create(user, job)
}

// Step 4: Verify count
const final = await client.cron.entries.listAll(user)
const totalCreated = final.reduce((sum, page) => sum + page.items.length, 0)
console.log(`Migrated ${totalCreated} entries`)
```

### Workflow: Scheduled Job Audit

Audit all users' cron jobs for compliance.

```
const problems: string[] = []

const iterator = client.cron.crontab.listGlobalIterator()

for await (const page of iterator) {
  for (const crontab of page.items) {
    const entries = await client.cron.entries.listAll(crontab.user)

    for (const entryPage of entries) {
      for (const entry of entryPage.items) {
        // Flag jobs running more frequently than hourly
        if (entry.schedule.startsWith('*/') && !entry.schedule.startsWith('*/59')) {
          problems.push(`${entry.id}: schedule "${entry.schedule}" may be too frequent`)
        }
      }
    }
  }
}

if (problems.length > 0) {
  console.log('Audit issues found:')
  problems.forEach(p => console.log(`  - ${p}`))
} else {
  console.log('All entries passed audit')
}
```

### Error Recovery Pattern

Handle failures gracefully during multi-step operations.

```
const user = 'dave'
const createdIds: string[] = []

try {
  const e1 = await client.cron.entries.create(user, {
    command: '/usr/bin/task1.sh',
    schedule: '0 * * * *'
  })
  createdIds.push(e1.id)

  const e2 = await client.cron.entries.create(user, {
    command: '/usr/bin/task2.sh',
    schedule: '0 0 * * *'
  })
  createdIds.push(e2.id)

} catch (err) {
  console.error('Operation failed, rolling back')
  for (const id of createdIds) {
    await client.cron.entries.delete(user, id)
  }
  throw err
}
```

### Performance Considerations

- **Use iterators** (`listIterator`, `listGlobalIterator`) for large datasets to avoid loading all pages into memory.
- **Batch deletions** — list all entries first, then delete in sequence. Avoid concurrent deletes on the same user.
- **Health checks** — call `client.cron.health.check()` before bulk operations to fail fast if the service is down.
- **Crontab vs entries** — `put` on crontab is a full replacement; prefer `entries` methods for granular changes.

### Retrieve OpenAPI Specification

```
const spec = await client.cron.system.getOpenApiJson()
console.log(spec.info.title)
```

---

## Quick Reference

### Endpoints

| Operation | SDK Method | HTTP |
|-----------|-----------|------|
| List all crontabs | `client.cron.crontab.listGlobal()` | GET /crontab |
| Get user crontab | `client.cron.crontab.get(user)` | GET /users/{user}/crontab |
| Replace user crontab | `client.cron.crontab.put(user, data)` | PUT /users/{user}/crontab |
| List user entries | `client.cron.entries.list(user)` | GET /users/{user}/entries |
| Create entry | `client.cron.entries.create(user, data)` | POST /users/{user}/entries |
| Get entry | `client.cron.entries.get(user, id)` | GET /users/{user}/entries/{id} |
| Update entry | `client.cron.entries.update(user, id, data)` | PATCH /users/{user}/entries/{id} |
| Delete entry | `client.cron.entries.delete(user, id)` | DELETE /users/{user}/entries/{id} |
| Health check | `client.cron.health.check()` | GET /health |
| OpenAPI JSON | `client.cron.system.getOpenApiJson()` | GET /openapi.json |
| OpenAPI YAML | `client.cron.system.getOpenApiYaml()` | GET /openapi.yaml |

### Required Fields

| Endpoint | Required Fields |
|----------|----------------|
| Create entry | `command` (string), `schedule` (string) |
| Update entry | — (partial update, no required fields) |
| Replace crontab | `crontab` (string) |

### Common Cron Schedules

| Schedule | Meaning |
|----------|---------|
| `* * * * *` | Every minute |
| `*/5 * * * *` | Every 5 minutes |
| `0 * * * *` | Every hour |
| `0 2 * * *` | Daily at 2:00 AM |
| `0 0 * * 0` | Every Sunday at midnight |
| `0 0 1 * *` | First day of every month |

### Response Patterns

- **List responses** include `items` array plus pagination fields
- **Single object responses** return the full entity with `id`, `user`, `command`, `schedule`, `enabled`, `createdAt`
- **Delete responses** include a `deleted` boolean confirmation
- **All `user` and `id` parameters** are passed as method arguments, not in the request body