mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-13 10:49:41 +02:00
docs(cron): document job API and launch statuses
I corrected the toolbar location and documented the existing cron request and response fields, with checks for route, schema, and status coverage.
This commit is contained in:
@@ -87,6 +87,85 @@ the HTTP status.
|
|||||||
|
|
||||||
Adding a new error code is non-breaking; removing or renaming one is a major change.
|
Adding a new error code is non-breaking; removing or renaming one is a major change.
|
||||||
|
|
||||||
|
## Cron jobs
|
||||||
|
|
||||||
|
Saved jobs and their launch history are separate from the legacy `/api/scheduled`
|
||||||
|
duration-bounded loops. Use `/api/v1/cron/...` in external clients; `/api/cron/...`
|
||||||
|
is the unversioned alias. These routes use the response envelope above; the table
|
||||||
|
lists the value inside `data` on success.
|
||||||
|
|
||||||
|
| Method | Path | Request body | Response `data` |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| GET | `/api/v1/cron/jobs` | None | `CronJob[]` |
|
||||||
|
| POST | `/api/v1/cron/jobs` | Full job definition below | `{ job: CronJob }` |
|
||||||
|
| GET | `/api/v1/cron/jobs/:id` | None | `CronJob` |
|
||||||
|
| PUT | `/api/v1/cron/jobs/:id` | Partial job definition | `{ job: CronJob }` |
|
||||||
|
| DELETE | `/api/v1/cron/jobs/:id` | None | `{}` |
|
||||||
|
| PUT | `/api/v1/cron/jobs/:id/enabled` | `{ enabled: boolean }` | `{ job: CronJob }` |
|
||||||
|
| POST | `/api/v1/cron/jobs/:id/run` | None | `{ run: CronJobRun, activeAgents: number }` |
|
||||||
|
| GET | `/api/v1/cron/jobs/:id/runs` | None | `CronJobRun[]` |
|
||||||
|
| GET | `/api/v1/cron/runs` | None | `CronJobRun[]` |
|
||||||
|
|
||||||
|
### Job request fields
|
||||||
|
|
||||||
|
The create body requires `name`, `agentType`, `workingDir`, `promptMode`,
|
||||||
|
`inputMode`, `scheduleType`, `enabled`, and `concurrencyPolicy`. Additional fields
|
||||||
|
are required according to the selected prompt and schedule:
|
||||||
|
|
||||||
|
| Field | Type / validation |
|
||||||
|
| --- | --- |
|
||||||
|
| `name` | String, 1–200 characters |
|
||||||
|
| `agentType` | A supported session mode (including `shell`) |
|
||||||
|
| `workingDir` | Existing, allowed working-directory path |
|
||||||
|
| `launchCommand` | Optional single-line string, at most 2000 characters; for shell jobs |
|
||||||
|
| `promptMode` | `inline_text` or `prompt_file_path` |
|
||||||
|
| `promptText` | Required for `inline_text`; nonempty single-line string, at most 100000 characters |
|
||||||
|
| `promptFilePath` | Required for `prompt_file_path`; regular file confined to `workingDir`, at most 1 MiB, read when the job fires |
|
||||||
|
| `inputMode` | `paste` or `typed` |
|
||||||
|
| `scheduleType` | `once`, `interval`, `daily`, or `weekly` |
|
||||||
|
| `runAt` | Required for `once`; positive integer Unix timestamp in milliseconds |
|
||||||
|
| `intervalMinutes` | Required for `interval`; integer from 1 to 525600 |
|
||||||
|
| `dailyTime` | Required for `daily`; `HH:MM` in server-local time |
|
||||||
|
| `weeklyDays` | Required for `weekly`; 1–7 weekday integers, 0 (Sunday) through 6 (Saturday) |
|
||||||
|
| `weeklyTime` | Required for `weekly`; `HH:MM` in server-local time |
|
||||||
|
| `enabled` | Boolean |
|
||||||
|
| `concurrencyPolicy` | `warn_only` or `skip_if_same_agent_running`; scheduled runs only |
|
||||||
|
| `autoClosePreviousSession` | Optional boolean, default `true`; ignored for `once` |
|
||||||
|
| `notes` | Optional string, at most 2000 characters |
|
||||||
|
|
||||||
|
`PUT /jobs/:id` accepts any subset of these fields, then validates the merged job.
|
||||||
|
When changing `promptMode` or `scheduleType`, supply the fields the new mode needs.
|
||||||
|
`Run Now` works even when the job is disabled, bypasses the scheduled concurrency
|
||||||
|
policy, and does not change the schedule. `activeAgents` counts live sessions of
|
||||||
|
the same agent type, excluding sessions created by this job.
|
||||||
|
|
||||||
|
### Job and run response fields
|
||||||
|
|
||||||
|
`CronJob` contains the request fields plus server-maintained `id`, optional
|
||||||
|
`owner` (multi-user mode), `createdAt`, `updatedAt`, `lastRunAt`, `nextRunAt`,
|
||||||
|
`lastStatus`, `lastDueKey`, and optional `completedOnce`. Times are Unix
|
||||||
|
milliseconds; `lastRunAt`, `nextRunAt`, `lastStatus`, and `lastDueKey` can be `null`.
|
||||||
|
|
||||||
|
`CronJobRun` contains `id`, `cronJobId`, nullable `sessionId` and `sessionName`,
|
||||||
|
`startedAt`, nullable `finishedAt`, `status`, optional `errorMessage`,
|
||||||
|
`triggerType` (`scheduled` or `manual_run_now`), and nullable `createdSessionUrl`.
|
||||||
|
Run times are also Unix milliseconds. Status is one of `created`,
|
||||||
|
`session_started`, `prompt_sent`, `failed`, or `skipped`.
|
||||||
|
|
||||||
|
Prompt delivery continues asynchronously after session launch, so `Run Now` can
|
||||||
|
return `session_started` before the prompt is sent. Read run history for the later
|
||||||
|
status. `finishedAt` refers to the launch/prompt-delivery attempt, **not completion
|
||||||
|
of the agent's task**; `prompt_sent` does not prove that the task succeeded.
|
||||||
|
|
||||||
|
In multi-user mode, list/history endpoints filter to accessible jobs. An unknown
|
||||||
|
or inaccessible job returns `NOT_FOUND`. Job creation and updates can return
|
||||||
|
`FORBIDDEN` for a working directory outside the owner's workspace or a shell /
|
||||||
|
launch-command job without the required privilege grant. Invalid definitions or
|
||||||
|
working directories return `INVALID_INPUT`; launch/delivery failures are recorded
|
||||||
|
on the run, so inspect its `status` and `errorMessage` even after an HTTP success.
|
||||||
|
|
||||||
|
See [Cron Jobs](wiki/Cron-Jobs.md) for the UI, scheduling, and prompt-file rules.
|
||||||
|
|
||||||
## Long-polling (agent wait)
|
## Long-polling (agent wait)
|
||||||
|
|
||||||
Three calls block until something happens instead of answering immediately. They
|
Three calls block until something happens instead of answering immediately. They
|
||||||
|
|||||||
+12
-7
@@ -4,8 +4,8 @@ Saved, named jobs that start a session and send it a prompt on a schedule. Cron
|
|||||||
sessions: *every weekday at 03:00, open a Claude session in `~/proj` and tell it to update
|
sessions: *every weekday at 03:00, open a Claude session in `~/proj` and tell it to update
|
||||||
dependencies and open a PR.*
|
dependencies and open a PR.*
|
||||||
|
|
||||||
The ⏰ **Cron** header button is opt-in. Turn it on in
|
The ⏰ **Cron** button in the bottom toolbar is opt-in. Turn it on under
|
||||||
**App Settings → Header & Panels**.
|
**App Settings → Header & Panels → Scheduling**.
|
||||||
|
|
||||||
## Creating a job
|
## Creating a job
|
||||||
|
|
||||||
@@ -96,11 +96,16 @@ The skip policy has the details you would want it to have:
|
|||||||
|
|
||||||
Every fire is recorded per job, with a status:
|
Every fire is recorded per job, with a status:
|
||||||
|
|
||||||
| Status | Meaning |
|
| Status | Meaning |
|
||||||
| --------- | -------------------------------------------------------------------- |
|
| ----------------- | ----------------------------------------------------------------- |
|
||||||
| `created` | The run started and a session was created. |
|
| `created` | The run record was created; the session has not started yet. |
|
||||||
| `skipped` | The concurrency policy blocked it. Not counted as a run. |
|
| `session_started` | The session started; prompt delivery is still pending. |
|
||||||
| `failed` | The prompt could not be resolved, or the working directory was gone. |
|
| `prompt_sent` | The prompt was sent to the session. |
|
||||||
|
| `skipped` | The concurrency policy blocked it. Not counted as a run. |
|
||||||
|
| `failed` | Prompt resolution, session launch, or prompt delivery failed. |
|
||||||
|
|
||||||
|
History records whether the session started and the prompt was delivered, **not whether
|
||||||
|
the agent's task succeeded**. `prompt_sent` is not a task-completion signal.
|
||||||
|
|
||||||
The schedule is advanced **before** the session launches, so a slow start cannot cause the
|
The schedule is advanced **before** the session launches, so a slow start cannot cause the
|
||||||
same job to re-trigger.
|
same job to re-trigger.
|
||||||
|
|||||||
@@ -92,6 +92,32 @@ Roughly 235 handlers across 26 route modules. By domain:
|
|||||||
|
|
||||||
Each route module documents its own endpoints in its file header.
|
Each route module documents its own endpoints in its file header.
|
||||||
|
|
||||||
|
## Cron jobs
|
||||||
|
|
||||||
|
Saved scheduled jobs are distinct from the legacy `/api/scheduled` loops. Their
|
||||||
|
versioned endpoints are:
|
||||||
|
|
||||||
|
| Method | Path | Purpose |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GET | `/api/v1/cron/jobs` | List jobs |
|
||||||
|
| POST | `/api/v1/cron/jobs` | Create a job |
|
||||||
|
| GET | `/api/v1/cron/jobs/:id` | Read a job |
|
||||||
|
| PUT | `/api/v1/cron/jobs/:id` | Update a job (partial body) |
|
||||||
|
| DELETE | `/api/v1/cron/jobs/:id` | Delete a job |
|
||||||
|
| PUT | `/api/v1/cron/jobs/:id/enabled` | Enable/disable with `{ enabled: boolean }` |
|
||||||
|
| POST | `/api/v1/cron/jobs/:id/run` | Run now, without changing the schedule |
|
||||||
|
| GET | `/api/v1/cron/jobs/:id/runs` | Read a job's run history |
|
||||||
|
| GET | `/api/v1/cron/runs` | Read all accessible run history |
|
||||||
|
|
||||||
|
Responses use the envelope above: job lists/history have arrays in `data`, a
|
||||||
|
single-job GET has the job itself, create/update/enable have `{ job }`, delete has
|
||||||
|
`{}`, and Run Now has `{ run, activeAgents }`. Prompt delivery is asynchronous;
|
||||||
|
`session_started` and `prompt_sent` describe launch/delivery, not task success.
|
||||||
|
|
||||||
|
The [cron API reference](https://github.com/Ark0N/Codeman/blob/master/docs/api-reference.md#cron-jobs)
|
||||||
|
lists all request and response fields, validation, and ownership restrictions.
|
||||||
|
[Creating a job](Cron-Jobs) covers the UI and schedule semantics.
|
||||||
|
|
||||||
## Long-polling instead of polling
|
## Long-polling instead of polling
|
||||||
|
|
||||||
Three calls block until something happens, so an agent driving Codeman from a shell can wait
|
Three calls block until something happens, so an agent driving Codeman from a shell can wait
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { CronJobSchema } from '../src/web/schemas.js';
|
||||||
|
|
||||||
|
const read = (path: string) => readFileSync(new URL(`../${path}`, import.meta.url), 'utf8');
|
||||||
|
const reference = read('docs/api-reference.md');
|
||||||
|
const wiki = read('docs/wiki/HTTP-API.md');
|
||||||
|
const guide = read('docs/wiki/Cron-Jobs.md');
|
||||||
|
|
||||||
|
const cronSection = (text: string) => text.split('## Cron jobs\n')[1]?.split('\n## ')[0] ?? '';
|
||||||
|
|
||||||
|
describe('cron documentation', () => {
|
||||||
|
it('documents every cron route in both API references', () => {
|
||||||
|
const routes = [...read('src/web/routes/cron-routes.ts').matchAll(/app\.(get|post|put|delete)\('([^']+)'/g)];
|
||||||
|
expect(routes).toHaveLength(9);
|
||||||
|
for (const [, method, path] of routes) {
|
||||||
|
const row = `| ${method.toUpperCase()} | \`${path.replace('/api/', '/api/v1/')}\` |`;
|
||||||
|
expect(cronSection(reference)).toContain(row);
|
||||||
|
expect(cronSection(wiki)).toContain(row);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('documents every accepted job field and validates the guide example', () => {
|
||||||
|
for (const field of Object.keys(CronJobSchema.shape)) {
|
||||||
|
expect(cronSection(reference)).toContain(`| \`${field}\` |`);
|
||||||
|
}
|
||||||
|
const example = guide.match(/-d '(\{[\s\S]*?\})'/)?.[1];
|
||||||
|
expect(example).toBeDefined();
|
||||||
|
expect(CronJobSchema.safeParse(JSON.parse(example!)).success).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('documents all run statuses without treating prompt delivery as task success', () => {
|
||||||
|
const statusType = read('src/types/cron.ts').match(/export type CronJobRunStatus = ([^;]+);/)?.[1];
|
||||||
|
expect(statusType).toBeDefined();
|
||||||
|
for (const [, status] of statusType!.matchAll(/'([^']+)'/g)) {
|
||||||
|
expect(guide).toContain(`| \`${status}\``);
|
||||||
|
expect(cronSection(reference)).toContain(`\`${status}\``);
|
||||||
|
}
|
||||||
|
expect(guide).toContain("not whether\nthe agent's task succeeded");
|
||||||
|
expect(guide).toContain('bottom toolbar');
|
||||||
|
expect(guide).toContain('App Settings → Header & Panels → Scheduling');
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user