How to Use OfficeCLI Python and Node.js SDKs for Resident-Pipe Communication
The OfficeCLI SDKs maintain a persistent subprocess connection to format-handler plugins via JSONL streams over stdin/stdout, automatically managing the open handshake, command serialization, idle-timeout watchdogs, and graceful shutdown.
The iOfficeAI/OfficeCLI repository provides official Python and Node.js SDKs that wrap the resident-pipe protocol, enabling stateful, low-latency interaction with Office documents without spawning a new process for each operation. By abstracting the subprocess lifecycle and JSONL message framing, these libraries allow developers to send structured commands like add, get, and save while the heavy native plugin remains resident in memory.
Understanding the Resident-Pipe Architecture
The resident-pipe architecture relies on format-handler plugins that implement a JSONL request/response protocol over standard streams. When you instantiate an SDK client, it spawns the plugin as a child process—using subprocess.Popen in Python or child_process.spawn in Node.js—and maintains that connection for the duration of the session.
Key protocol elements include:
- Open Handshake: The client sends
{"msg_type":"open",...}to establish capabilities and vocabulary, and the plugin replies with supported features. - Message Framing: All communication uses one JSON object per line, UTF-8 encoded, without BOM, ensuring deterministic request/response ordering.
- Idle-Timeout Watchdog: A watchdog monitors activity and terminates inactive plugins; long operations must emit
{"heartbeat":true}onstderrto stay alive.
These mechanisms are defined in the [plugins/plugin-protocol.md](https://github.com/iOfficeAI/OfficeCLI/blob/main/plugins/plugin-protocol.md) specification.
Python SDK Integration with ResidentPipeClient
The Python implementation resides in [sdk/python/officecli.py](https://github.com/iOfficeAI/OfficeCLI/blob/main/sdk/python/officecli.py), exposing the ResidentPipeClient class to manage the persistent subprocess.
Initializing the Client and Opening a Document
Instantiate ResidentPipeClient with the plugin_cmd array and optional idle_timeout override (in seconds). The constructor automatically performs the open handshake and stores the plugin's capabilities.
from officecli import ResidentPipeClient
# Spawn the format-handler and perform the open handshake
client = ResidentPipeClient(
plugin_cmd=["officecli-format-handler", "open", "report.docx"],
idle_timeout=120, # Overrides the manifest idle_timeout_seconds
)
Sending Commands and Managing Responses
Use the command() method to send operations. It accepts command, args, and optional props parameters, serializes them to JSONL, and returns the parsed response. For long-running tasks, call send_heartbeat() to prevent the watchdog from terminating the plugin.
# Add a paragraph to the document body
resp = client.command(
command="add",
args={"parent": "/body", "type": "paragraph"},
props={"text": "Automated content from Python"},
)
print(f"Added node: {resp}")
# Retrieve an existing node
node = client.command(
command="get",
args={"path": "/body/paragraph[1]"},
)
# Persist changes and close the session
client.command(command="save", args={})
client.command(command="close", args={}) # Terminates the plugin process
Errors raise PluginError with code and message attributes corresponding to protocol error responses such as invalid_argument or plugin_idle_timeout.
Node.js SDK Integration with ResidentPipe
The Node.js equivalent is implemented in [sdk/node/index.js](https://github.com/iOfficeAI/OfficeCLI/blob/main/sdk/node/index.js) and exports the ResidentPipe class.
Creating a Persistent Pipe Connection
Configure the pipe with the cmd array and idleTimeout option. The constructor queues commands internally to guarantee strict request-response ordering as required by the protocol.
const { ResidentPipe } = require("@officecli/sdk");
const pipe = new ResidentPipe({
cmd: ["officecli-format-handler", "open", "presentation.pptx"],
idleTimeout: 180, // Seconds; relaxes the watchdog for heavy exports
});
Executing Commands and Lifecycle Management
The command() method returns a Promise that resolves with the JSON response. Always send save before close to ensure changes are flushed to disk.
// Add a slide to the presentation
await pipe.command({
command: "add",
args: { parent: "/slides", type: "slide" },
props: { title: "Q4 Results" },
});
// Query the first slide
const slide = await pipe.command({
command: "get",
args: { path: "/slides/slide[1]" },
});
// Save and terminate
await pipe.command({ command: "save", args: {} });
await pipe.command({ command: "close", args: {} });
Call sendHeartbeat() during intensive operations to emit the required heartbeat on stderr and reset the idle timer.
Protocol Implementation Details
Open Handshake and Capabilities
As implemented in the SDKs, the open handshake exchanges capability metadata. You can inspect the returned vocabulary to determine which commands and properties the specific plugin supports for the opened file format.
Idle-Timeout and Heartbeat Mechanics
Both SDKs respect the idle_timeout_seconds field from the plugin manifest, which can be overridden via constructor options. The watchdog resets when:
- Any byte is received on
stdout(a response), or - A line containing
{"heartbeat":true}is received onstderr
The SDKs provide send_heartbeat() (Python) and sendHeartbeat() (Node) to emit these heartbeats manually during long computations.
Error Handling
When a plugin returns an error response, the SDKs throw PluginError (Python) or reject with a PluginError instance (Node). These objects expose:
code: Protocol error string (e.g.,node_not_found,save_failed)message: Human-readable description
Handle these by restarting the client if the plugin process terminates unexpectedly.
Summary
- Resident-pipe architecture maintains a persistent
format-handlerplugin process, eliminating startup overhead for multiple operations. - Python SDK: Use
ResidentPipeClientin [sdk/python/officecli.py](https://github.com/iOfficeAI/OfficeCLI/blob/main/sdk/python/officecli.py) withcommand(),send_heartbeat(), andPluginErrorhandling. - Node.js SDK: Use
ResidentPipein [sdk/node/index.js](https://github.com/iOfficeAI/OfficeCLI/blob/main/sdk/node/index.js) with asynccommand()and configurableidleTimeout. - Protocol compliance: All communication uses JSONL framing over stdin/stdout with an initial open handshake and mandatory heartbeat management.
- Lifecycle requirements: Always send
savebeforecloseto persist changes; catchPluginErrorto handle timeouts and invalid arguments.
Frequently Asked Questions
What distinguishes a format-handler plugin from other plugin kinds?
A format-handler is a long-lived plugin kind that maintains stateful document models in memory, enabling rapid incremental updates via the resident-pipe protocol. Other kinds, such as exporters or importers, are typically short-lived and execute a single task before exiting, whereas format-handlers persist until explicitly closed.
How do I prevent the plugin from timing out during long exports?
Set the idle_timeout (Python) or idleTimeout (Node) constructor option to a value exceeding your longest expected operation, or manually invoke send_heartbeat() / sendHeartbeat() periodically. This writes {"heartbeat":true} to the plugin's stderr, resetting the watchdog timer.
Can I use the resident-pipe SDKs for read-only document inspection?
Yes. Open the document in read-only mode (if the plugin supports it) or simply avoid sending save commands. The command() method supports read operations like get without modifying the underlying file, though you must still send close to terminate the plugin process cleanly.
What error codes does PluginError expose, and how should I handle them?
PluginError surfaces protocol-level codes such as invalid_argument, node_not_found, plugin_idle_timeout, and save_failed. Handle plugin_idle_timeout by recreating the client, handle invalid_argument by validating inputs against the capabilities returned during the open handshake, and treat save_failed as a critical error requiring user intervention or disk space checks.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →