CLI Scripting
witan pptx exec is the main presentations interface for agents. The CLI sends the deck and script to the configured Witan API, which runs the script inside a sandboxed, headless Office.js-compatible PowerPoint runtime.
Use it when a task needs to inspect, create, edit, export, or verify a deck from code.
Command
witan pptx exec <file.pptx> [flags]
Provide exactly one code source: --code, --script, --stdin, or --expr.
witan pptx exec deck.pptx --expr 'PowerPoint.run(async context => {
const count = context.presentation.slides.getCount();
await context.sync();
return count.value;
})'
For multiline scripts, use --stdin:
witan pptx exec deck.pptx --stdin <<'JS'
return await PowerPoint.run(async context => {
const slide = context.presentation.slides.getItemAt(0);
slide.shapes.addTextBox("Updated", { left: 60, top: 60, width: 300, height: 80 });
await context.sync();
return slide.id;
});
JS
Flags
| Flag | Default | Description |
|---|---|---|
--code <ts/js> |
- | Inline TypeScript or JavaScript source. |
--script <path> |
- | Path to a TypeScript or JavaScript file. |
--stdin |
false |
Read TypeScript or JavaScript source from stdin. |
--expr <expr> |
- | Single-expression shorthand; wraps as return (<expr>);. |
--input-json <json> |
{} |
JSON value exposed as the global input. |
--input-file <key=@path> |
- | Add a local PNG/JPEG file to input as a data:image/...;base64,... URI. Repeatable. |
--locale <name> |
Environment-derived | Execution locale. Falls back to WITAN_LOCALE, then LC_ALL, LC_MESSAGES, and LANG. |
--stdin-timeout-ms <ms> |
2000 |
Maximum time to wait for EOF when reading --stdin; 0 disables. |
--timeout-ms <ms> |
0 |
Execution timeout override. 0 uses the server default. |
--max-output-chars <n> |
0 |
Stdout capture limit override. 0 uses the server default. |
--create |
false |
Create a new .pptx file instead of opening an existing file. |
--save |
false |
Write returned presentation bytes to the target path. |
--json |
false |
Use witan pptx --json exec ... to print the full response envelope. |
Runtime Model
Scripts usually start with PowerPoint.run:
return await PowerPoint.run(async context => {
const presentation = context.presentation;
const slide = presentation.slides.getItemAt(0);
const shapeCount = slide.shapes.getCount();
await context.sync();
return shapeCount.value;
});
OfficeExtension.ClientResult values, such as getCount(), should be read after await context.sync(). In Witan's headless runtime, load(...) and sync() are compatibility calls rather than network round trips: load(...) records requested properties, and sync() provides the Office.js sequencing point while resolving against the in-memory presentation model.
Type Declarations
Fetch the combined Office.js PowerPoint and Witan extension declarations when you need to confirm an exact member or enum:
witan pptx exec-types > "${TMPDIR:-/tmp}/witan-pptx-types.d.ts"
rg -n "SlideCollection|addChart|ChartSeries" "${TMPDIR:-/tmp}/witan-pptx-types.d.ts"
The command requires no authentication. Its output is a large raw .d.ts file, so save it once and search it rather than printing it repeatedly.
Object Validity After Mutation
Objects returned by collections remain live while the underlying presentation object exists. Deleting a shape, slide, group member, tag, custom property, or other model object invalidates wrappers that refer to it. Reacquire the object from its collection after structural mutations instead of continuing to use an old wrapper.
Use getItemOrNullObject(...) and other OrNullObject methods when an object may be absent, and check isNullObject. Accessing an object that existed earlier but has since been deleted raises an OfficeExtension.Error, matching Office.js-style failure behavior.
Creating Decks
Use --create with a path that does not exist:
witan pptx exec new-deck.pptx --create --save --stdin <<'JS'
return await PowerPoint.run(async context => {
const slide = context.presentation.slides.getItemAt(0);
slide.shapes.addTextBox("Created", { left: 60, top: 60, width: 300, height: 80 });
await context.sync();
return true;
});
JS
PowerPoint.createPresentation(...) is intentionally not implemented. File creation belongs to the CLI because the runtime runs headlessly inside the open presentation session.
Saving
Without --save, writes are in-memory for the request. Use --save when the modified .pptx should be written back to disk.
For uploaded presentations, updated bytes are returned only when writes are detected. For --create --save, the created file bytes are returned when execution succeeds.
Input Files
Use --input-file for image authoring:
witan pptx exec deck.pptx --save \
--input-file logo=@./logo.png \
--stdin <<'JS'
return await PowerPoint.run(async context => {
const slide = context.presentation.slides.getItemAt(0);
slide.shapes.addPicture(input.logo, {
left: 54,
top: 42,
width: 120,
height: 60
});
await context.sync();
return true;
});
JS
Chart Extensions
Witan adds chart APIs to the Office.js PowerPoint object model:
const shape = slide.shapes.addChart(PowerPoint.ChartType.waterfall, [
["Step", "Amount"],
["Start", 120],
["Growth", 45],
["Churn", -18],
["End", 147]
], { left: 72, top: 100, width: 600, height: 340 });
const chart = shape.getChart();
chart.title.text = "Revenue Bridge";
chart.series.getItemAt(0).showConnectorLines = true;
Charts are real PowerPoint chart parts with embedded workbook data. See Charts and pptx API.
Sandbox
- No filesystem access.
- No network access.
- No static or dynamic imports.
- Top-level
awaitis supported. - Use
print(...)for progress output. - The script return value is serialized as JSON.
Verification
After visual edits, render the affected slide:
witan pptx render deck.pptx --slide 3 -o slide-3.png
For regression checks:
witan pptx render deck.pptx --slide 3 -o before.png
# ... edit ...
witan pptx render deck.pptx --slide 3 --diff before.png -o diff.png
See Render for rendering behavior and limits.
Run Witan Lint after structural or chart edits to check chart data and text occlusion before visual review.