Setting up

Desktop apps

On this page
  1. Use the plugin, never outside scripting
  2. The endpoints
  3. 1. Find the app
  4. 2. Send a command
  5. 3. Read the result
  6. Command kinds
  7. Code in each app
  8. The person's approval
  9. One example per app

The NOLGIA plugin for a desktop app connects the app on your computer to NOLGIA. Once it is signed in and Connected is on, you, NOLGIA Agent or any agent holding your token can send it commands: read what is open, run code in the app's own scripting language, render a preview, import a file from your NOLGIA library, export, save, open, and for some apps run the DIT or transcribe clips. The plugin runs each command on your computer and posts the result back.

Use the plugin, never outside scripting #

Send every command through the NOLGIA plugin. Never drive the app from outside it: no AppleScript or osascript, no COM, no Windows UI automation, no simulated keystrokes or clicks, no second copy of Blender run from a terminal on the person's file, and no DaVinciResolveScript from a terminal.

The plugin is what keeps the person in control and their work safe. It lists every command in its Activity list, waits for their approval when Ask before running code is on, runs code inside one undo step where the app has one, holds back the app's own dialogs where the app allows it so nothing waits for a click, catches every script error and hands it back as text with the failing line. A command sent from outside skips all of that, and a bad call that way can crash the app and lose unsaved work: in testing, one malformed AppleScript command sent straight to After Effects crashed it.

The endpoints #

Method Path What it does
GET /desktop-apps List the desktop apps NOLGIA has a plugin for, with each plugin's status and download. Public, identical for every caller.
GET /desktop-apps/{app}/rules Read the rules every agent follows when it works in one desktop app. Public, identical for every caller.
GET /bridge/sessions List your desktop apps connected to NOLGIA right now.
POST /bridge/sessions Register a desktop app plugin, or check in again (heartbeat).
DELETE /bridge/sessions/{id} Disconnect a desktop app session.
GET /bridge/sessions/{id}/next Long poll for the next command for this app (plugin side).
GET /bridge/commands List the commands most recently sent to your desktop apps, newest first.
POST /bridge/commands Send a command to one of your connected desktop apps.
GET /bridge/commands/{id} Read a command, optionally waiting for it to finish.
POST /bridge/commands/{id}/result Report the outcome of a command (plugin side).
POST /bridge/commands/{id}/cancel Cancel a queued or running command.

POST /bridge/sessions, GET /bridge/sessions/{id}/next and POST /bridge/commands/{id}/result are what the plugin itself calls; DELETE /bridge/sessions/{id} is the plugin switching off. You use the others.

1. Find the app #

GET /bridge/sessions lists the apps connected right now, most recently seen first. A session is live while its plugin has checked in within the last 60 seconds.

shell
curl -s https://api.nolgia.ai/v1/bridge/sessions -H "Authorization: Bearer $NOLGIA_TOKEN"
# or, with the nolgia CLI:
nolgia api GET /bridge/sessions
Field Type Required Description
id string Yes
app BridgeApp Yes
app_version string Yes
plugin_version string Yes
machine_name string Yes The computer's name, as the plugin reports it.
os BridgeOS No
document BridgeDocument Yes
capabilities array of string Yes Command kinds the plugin runs, as it listed them. info and run are implied, listed or not.
allow_agent boolean Yes False when the person switched off "Allow NOLGIA Agent" in the plugin; the NOLGIA Agent's commands are then refused.
last_seen_at string Yes The plugin's latest check in. The session is live while this is within 60 seconds.

An empty list means no app is connected: install the plugin, sign in and turn on Connected. capabilities lists the command kinds that plugin runs; info and run are always there.

2. Send a command #

POST /bridge/commands queues a command for one app. Without session_id it goes to the app's most recently seen live session; pass session_id when two windows of the same app are connected.

