@tbrandenburg/node-red-agents 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +34 -0
- package/nodes/agent/agent.html +550 -0
- package/nodes/agent/agent.js +396 -0
- package/nodes/agent/icons/agent.svg +27 -0
- package/nodes/agent/lib/agents/base.js +42 -0
- package/nodes/agent/lib/agents/opencode.js +141 -0
- package/nodes/agent/lib/agents/pi.js +220 -0
- package/nodes/agent/lib/execution/lifecycle.js +69 -0
- package/nodes/agent/lib/execution/scheduler.js +97 -0
- package/nodes/agent/lib/execution/status.js +24 -0
- package/nodes/agent/lib/mcp/normalize.js +31 -0
- package/nodes/agent/lib/runtimes/base.js +21 -0
- package/nodes/agent/lib/runtimes/direct.js +28 -0
- package/nodes/agent/lib/runtimes/process-exec.js +105 -0
- package/nodes/agent/lib/runtimes/srt.js +63 -0
- package/nodes/agent-server/agent-server.html +365 -0
- package/nodes/agent-server/agent-server.js +481 -0
- package/nodes/agent-server/icons/agent.svg +27 -0
- package/nodes/agent-server/lib/daemon.js +149 -0
- package/nodes/agent-server/lib/http.js +60 -0
- package/nodes/agent-server/lib/model.js +20 -0
- package/nodes/agent-server/lib/port.js +31 -0
- package/nodes/agent-server/lib/registry.js +77 -0
- package/nodes/agent-server/lib/status.js +15 -0
- package/nodes/gh/README.md +75 -0
- package/nodes/gh/examples/list-pull-requests.json +48 -0
- package/nodes/gh/examples/run-workflow.json +42 -0
- package/nodes/gh/gh.html +146 -0
- package/nodes/gh/gh.js +237 -0
- package/nodes/gh/icons/gh.svg +15 -0
- package/nodes/gh/lib/parse-args.js +67 -0
- package/package.json +60 -0
- package/shared/srt-settings.js +71 -0
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
(function () {
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
// Much simpler than the `agent` node's mcpServerRow: one plain text
|
|
6
|
+
// input per row, no per-row type switch. Used for the SRT "Allowed
|
|
7
|
+
// domains" and "Allowed write directories" lists.
|
|
8
|
+
function simpleStringRow(container, value) {
|
|
9
|
+
const input = $('<input/>', { type: 'text', style: 'width:100%' }).val(value || '').appendTo(container);
|
|
10
|
+
container.data('getValue', function () {
|
|
11
|
+
return input.val().trim();
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
RED.nodes.registerType('agent-server', {
|
|
16
|
+
category: 'Agent',
|
|
17
|
+
color: '#8B5CF6',
|
|
18
|
+
defaults: {
|
|
19
|
+
name: { value: '' },
|
|
20
|
+
|
|
21
|
+
operation: { value: 'message' },
|
|
22
|
+
|
|
23
|
+
hostname: { value: '127.0.0.1' },
|
|
24
|
+
opencodeBinary: { value: '' },
|
|
25
|
+
|
|
26
|
+
sessionIdProp: { value: 'sessionID' },
|
|
27
|
+
sessionIdPropType: { value: 'msg' },
|
|
28
|
+
|
|
29
|
+
promptProp: { value: 'payload' },
|
|
30
|
+
promptPropType: { value: 'msg' },
|
|
31
|
+
|
|
32
|
+
model: { value: '' },
|
|
33
|
+
modelType: { value: 'str' },
|
|
34
|
+
|
|
35
|
+
startupTimeoutMs: { value: 15000, validate: RED.validators.number() },
|
|
36
|
+
requestTimeoutMs: { value: 120000, validate: RED.validators.number() },
|
|
37
|
+
maxInstances: { value: 5, validate: RED.validators.number() },
|
|
38
|
+
|
|
39
|
+
authUsername: { value: '' },
|
|
40
|
+
authPassword: { value: '' },
|
|
41
|
+
|
|
42
|
+
runtime: { value: 'direct' },
|
|
43
|
+
srtBinary: { value: '' },
|
|
44
|
+
srtSettingsMode: { value: 'file' },
|
|
45
|
+
srtSettingsPath: { value: '' },
|
|
46
|
+
srtAllowedDomains: { value: [] },
|
|
47
|
+
srtAllowedWriteDirs: { value: ['.', '/tmp', '~/.local/share/opencode'] },
|
|
48
|
+
srtStrictAllowlist: { value: true },
|
|
49
|
+
srtAdvancedJson: { value: '' }
|
|
50
|
+
},
|
|
51
|
+
inputs: 1,
|
|
52
|
+
outputs: 2,
|
|
53
|
+
outputLabels: ['result', 'lifecycle events'],
|
|
54
|
+
icon: 'agent.svg',
|
|
55
|
+
paletteLabel: 'agent-server',
|
|
56
|
+
label: function () {
|
|
57
|
+
return this.name || `agent-server (${this.operation})`;
|
|
58
|
+
},
|
|
59
|
+
oneditprepare: function () {
|
|
60
|
+
const node = this;
|
|
61
|
+
|
|
62
|
+
$('#node-input-sessionIdProp').typedInput({
|
|
63
|
+
typeField: '#node-input-sessionIdPropType',
|
|
64
|
+
types: ['msg', 'str', 'flow', 'global']
|
|
65
|
+
});
|
|
66
|
+
$('#node-input-promptProp').typedInput({
|
|
67
|
+
typeField: '#node-input-promptPropType',
|
|
68
|
+
types: ['msg', 'str', 'flow', 'global', 'env']
|
|
69
|
+
});
|
|
70
|
+
$('#node-input-model').typedInput({
|
|
71
|
+
typeField: '#node-input-modelType',
|
|
72
|
+
types: ['str', 'msg', 'flow', 'global', 'env']
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
function updateOperationRows() {
|
|
76
|
+
const op = $('#node-input-operation').val();
|
|
77
|
+
$('.agent-server-row-prompt').toggle(op === 'message');
|
|
78
|
+
$('.agent-server-row-spawn').toggle(op === 'message');
|
|
79
|
+
}
|
|
80
|
+
$('#node-input-operation').on('change', updateOperationRows);
|
|
81
|
+
|
|
82
|
+
function updateRuntimeRows() {
|
|
83
|
+
$('.agent-server-row-srt').toggle($('#node-input-runtime').val() === 'srt');
|
|
84
|
+
}
|
|
85
|
+
$('#node-input-runtime').on('change', updateRuntimeRows);
|
|
86
|
+
|
|
87
|
+
function updateSrtModeRows() {
|
|
88
|
+
const mode = $('#node-input-srtSettingsMode').val();
|
|
89
|
+
$('.agent-server-row-srt-file').toggle(mode === 'file');
|
|
90
|
+
$('.agent-server-row-srt-inline').toggle(mode === 'inline');
|
|
91
|
+
}
|
|
92
|
+
$('#node-input-srtSettingsMode').on('change', updateSrtModeRows);
|
|
93
|
+
|
|
94
|
+
const domainsList = $('#node-input-srtAllowedDomains-list');
|
|
95
|
+
domainsList.editableList({
|
|
96
|
+
addButton: 'Add domain',
|
|
97
|
+
height: 100,
|
|
98
|
+
addItem: function (container, index, value) {
|
|
99
|
+
simpleStringRow(container, value);
|
|
100
|
+
},
|
|
101
|
+
removable: true,
|
|
102
|
+
sortable: true
|
|
103
|
+
});
|
|
104
|
+
(node.srtAllowedDomains || []).forEach((entry) => domainsList.editableList('addItem', entry));
|
|
105
|
+
|
|
106
|
+
const writeDirsList = $('#node-input-srtAllowedWriteDirs-list');
|
|
107
|
+
writeDirsList.editableList({
|
|
108
|
+
addButton: 'Add directory',
|
|
109
|
+
height: 100,
|
|
110
|
+
addItem: function (container, index, value) {
|
|
111
|
+
simpleStringRow(container, value);
|
|
112
|
+
},
|
|
113
|
+
removable: true,
|
|
114
|
+
sortable: true
|
|
115
|
+
});
|
|
116
|
+
(node.srtAllowedWriteDirs || []).forEach((entry) => writeDirsList.editableList('addItem', entry));
|
|
117
|
+
|
|
118
|
+
updateOperationRows();
|
|
119
|
+
updateRuntimeRows();
|
|
120
|
+
updateSrtModeRows();
|
|
121
|
+
},
|
|
122
|
+
oneditsave: function () {
|
|
123
|
+
function collectStrings(listEl) {
|
|
124
|
+
const values = [];
|
|
125
|
+
listEl.editableList('items').each(function () {
|
|
126
|
+
const v = $(this).data('getValue')();
|
|
127
|
+
if (v) values.push(v);
|
|
128
|
+
});
|
|
129
|
+
return values;
|
|
130
|
+
}
|
|
131
|
+
this.srtAllowedDomains = collectStrings($('#node-input-srtAllowedDomains-list'));
|
|
132
|
+
this.srtAllowedWriteDirs = collectStrings($('#node-input-srtAllowedWriteDirs-list'));
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}());
|
|
136
|
+
</script>
|
|
137
|
+
|
|
138
|
+
<script type="text/html" data-template-name="agent-server">
|
|
139
|
+
<div class="form-row">
|
|
140
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
141
|
+
<input type="text" id="node-input-name" placeholder="Agent Server">
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
<div class="form-row">
|
|
145
|
+
<label for="node-input-operation"><i class="fa fa-play"></i> Operation</label>
|
|
146
|
+
<select id="node-input-operation" style="width:70%">
|
|
147
|
+
<option value="message">Send message</option>
|
|
148
|
+
<option value="status">Status (this node's tracked sessions)</option>
|
|
149
|
+
<option value="abort">Abort session</option>
|
|
150
|
+
<option value="history">Session history</option>
|
|
151
|
+
<option value="terminate">Terminate session (kill daemon)</option>
|
|
152
|
+
</select>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<div class="form-row">
|
|
156
|
+
<label for="node-input-sessionIdProp"><i class="fa fa-key"></i> Session ID</label>
|
|
157
|
+
<input type="text" id="node-input-sessionIdProp" style="width:60%">
|
|
158
|
+
<input type="hidden" id="node-input-sessionIdPropType">
|
|
159
|
+
</div>
|
|
160
|
+
<div class="form-tips">
|
|
161
|
+
Empty/absent on a "Send message" trigger → spawns a <b>new</b> daemon + session and
|
|
162
|
+
returns its id (as <code>msg.sessionID</code>) for reuse. A <b>present</b> id must belong to
|
|
163
|
+
a session this node instance already spawned -- any other (foreign/unknown) id is always an
|
|
164
|
+
error, never a fallback spawn or resume.
|
|
165
|
+
</div>
|
|
166
|
+
|
|
167
|
+
<div class="form-row agent-server-row-prompt">
|
|
168
|
+
<label for="node-input-promptProp"><i class="fa fa-comment"></i> Prompt</label>
|
|
169
|
+
<input type="text" id="node-input-promptProp" style="width:60%">
|
|
170
|
+
<input type="hidden" id="node-input-promptPropType">
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<div class="form-row agent-server-row-prompt">
|
|
174
|
+
<label for="node-input-model"><i class="fa fa-microchip"></i> Model</label>
|
|
175
|
+
<input type="text" id="node-input-model" placeholder="provider/model, e.g. github-copilot/claude-sonnet-4.6" style="width:60%">
|
|
176
|
+
<input type="hidden" id="node-input-modelType">
|
|
177
|
+
</div>
|
|
178
|
+
<div class="form-tips agent-server-row-prompt">
|
|
179
|
+
Leave blank to use opencode's own configured default. Only applies per-message
|
|
180
|
+
(the HTTP API takes <code>{providerID, modelID}</code>, not opencode's CLI
|
|
181
|
+
<code>provider/model</code> shorthand -- this field is split on the first
|
|
182
|
+
<code>/</code> for you).
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<div class="form-row agent-server-row-spawn">
|
|
186
|
+
<label for="node-input-hostname"><i class="fa fa-server"></i> Hostname</label>
|
|
187
|
+
<input type="text" id="node-input-hostname" placeholder="127.0.0.1" style="width:60%">
|
|
188
|
+
</div>
|
|
189
|
+
<div class="form-row agent-server-row-spawn">
|
|
190
|
+
<label for="node-input-opencodeBinary"><i class="fa fa-terminal"></i> opencode binary</label>
|
|
191
|
+
<input type="text" id="node-input-opencodeBinary" placeholder="opencode" style="width:60%">
|
|
192
|
+
</div>
|
|
193
|
+
<div class="form-row agent-server-row-spawn">
|
|
194
|
+
<label for="node-input-maxInstances"><i class="fa fa-tasks"></i> Max instances</label>
|
|
195
|
+
<input type="text" id="node-input-maxInstances" placeholder="5" style="width:80px">
|
|
196
|
+
<i class="fa fa-info-circle" style="color:#888; cursor:help; margin-left:4px;"
|
|
197
|
+
title="Maximum number of daemons this node may have spawned at once. A new no-session-id trigger errors immediately once reached (no queueing). 0 = unlimited."></i>
|
|
198
|
+
</div>
|
|
199
|
+
<div class="form-row agent-server-row-spawn">
|
|
200
|
+
<label for="node-input-startupTimeoutMs"><i class="fa fa-clock-o"></i> Startup timeout</label>
|
|
201
|
+
<input type="text" id="node-input-startupTimeoutMs" placeholder="15000" style="width:100px"> ms
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
<div class="form-row">
|
|
205
|
+
<label for="node-input-requestTimeoutMs"><i class="fa fa-clock-o"></i> Request timeout</label>
|
|
206
|
+
<input type="text" id="node-input-requestTimeoutMs" placeholder="120000" style="width:100px"> ms
|
|
207
|
+
</div>
|
|
208
|
+
|
|
209
|
+
<div class="form-row">
|
|
210
|
+
<label for="node-input-runtime"><i class="fa fa-cogs"></i> Runtime</label>
|
|
211
|
+
<select id="node-input-runtime" style="width:70%">
|
|
212
|
+
<option value="direct">Direct</option>
|
|
213
|
+
<option value="srt">SRT (sandboxed -- currently non-functional, see below)</option>
|
|
214
|
+
</select>
|
|
215
|
+
</div>
|
|
216
|
+
<div class="form-row agent-server-row-srt" style="background:#fff3cd; padding:6px; border-radius:4px;">
|
|
217
|
+
<i class="fa fa-warning" style="color:#b8860b;"></i>
|
|
218
|
+
<b>SRT only sandboxes outbound network egress</b> -- it has no concept of
|
|
219
|
+
exposing an inbound listening port. A daemon spawned under <code>srt</code> will
|
|
220
|
+
print "listening" but genuinely never become reachable from this node (verified
|
|
221
|
+
empirically, not opencode-specific -- any inbound server behaves the same way
|
|
222
|
+
under srt). Startup will always time out. Use <b>Direct</b> runtime instead;
|
|
223
|
+
SRT sandboxing works fine for the one-shot <b>agent</b> node (outbound-only
|
|
224
|
+
<code>opencode run</code>), just not here.
|
|
225
|
+
</div>
|
|
226
|
+
<div class="form-row agent-server-row-srt">
|
|
227
|
+
<label for="node-input-srtSettingsMode"><i class="fa fa-shield"></i> SRT settings</label>
|
|
228
|
+
<select id="node-input-srtSettingsMode" style="width:60%">
|
|
229
|
+
<option value="file">File path</option>
|
|
230
|
+
<option value="inline">Inline</option>
|
|
231
|
+
</select>
|
|
232
|
+
</div>
|
|
233
|
+
<div class="form-row agent-server-row-srt agent-server-row-srt-file">
|
|
234
|
+
<label for="node-input-srtSettingsPath"> </label>
|
|
235
|
+
<input type="text" id="node-input-srtSettingsPath" placeholder="~/.srt-settings.json (srt's own default)" style="width:60%">
|
|
236
|
+
</div>
|
|
237
|
+
|
|
238
|
+
<div class="form-row agent-server-row-srt agent-server-row-srt-inline">
|
|
239
|
+
<label>
|
|
240
|
+
<i class="fa fa-globe"></i> Allowed domains
|
|
241
|
+
<i class="fa fa-info-circle" style="color:#888; cursor:help; margin-left:4px;"
|
|
242
|
+
title="e.g. api.githubcopilot.com, opencode.ai -- the domain your configured model's API lives on."></i>
|
|
243
|
+
</label>
|
|
244
|
+
<ol id="node-input-srtAllowedDomains-list"></ol>
|
|
245
|
+
</div>
|
|
246
|
+
|
|
247
|
+
<div class="form-row agent-server-row-srt agent-server-row-srt-inline">
|
|
248
|
+
<label><i class="fa fa-folder"></i> Allowed write dirs</label>
|
|
249
|
+
<ol id="node-input-srtAllowedWriteDirs-list"></ol>
|
|
250
|
+
</div>
|
|
251
|
+
|
|
252
|
+
<div class="form-row agent-server-row-srt agent-server-row-srt-inline">
|
|
253
|
+
<input type="checkbox" id="node-input-srtStrictAllowlist" style="display:inline-block; width:auto; vertical-align:top;">
|
|
254
|
+
<label for="node-input-srtStrictAllowlist" style="width:auto;">Strict allowlist</label>
|
|
255
|
+
</div>
|
|
256
|
+
|
|
257
|
+
<div class="form-row agent-server-row-srt agent-server-row-srt-inline">
|
|
258
|
+
<details>
|
|
259
|
+
<summary style="cursor:pointer;">Advanced (raw JSON)</summary>
|
|
260
|
+
<textarea id="node-input-srtAdvancedJson" rows="4" style="width:100%; font-family:monospace;"
|
|
261
|
+
placeholder='If set, replaces the domains/directories above entirely.'></textarea>
|
|
262
|
+
</details>
|
|
263
|
+
</div>
|
|
264
|
+
|
|
265
|
+
<div class="form-row agent-server-row-srt">
|
|
266
|
+
<label for="node-input-srtBinary"><i class="fa fa-terminal"></i> SRT binary</label>
|
|
267
|
+
<input type="text" id="node-input-srtBinary" placeholder="srt" style="width:60%">
|
|
268
|
+
</div>
|
|
269
|
+
|
|
270
|
+
<div class="form-row">
|
|
271
|
+
<details>
|
|
272
|
+
<summary style="cursor:pointer;"><i class="fa fa-lock"></i> Auth (optional)</summary>
|
|
273
|
+
<div class="form-row">
|
|
274
|
+
<label for="node-input-authUsername">Username</label>
|
|
275
|
+
<input type="text" id="node-input-authUsername" placeholder="opencode" style="width:60%">
|
|
276
|
+
</div>
|
|
277
|
+
<div class="form-row">
|
|
278
|
+
<label for="node-input-authPassword">Password</label>
|
|
279
|
+
<input type="password" id="node-input-authPassword" style="width:60%">
|
|
280
|
+
</div>
|
|
281
|
+
</details>
|
|
282
|
+
</div>
|
|
283
|
+
</script>
|
|
284
|
+
|
|
285
|
+
<script type="text/html" data-help-name="agent-server">
|
|
286
|
+
<p>Manages long-lived <code>opencode serve</code> daemons, one per session,
|
|
287
|
+
as a companion to the <b>agent</b> node's one-shot execution model.</p>
|
|
288
|
+
<h3>Known issues</h3>
|
|
289
|
+
<p><b>SRT runtime is currently non-functional here</b> (unlike the <b>agent</b>
|
|
290
|
+
node): srt only sandboxes <em>outbound</em> egress, not inbound connections,
|
|
291
|
+
so a daemon spawned under srt can never be reached back by this node -- use
|
|
292
|
+
<b>Direct</b> runtime instead. Verified empirically 2026-08-13 (a trivial
|
|
293
|
+
plain Node http server has the same problem, not opencode-specific).</p>
|
|
294
|
+
<p>On <b>Linux</b> this is structural, not a config gap: srt always runs the
|
|
295
|
+
sandboxed process in its own network namespace (<code>bwrap --unshare-net</code>),
|
|
296
|
+
and <code>network.allowLocalBinding</code> -- srt's one settings-file knob that
|
|
297
|
+
sounds related -- has no Linux implementation at all. Checked the installed
|
|
298
|
+
<code>@anthropic-ai/sandbox-runtime</code> source directly: the option is only
|
|
299
|
+
read by the macOS Seatbelt profile generator; the Linux code path never
|
|
300
|
+
receives it. Setting it in a settings file is a silent no-op here.</p>
|
|
301
|
+
<p>On <b>macOS</b>, <code>allowLocalBinding: true</code> might actually work,
|
|
302
|
+
since Seatbelt is a syscall policy on the same host network stack (no
|
|
303
|
+
separate namespace to cross) -- untested on real hardware. It's a broad
|
|
304
|
+
grant (<code>network-inbound "*:*"</code>, not scoped to one port), so treat
|
|
305
|
+
it as "open all loopback inbound for this process" if it's ever wired up
|
|
306
|
+
here, not a scalpel.</p>
|
|
307
|
+
<p>An FD-inheritance / socket-activation workaround (bind the port outside the
|
|
308
|
+
sandbox, hand the sandboxed process the already-listening socket) doesn't
|
|
309
|
+
work with the current tooling either: srt's own CLI spawns the sandboxed
|
|
310
|
+
command with a plain <code>stdio: 'inherit'</code> (fds 0-2 only, no extra-fd
|
|
311
|
+
forwarding), and <code>opencode serve</code> has no <code>--fd</code>/socket-
|
|
312
|
+
activation flag to accept a pre-bound socket even if one arrived.</p>
|
|
313
|
+
<h3>Operations</h3>
|
|
314
|
+
<ul>
|
|
315
|
+
<li><b>Send message</b>: no Session ID on the trigger → spawns a new daemon +
|
|
316
|
+
session; a tracked Session ID → reuses that same daemon. An unknown/foreign
|
|
317
|
+
Session ID is always an error.</li>
|
|
318
|
+
<li><b>Status</b>: no Session ID → local aggregate (count of tracked daemons,
|
|
319
|
+
busy/idle breakdown) for this node instance, no network calls. With a Session ID
|
|
320
|
+
→ that session's own local status.</li>
|
|
321
|
+
<li><b>Abort</b> / <b>Session history</b>: require a tracked Session ID.</li>
|
|
322
|
+
<li><b>Terminate</b>: kills the daemon process for a tracked Session ID
|
|
323
|
+
(SIGTERM, escalating to SIGKILL after a grace period) and stops
|
|
324
|
+
tracking it -- unlike <b>Abort</b>, the daemon does not survive this
|
|
325
|
+
and the Session ID becomes unknown/foreign afterwards. Use this to
|
|
326
|
+
actually free resources held by a long-running or stuck session
|
|
327
|
+
instead of just cancelling its current message.</li>
|
|
328
|
+
</ul>
|
|
329
|
+
<p><b>Model</b> (only relevant to Send message) is split into the
|
|
330
|
+
<code>{providerID, modelID}</code> shape the HTTP API requires -- e.g.
|
|
331
|
+
<code>github-copilot/claude-sonnet-4.6</code>. Leave blank to use whatever
|
|
332
|
+
opencode is configured to default to.</p>
|
|
333
|
+
<p><b>Each node instance tracks its own daemons privately</b> -- there is no
|
|
334
|
+
shared/global registry across node instances (same scoping as the <b>agent</b>
|
|
335
|
+
node's running/queued counts). This means <code>Status</code>/<code>Abort</code>/
|
|
336
|
+
<code>Session history</code> are only useful when checking <em>this same node's own</em>
|
|
337
|
+
spawned sessions. Set <code>msg.operation</code> (<code>"message"</code> /
|
|
338
|
+
<code>"status"</code> / <code>"abort"</code> / <code>"history"</code> /
|
|
339
|
+
<code>"terminate"</code>) to override the
|
|
340
|
+
configured Operation for a single trigger -- e.g. to ask a message-sending node
|
|
341
|
+
instance for its own current status, without needing a second node pointed at
|
|
342
|
+
nothing.</p>
|
|
343
|
+
<h3>Outputs</h3>
|
|
344
|
+
<ol class="node-ports">
|
|
345
|
+
<li>Result
|
|
346
|
+
<dl class="message-properties">
|
|
347
|
+
<dt>payload</dt>
|
|
348
|
+
<dd>Operation-dependent: assistant reply text (message), a status object, etc.</dd>
|
|
349
|
+
<dt>sessionID <span class="property-type">string</span></dt>
|
|
350
|
+
<dd>Echoed back so a later trigger can reuse the same daemon/session.</dd>
|
|
351
|
+
</dl>
|
|
352
|
+
</li>
|
|
353
|
+
<li>Lifecycle events
|
|
354
|
+
<dl class="message-properties">
|
|
355
|
+
<dt>payload.type</dt>
|
|
356
|
+
<dd><code>spawned</code> / <code>running</code> / <code>completed</code> /
|
|
357
|
+
<code>failed</code> / <code>closed</code>, each with <code>sessionID</code>,
|
|
358
|
+
<code>timestamp</code>, and this node instance's live <code>active</code>
|
|
359
|
+
(busy) / <code>tracked</code> (total) daemon counts.</dd>
|
|
360
|
+
</dl>
|
|
361
|
+
</li>
|
|
362
|
+
</ol>
|
|
363
|
+
<p>On redeploy/removal, every daemon this node instance is still tracking is killed
|
|
364
|
+
(SIGTERM, escalating to SIGKILL).</p>
|
|
365
|
+
</script>
|