BrowserProviderJobState browser-provider.job-state@1
Generated at from
openvibe-contracts v0.177.0 and
openvibe-sdk v0.44.0.
- Version
- 1.0.0
- Owner
- run
- Visibility
- internal
- Status
- planned
- Compatibility
- backward
- Decision
- ADR-034
- Schema
https://openvibe.network/contracts/browser-provider/job-state.v1.json
browser-provider.job-state@1 (PLANNED; plan T14 R4a, ADR-034): an external managed-browser provider's state of one job, the body of GET <provider>/jobs/:id and the `job` of browser-provider.job-create-result@1. OpenVibe.Run validates it before adopting any of it: a malformed state is never adopted, and the job stays as Run last saw it. A 404 on GET or on cancel means the provider does not know the job (Run fails the job run.job.worker_failed). STATE is queued, running, then one end state: succeeded, failed, cancelled or expired. WALL_MS is the provider's CLAIM of the time the job ran, nothing more: Run bills from its own observed duration and clamps a larger claim (observed plus 5 s), so this field never sets a price. STARTED_MS and FINISHED_MS are the provider's clock, Unix ms, null until they happen. EXIT_REASON uses the reasons of platform.job-frame@1 `job_exit` (exited, cancelled, ttl, limit, stopped, failed) and is null until the job ended. EXIT_CODE is the browser script's exit status, null when it was killed or never ran. RESULT is the declared output (an object, at most 256 KiB serialized), null unless the job succeeded. STDOUT is the captured output in the ring shape OpenVibe.Bot returns (bot.job-dispatch-result@1) so Run's poller streams it the same way, or null when there is none. There is no node, project or class field: a provider cannot name a node or a project.
Fields
| Field | Type | Required | Description | Constraints |
|---|---|---|---|---|
id | https://openvibe.network/contracts/platform/job-frame.v1.json#/$defs/job_id | yes | ||
state | enum | yes | queued (accepted, not started), running, then one end state: succeeded (exited 0), failed (another exit code, a limit, or the provider could not run it), cancelled (a cancel), expired (the job's lifetime ran out). |
|
started_ms | integer | null | yes | When the job started, provider's clock, Unix ms; null until it started. |
|
finished_ms | integer | null | yes | When the job ended, provider's clock, Unix ms; null until it ended. |
|
wall_ms | integer | yes | The provider's CLAIM of the running time; Run clamps it to its own observed duration plus 5 s and never bills above that. 0 while the job has not run. |
|
exit_reason | enum | yes | Why the job ended, as platform.job-frame@1 job_exit reason; null until it ended. |
|
exit_code | integer | null | yes | The script's exit status; null when it was killed or never ran. | |
result | object | null | yes | The declared output as a JSON object, at most 256 KiB serialized; null unless the job succeeded. | |
stdout | one of | yes | The captured stdout, or null when the job has none. |
Examples
From the contract's own test fixtures: valid ones validate, rejected ones must fail.
Valid: failed-limit
{
"id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"state": "failed",
"started_ms": 1790935200250,
"finished_ms": 1790935260250,
"wall_ms": 60000,
"exit_reason": "limit",
"exit_code": null,
"result": null,
"stdout": null
}Valid: queued
{
"id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"state": "queued",
"started_ms": null,
"finished_ms": null,
"wall_ms": 0,
"exit_reason": null,
"exit_code": null,
"result": null,
"stdout": null
}Valid: running-with-stdout
{
"id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"state": "running",
"started_ms": 1790935200250,
"finished_ms": null,
"wall_ms": 1200,
"exit_reason": null,
"exit_code": null,
"result": null,
"stdout": {
"text": "loaded\n",
"first_seq": 1,
"last_seq": 1,
"truncated": false
}
}Valid: succeeded
{
"id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"state": "succeeded",
"started_ms": 1790935200250,
"finished_ms": 1790935202600,
"wall_ms": 2350,
"exit_reason": "exited",
"exit_code": 0,
"result": {
"title": "Example Domain"
},
"stdout": {
"text": "loaded\ndone\n",
"first_seq": 1,
"last_seq": 2,
"truncated": false
}
}Rejected: class-field
{
"id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"state": "queued",
"started_ms": null,
"finished_ms": null,
"wall_ms": 0,
"exit_reason": null,
"exit_code": null,
"result": null,
"stdout": null,
"class": "browser"
}Rejected: missing-wall-ms
{
"id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"state": "queued",
"started_ms": null,
"finished_ms": null,
"exit_reason": null,
"exit_code": null,
"result": null,
"stdout": null
}Rejected: negative-started-ms
{
"id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"state": "running",
"started_ms": -5,
"finished_ms": null,
"wall_ms": 1200,
"exit_reason": null,
"exit_code": null,
"result": null,
"stdout": {
"text": "loaded\n",
"first_seq": 1,
"last_seq": 1,
"truncated": false
}
}Rejected: negative-wall-ms
{
"id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"state": "succeeded",
"started_ms": 1790935200250,
"finished_ms": 1790935202600,
"wall_ms": -1,
"exit_reason": "exited",
"exit_code": 0,
"result": {
"title": "Example Domain"
},
"stdout": {
"text": "loaded\ndone\n",
"first_seq": 1,
"last_seq": 2,
"truncated": false
}
}Rejected: node-id
{
"id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"state": "queued",
"started_ms": null,
"finished_ms": null,
"wall_ms": 0,
"exit_reason": null,
"exit_code": null,
"result": null,
"stdout": null,
"node_id": "dev_01JAB2C3D4E5F6G7H8J9K0MNPQ"
}Rejected: project-id
{
"id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"state": "queued",
"started_ms": null,
"finished_ms": null,
"wall_ms": 0,
"exit_reason": null,
"exit_code": null,
"result": null,
"stdout": null,
"project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ"
}Rejected: result-not-object
{
"id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"state": "succeeded",
"started_ms": 1790935200250,
"finished_ms": 1790935202600,
"wall_ms": 2350,
"exit_reason": "exited",
"exit_code": 0,
"result": "ok",
"stdout": {
"text": "loaded\ndone\n",
"first_seq": 1,
"last_seq": 2,
"truncated": false
}
}Rejected: stdout-without-seq
{
"id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"state": "running",
"started_ms": 1,
"finished_ms": null,
"wall_ms": 1,
"exit_reason": null,
"exit_code": null,
"result": null,
"stdout": {
"text": "x"
}
}Rejected: unknown-exit-reason
{
"id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"state": "succeeded",
"started_ms": 1790935200250,
"finished_ms": 1790935202600,
"wall_ms": 2350,
"exit_reason": "crashed",
"exit_code": 0,
"result": {
"title": "Example Domain"
},
"stdout": {
"text": "loaded\ndone\n",
"first_seq": 1,
"last_seq": 2,
"truncated": false
}
}Rejected: unknown-state
{
"id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"state": "placed",
"started_ms": null,
"finished_ms": null,
"wall_ms": 0,
"exit_reason": null,
"exit_code": null,
"result": null,
"stdout": null
}Rejected: wall-ms-string
{
"id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"state": "succeeded",
"started_ms": 1790935200250,
"finished_ms": 1790935202600,
"wall_ms": "2350",
"exit_reason": "exited",
"exit_code": 0,
"result": {
"title": "Example Domain"
},
"stdout": {
"text": "loaded\ndone\n",
"first_seq": 1,
"last_seq": 2,
"truncated": false
}
}Validate
const contracts = require('openvibe-contracts');
contracts.validate('browser-provider.job-state@1', value); // { valid, errors: [{ path, message }] }