Field Type Required Description
app BridgeApp Yes
session_id string No A live session of app to send it to. Default is the most recently seen live one.
kind string Yes info, run, or one of the session's capabilities.
args object No The command's arguments, at most 256 KB as JSON, each kind its own (https://docs.nolgia.ai/guides/desktop-apps.html). Default {}.
timeout_seconds integer No Seconds until the command expires if the app has not finished it.
shell
curl -s https://api.nolgia.ai/v1/bridge/commands \
  -H "Authorization: Bearer $NOLGIA_TOKEN" -H "Content-Type: application/json" \
  -d '{"app": "blender", "kind": "run", "args": {"language": "python", "code": "import bpy\nresult = [o.name for o in bpy.data.objects]"}}'
# or: nolgia api POST /bridge/commands --body @command.json

The answer is the command, 201 and queued. Refusals carry a code:

Status code Meaning
409 app_not_connected No live session of app, or the named session is not live.
422 language_not_supported A run in a language the app does not run.
422 capability_not_supported The plugin does not run this kind: update it.
403 agent_not_allowed NOLGIA Agent sent it and Allow NOLGIA Agent is off.
429 More than 120 commands in a minute.

args is at most 256 KB as JSON. timeout_seconds (5 to 900, default 120) sets expires_at: give a command that waits for the person's approval, a long render or a DIT offload more time.

3. Read the result #

GET /bridge/commands/{id}?wait=25 holds the request until the command finishes or 25 seconds pass, then answers with the command as it is. Ask again until status is no longer queued or running.

shell
while :; do
  cmd=$(nolgia api GET /bridge/commands/$ID --query wait=25)
  status=$(printf '%s' "$cmd" | python3 -c 'import json,sys; print(json.load(sys.stdin)["status"])')
  case "$status" in queued|running) ;; *) printf '%s\n' "$cmd"; break ;; esac
done
status Meaning
queued Waiting for the plugin to pick it up, normally within moments.
running The app is running it, or the plugin is waiting for the person's approval.
succeeded Done: result holds what the plugin returned.
failed It did not work, or the person clicked Deny: error says which, and for run the line that failed.
expired expires_at passed first, or the app disconnected.
cancelled You cancelled it.

POST /bridge/commands/{id}/cancel cancels a queued or running command. The app may already have made changes; cancelling does not undo them. GET /bridge/commands lists recent commands (never their args, because a run carries code).

Command kinds #

