@transai/connector-runner-ipp
v1.0.3
Published
Readme
transAI IPP Connector
Generates PDFs from Markdown and prints documents on any IPP/IPPS printer. Several PDFs are printed as one merged job, optionally double-sided, on a printer chosen by name.
Connector configuration
{
"fileDsn": "file:///",
"requestingUserName": "xod-connector",
"printerTimeout": 30000,
"pollIntervalMs": 2000,
"maxPollAttempts": 30,
"printers": [
{ "name": "office", "printerUrl": "ipp://10.0.0.5:631/ipp/print" },
{ "name": "warehouse", "printerUrl": "ipps://10.0.0.9/ipp/print" }
]
}| Field | Required | Default | Description |
| ----------------------- | -------- | --------------- | ----------------------------------------------------------------------- |
| printers | yes | | One printer object or an array of them |
| printers[].name | no | | Label used only to select the printer; never sent to the printer |
| printers[].printerUrl | yes | | ipp:// or ipps:// URL of the printer |
| fileDsn | yes | | Storage root that generate_pdf writes to and print_files reads from |
| requestingUserName | no | xod-connector | IPP requesting-user-name: the job owner shown in the printer queue |
| printerTimeout | no | 30000 | IPP request timeout in ms |
| pollIntervalMs | no | 2000 | Delay between job status polls |
| maxPollAttempts | no | 30 | Polls before a job is reported as poll-timeout |
The connector refuses to start when printers is missing, empty, or an entry has no printerUrl.
Actions
The action is selected by its identifier: generate_pdf or print_files. Any other identifier returns badRequest.
generate_pdf
Renders Markdown to a PDF and writes it to storage. It does not print.
| Input | Required | Description |
| ---------- | -------- | ----------------------------------------------------------- |
| markdown | yes | Markdown text to render; must not be empty |
| path | yes | Target file, resolved against fileDsn; must end in .pdf |
| title | no | Printed as the document title and stored as the PDF title |
| Action configuration | Description |
| -------------------- | ---------------------------------------------- |
| pageSize | A4 (default) or Letter |
| margin | Page margin in points (default 56, minimum 18) |
| header | Text centred at the top of every page |
| footer | Text centred at the bottom of every page |
| pageNumbers | Add Page n / N to the footer |
Missing directories are created and an existing file is overwritten. A path outside fileDsn returns badRequest. In a test run the PDF is rendered but not written; a following print_files in the same test run skips the missing file (see below).
Output:
{ "dsn": "file:///tmp/somedirectory/file.pdf", "path": "/tmp/somedirectory/file.pdf", "pages": 2, "bytes": 18234 }Pass dsn in filepaths of print_files to print the generated file.
print_files
Prints existing files from storage.
| Input | Required | Description |
| ----------- | -------- | ---------------------------------------------------------------------------------------------------- |
| filepaths | yes | Non-empty array of files to print, in order. Each is a path relative to fileDsn or a DSN inside it |
| printer | no | Printer name from printers. Unknown or absent: the first printer (an unknown name logs a warning) |
| duplex | no | Boolean. true two-sided (long edge), false one-sided, absent: printer default |
| jobName | no | Name in the printer queue. See below for the default |
A null value for printer, duplex or jobName counts as absent. duplex must be a boolean, otherwise the action returns badRequest.
The document format comes from the file extension: .pdf, .ps, .pcl, .png, .jpg, .jpeg, .pwg, .tif, .tiff. Before every job the connector reads the printer's document-format-supported; if the printer reports formats without the one needed, the action returns badRequest. If the printer does not report the attribute the job is sent anyway. For separate jobs (see below) the formats of all files are checked once before the first job is sent, so an unsupported file returns badRequest without anything being printed.
One merged job or separate jobs
- Every file is a PDF: the PDFs are merged, in order, into one PDF and sent as a single job. The printer queue holds one job, so other senders cannot interleave pages. A single PDF is sent as-is: it is not parsed or validated, so the printer decides what to do with it. Merging needs readable PDFs: when a PDF is merged with others, an encrypted PDF (including one protected only by an owner password) or a malformed PDF the merger cannot parse returns
badRequestand nothing is printed. - Any file is not a PDF: each file is its own job, sent in order, each waiting until the previous job reached a final state. The first failed job stops the run and the remaining files are not sent. Ordering against other senders is not guaranteed in this mode.
Once a separate job has printed, any later failure (a job that ends unsuccessfully, a connection error or a timeout) returns internalServerError whose message lists the job ids already printed. It is never reported as badRequest, so check the printed jobs before sending the request again to avoid duplicate prints.
Duplex
duplex maps to the IPP sides attribute: two-sided-long-edge for true, one-sided for false, not sent when absent.
When PDFs are merged and duplex is true, a blank page is added after every document with an odd page count, except the last, so each document starts on a new sheet. Separate jobs get no padding, but still send sides.
Job name
A jobName from the caller names every job. Without it, a merged job uses the first file's name and separate jobs use their own file's name.
Response
{
"printer": "office",
"results": [
{
"jobId": 42,
"jobState": "completed",
"jobStateReasons": [],
"success": true,
"filePaths": ["/tmp/somedirectory/cover.pdf", "orders/123.pdf"]
}
]
}A merged job yields one result listing all its files; separate jobs yield one result per file. printer is the name of the printer used, or its URL when it has no name. A job that ends canceled or aborted, or does not finish in time (poll-timeout), returns internalServerError with the job id, state and reasons; for separate jobs the message lists the jobs completed so far.
In a test run, files are read and merged but no IPP calls are made: each job reports jobId: -1, jobState: completed and reason test-mode. A file that does not exist is skipped with a warning naming it, so generate_pdf followed by print_files can be test-run even though the test run of generate_pdf wrote nothing. When every file is skipped the response has results: []. A path outside fileDsn and other read errors still return badRequest; outside a test run a missing file returns badRequest.
Markdown support
Headings (h1-h3), paragraphs, bold, italic, inline code, bullet and numbered lists, simple tables (column widths follow content, header repeated on every page, over-long cells are truncated with ...), code blocks, horizontal rules and links (printed as text). Anything else is printed as plain text.
Limitations:
- Built-in PDF fonts only: characters outside Latin-1 (some symbols, CJK, emoji) are printed as
?. - No images or HTML.
When the Markdown comes from an AI agent, instruct it to use only the subset above.
Migration
When upgrading the live connector from the single-printer version:
- Replace the connector config: move
printerUrlintoprinters: [{ "name": "…", "printerUrl": "…" }]; keepfileDsn,requestingUserNameand the timing settings at the root. - Replace every workflow step that calls
print_markdownwithgenerate_pdf(with apath) followed byprint_filespassing the returneddsninfilepaths. - Drop
archiveDirand any action-levelprinterUrlfrom action configs.
