a job is: - id - executable data - marking: new/taken/done/failed - dates: created, taken, finished - metadata: dict[str,str] keys cannot include : or ' ', neither can include \n server protocol: case-insensitive commands separated by \n on error, send back '?', else send back 'OKAY' commands which allow [job id] or [for job id] to be omitted will operate implicitly on the last-mentioned job, or error if there is none server verbs: - '' ignored - NEW JOB len server reads in len bytes, and sends back a job id - LIST marking? JOBS server sends back a list of all jobs, in the format: id\tcreated\tlen\tmarking - INFO [FOR JOB id] IS server sends back a response of the form: SCRIPT IS len BYTES MARKED AS marking CREATED|TAKEN|FINISHED|UPDATED AT iso-8601 - SCRIPT [FOR JOB id] IS len? get the script as len\ndata, or update the script. if a job is not marked 'new', the update fails - META [FOR JOB id] IS get metadata for id as key: value lines, terminated with a blank line - META key [FOR JOB id] IS value? set/get id.key. nil is returned as a blank line. - META key [FOR JOB id] CLEAR clear a metadata key - TAKE [JOB id] mark a job as taken, record timestamp - TAKE JOB take the first available job. if none is available, error. - MARK [JOB n] AS marking mark a job as complete, record timestamp - CANCEL [JOB n] delete a job from the job queue - all others: send back ?