kind args Apps
info none All. What is open: the document or project, its comps, sequences, layers, scenes or timelines, the selection. Start every piece of work with it.
run code (required), language (optional, the app's own), timeout_seconds All. Code in the app; see below.
preview width (16 to 1920); Blender camera, frame, engine; After Effects comp and frame; Premiere Pro sequence and frame; Illustrator artboard; Photoshop document, region; DaVinci Resolve timeline, frame All. A picture of the document, saved to your NOLGIA library.
import_asset asset_id (a NOLGIA library asset), as (Blender plane, texture or clip; After Effects footage or layer; Premiere Pro clip; Illustrator embed or link; Photoshop layer, pixels or document); color_preset (a NOLGIA color preset slug, installed as a LUT: DaVinci Resolve, Premiere Pro, After Effects); DaVinci Resolve also asset_ids, project_id, bin, append, apply_to, node All.
export format: Blender blend, png, mp4, glb; After Effects mp4, png, aep; Premiere Pro mp4, png, prproj; Illustrator png, svg, pdf, ai; Photoshop png, jpg, psd; DaVinci Resolve png, mp4. Also frames (24 or 1-120) and filename All. Pictures and video go to your NOLGIA library; project-file formats make a safety copy next to the file.
save path (optional; the open file by default). DaVinci Resolve takes none: it saves the open project. All.
open path; DaVinci Resolve project and folder All.
dit action (start, status, cancel), subcommand (offload, verify, manifest, inventory, rename, undo, report), sources (one path per camera card), destinations, shoot, dry_run, dir, apply and the rest of nolgia dit After Effects, Premiere Pro, Illustrator, DaVinci Resolve. Runs the NOLGIA CLI on the person's computer.
transcribe action, clips, sequence, engine, allow_credits, language, words Premiere Pro.

A plugin checks args before it runs anything and answers a wrong one with an error that names the right choices. GET /desktop-apps/{app}/rules serves the rules every NOLGIA workflow in that app follows (safety copies, keeping work editable, previews, the scripting traps), and they apply to your own commands just as well.

Code in each app #

run carries code in the app's own language. Set result to hand data back; the plugin returns {value, stdout, stderr}, and on failure the error with the line that failed.

App language What the code sees
Blender python bpy and the open file. Each run is one undo step.
DaVinci Resolve Studio python resolve, project_manager, project, media_pool, timeline (None when no timeline is open) and fusion. Resolve objects in result come back as their names.
After Effects, Premiere Pro, Illustrator extendscript The app's ExtendScript DOM. It is ES3: no let, const, arrow functions or JSON. result, or the last expression, is returned. In After Effects each run is one undo step.
Photoshop uxp The body of an async function, already inside core.executeAsModal: app, doc (the active document), core, action, batchPlay, play (batchPlay that throws on a failed step), constants, imaging, fs, modal, sleep. Each run is one History step, undone for you when it fails.

Code cannot be stopped from outside once it runs: after timeout_seconds the plugin stops waiting and says so, and a loop that never ends keeps the app busy until it does. Keep each run to one small step.

The person's approval #

Some commands wait in the plugin for the person to click before they run. While they wait the command is running.

A Deny ends the command as failed with an error such as "The person at After Effects clicked Deny, so this did not run." No answer before expires_at ends it as failed with "Nobody approved this in After Effects in time, so it did not run." Tell the person to look at the NOLGIA panel before you send a command that waits, give it a long timeout_seconds, and never send it again on your own after a Deny.

One example per app #

Each body below goes to POST /bridge/commands.

Blender, the objects in the scene:

JSON
{"app": "blender", "kind": "run", "args": {"code": "import bpy\nresult = [(o.name, o.type) for o in bpy.context.scene.objects]"}}

After Effects, the comp in the viewer:

JSON
{"app": "after_effects", "kind": "run", "args": {"code": "var c = app.project.activeItem;\nresult = (c && c instanceof CompItem) ? {name: c.name, layers: c.numLayers, fps: c.frameRate} : null;"}}

Premiere Pro, the sequences in the project:

JSON
{"app": "premiere", "kind": "run", "args": {"code": "var out = [];\nfor (var i = 0; i < app.project.sequences.numSequences; i++) out.push(app.project.sequences[i].name);\nresult = out;"}}

Illustrator, the open document and its artboards:

JSON
{"app": "illustrator", "kind": "run", "args": {"code": "result = app.documents.length ? {name: app.activeDocument.name, artboards: app.activeDocument.artboards.length} : null;"}}

Photoshop, the layers of the active document:

JSON
{"app": "photoshop", "kind": "run", "args": {"code": "result = doc ? doc.layers.map(l => ({ id: l.id, name: l.name, visible: l.visible })) : null;"}}

DaVinci Resolve Studio, the project and its current timeline:

JSON
{"app": "resolve", "kind": "run", "args": {"code": "result = {'project': project.GetName(), 'timeline': timeline.GetName() if timeline else None}"}}

A preview of the Blender scene at frame 1:

JSON
{"app": "blender", "kind": "preview", "args": {"frame": 1, "width": 768}}

A DIT plan in Premiere Pro, one path per card, nothing copied:

JSON
{"app": "premiere", "kind": "dit", "args": {"action": "start", "subcommand": "offload", "sources": ["/Volumes/A001", "/Volumes/B001"], "destinations": ["/Volumes/RAID/Shoots", "/Volumes/Shuttle"], "shoot": "TEST", "dry_run": true}, "timeout_seconds": 300}

The offload itself is the same body without dry_run: it waits for Copy and verify in the panel and hands back a job_id to follow with "action": "status".