@intx/workflow-host 0.2.2
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 +176 -0
- package/README.md +287 -0
- package/dist/adapters/blob-substrate.d.ts +49 -0
- package/dist/adapters/blob-substrate.js +140 -0
- package/dist/adapters/repo-store.d.ts +39 -0
- package/dist/adapters/repo-store.js +344 -0
- package/dist/adapters/spawn-child.d.ts +74 -0
- package/dist/adapters/spawn-child.js +152 -0
- package/dist/adapters/step-invoker.d.ts +114 -0
- package/dist/adapters/step-invoker.js +360 -0
- package/dist/child/env-bootstrap.d.ts +56 -0
- package/dist/child/env-bootstrap.js +120 -0
- package/dist/child/from-process-env.d.ts +127 -0
- package/dist/child/from-process-env.js +183 -0
- package/dist/child/index.d.ts +9 -0
- package/dist/child/index.js +9 -0
- package/dist/child/outbound-mail-bridge.d.ts +36 -0
- package/dist/child/outbound-mail-bridge.js +143 -0
- package/dist/child/proxy-repo-store.d.ts +27 -0
- package/dist/child/proxy-repo-store.js +200 -0
- package/dist/child/run-child.d.ts +320 -0
- package/dist/child/run-child.js +900 -0
- package/dist/child/self-discovery.d.ts +29 -0
- package/dist/child/self-discovery.js +57 -0
- package/dist/child/substrate-write-bridge.d.ts +72 -0
- package/dist/child/substrate-write-bridge.js +188 -0
- package/dist/child/supervisor-backed-transport.d.ts +10 -0
- package/dist/child/supervisor-backed-transport.js +113 -0
- package/dist/child/warm-agent-cache.d.ts +78 -0
- package/dist/child/warm-agent-cache.js +112 -0
- package/dist/drain-controller.d.ts +37 -0
- package/dist/drain-controller.js +46 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/ipc/control-channel.d.ts +336 -0
- package/dist/ipc/control-channel.js +532 -0
- package/dist/ipc/crypto.d.ts +46 -0
- package/dist/ipc/crypto.js +126 -0
- package/dist/ipc/envelope.d.ts +53 -0
- package/dist/ipc/envelope.js +88 -0
- package/dist/ipc/event-channel.d.ts +677 -0
- package/dist/ipc/event-channel.js +278 -0
- package/dist/ipc/index.d.ts +4 -0
- package/dist/ipc/index.js +143 -0
- package/dist/mail-bus/hub-transport-adapter.d.ts +30 -0
- package/dist/mail-bus/hub-transport-adapter.js +76 -0
- package/dist/mail-bus/index.d.ts +1 -0
- package/dist/mail-bus/index.js +1 -0
- package/dist/seams/index.d.ts +3 -0
- package/dist/seams/index.js +3 -0
- package/dist/seams/scheduler-adapter.d.ts +3 -0
- package/dist/seams/scheduler-adapter.js +24 -0
- package/dist/seams/scheduler.d.ts +94 -0
- package/dist/seams/scheduler.js +397 -0
- package/dist/seams/signal-channel.d.ts +74 -0
- package/dist/seams/signal-channel.js +304 -0
- package/dist/supervisor/cancel-signing.d.ts +68 -0
- package/dist/supervisor/cancel-signing.js +144 -0
- package/dist/supervisor/child-termination.d.ts +51 -0
- package/dist/supervisor/child-termination.js +76 -0
- package/dist/supervisor/credentials.d.ts +101 -0
- package/dist/supervisor/credentials.js +153 -0
- package/dist/supervisor/dispatch-attribution.d.ts +37 -0
- package/dist/supervisor/dispatch-attribution.js +114 -0
- package/dist/supervisor/drain-timeout.d.ts +127 -0
- package/dist/supervisor/drain-timeout.js +231 -0
- package/dist/supervisor/index.d.ts +7 -0
- package/dist/supervisor/index.js +6 -0
- package/dist/supervisor/recycle.d.ts +212 -0
- package/dist/supervisor/recycle.js +440 -0
- package/dist/supervisor/run-event-compaction.d.ts +34 -0
- package/dist/supervisor/run-event-compaction.js +115 -0
- package/dist/supervisor/spawn-env.d.ts +39 -0
- package/dist/supervisor/spawn-env.js +36 -0
- package/dist/supervisor/supervisor.d.ts +202 -0
- package/dist/supervisor/supervisor.js +2244 -0
- package/dist/supervisor/terminal-broadcaster.d.ts +45 -0
- package/dist/supervisor/terminal-broadcaster.js +184 -0
- package/dist/supervisor/types.d.ts +542 -0
- package/dist/supervisor/types.js +10 -0
- package/package.json +35 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { TerminalEventSource, TerminalRunEvent } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Per-cohort terminal-run broadcaster. Lifetime matches the supervisor
|
|
4
|
+
* spawn cohort's `terminalCohortAbort`; `dispose()` is invoked when the
|
|
5
|
+
* cohort is torn down (shutdown or recycle's `installNewChild`).
|
|
6
|
+
*/
|
|
7
|
+
export interface TerminalBroadcaster {
|
|
8
|
+
/**
|
|
9
|
+
* Fan a terminal-run event out to every active subscriber listening
|
|
10
|
+
* for the supplied `runId`. The broadcaster buffers one event per
|
|
11
|
+
* subscriber if the consumer's first `next()` has not yet been
|
|
12
|
+
* awaited so a notification that lands between `subscribe` and the
|
|
13
|
+
* first `next()` is still delivered. Notifications for runIds with
|
|
14
|
+
* no listeners are dropped (the dispatch loop and the drain
|
|
15
|
+
* accumulator both arm before the corresponding trigger.fire /
|
|
16
|
+
* drain mail is forwarded to the child).
|
|
17
|
+
*/
|
|
18
|
+
notify(runId: string, event: TerminalRunEvent): void;
|
|
19
|
+
/**
|
|
20
|
+
* `TerminalEventSource`-shaped accessor for consumers. Each call
|
|
21
|
+
* yields an `AsyncIterable<TerminalRunEvent>` scoped to one `runId`.
|
|
22
|
+
* The iterator yields the first terminal event the broadcaster fans
|
|
23
|
+
* out for that runId (or `done: true` if the broadcaster is disposed
|
|
24
|
+
* first) and ends.
|
|
25
|
+
*/
|
|
26
|
+
readonly source: TerminalEventSource;
|
|
27
|
+
/**
|
|
28
|
+
* Finalise every minted iterator with `done: true` and drop every
|
|
29
|
+
* listener. Called on cohort teardown (shutdown / recycle).
|
|
30
|
+
*/
|
|
31
|
+
dispose(): void;
|
|
32
|
+
/**
|
|
33
|
+
* Whether `dispose()` has been called. Post-dispose, `source(...)`
|
|
34
|
+
* still returns an iterable whose first `next()` immediately yields
|
|
35
|
+
* `{done: true}` -- callers do not need to guard against the cohort
|
|
36
|
+
* being torn down before they subscribe.
|
|
37
|
+
*/
|
|
38
|
+
readonly disposed: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Construct a fresh per-cohort terminal-run broadcaster. The fan-out is
|
|
42
|
+
* single-shot per listener: a notification settles every matching
|
|
43
|
+
* listener exactly once, dropping the listener afterwards.
|
|
44
|
+
*/
|
|
45
|
+
export declare function createTerminalBroadcaster(): TerminalBroadcaster;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// Per-cohort terminal-run event broadcaster.
|
|
2
|
+
//
|
|
3
|
+
// The supervisor's workflow-process child commits each run's terminal
|
|
4
|
+
// event (`RunCompleted`, `RunFailed`, `RunCancelled`) through its own
|
|
5
|
+
// substrate handle and mirrors the event over the control IPC as a
|
|
6
|
+
// `terminal.event` upstream frame. The supervisor's `pumpUpstreamControl`
|
|
7
|
+
// calls `notify()` on this broadcaster when such a frame lands; the
|
|
8
|
+
// dispatch loop and any armed drainTimeout accumulator subscribe via
|
|
9
|
+
// `source(runId)` to await the matching terminal event without
|
|
10
|
+
// re-reading the workflow-run substrate from the supervisor's address
|
|
11
|
+
// space.
|
|
12
|
+
//
|
|
13
|
+
// One broadcaster instance per spawn cohort. The supervisor mints a
|
|
14
|
+
// fresh broadcaster inside `spawn()` and inside the recycle path's
|
|
15
|
+
// `installNewChild`; the previous cohort's broadcaster is disposed,
|
|
16
|
+
// which finalises every minted iterator with `done: true` (the
|
|
17
|
+
// disposed-side iterator settles its pending `next()` resolver
|
|
18
|
+
// directly so the consumer wakes up even if its own cohort-abort race
|
|
19
|
+
// has not landed yet).
|
|
20
|
+
import { getLogger } from "@intx/log";
|
|
21
|
+
const logger = getLogger([
|
|
22
|
+
"workflow-host",
|
|
23
|
+
"supervisor",
|
|
24
|
+
"terminal-broadcaster",
|
|
25
|
+
]);
|
|
26
|
+
/**
|
|
27
|
+
* Construct a fresh per-cohort terminal-run broadcaster. The fan-out is
|
|
28
|
+
* single-shot per listener: a notification settles every matching
|
|
29
|
+
* listener exactly once, dropping the listener afterwards.
|
|
30
|
+
*/
|
|
31
|
+
export function createTerminalBroadcaster() {
|
|
32
|
+
const listenersByRunId = new Map();
|
|
33
|
+
let disposed = false;
|
|
34
|
+
function addListener(runId, listener) {
|
|
35
|
+
let set = listenersByRunId.get(runId);
|
|
36
|
+
if (set === undefined) {
|
|
37
|
+
set = new Set();
|
|
38
|
+
listenersByRunId.set(runId, set);
|
|
39
|
+
}
|
|
40
|
+
set.add(listener);
|
|
41
|
+
return () => {
|
|
42
|
+
const current = listenersByRunId.get(runId);
|
|
43
|
+
if (current === undefined)
|
|
44
|
+
return;
|
|
45
|
+
current.delete(listener);
|
|
46
|
+
if (current.size === 0)
|
|
47
|
+
listenersByRunId.delete(runId);
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const source = (runId) => ({
|
|
51
|
+
[Symbol.asyncIterator]() {
|
|
52
|
+
let resolved = false;
|
|
53
|
+
let unsubscribe = null;
|
|
54
|
+
let pending = null;
|
|
55
|
+
let resolveNext = null;
|
|
56
|
+
function deliverEvent(event) {
|
|
57
|
+
if (resolved)
|
|
58
|
+
return;
|
|
59
|
+
if (resolveNext !== null) {
|
|
60
|
+
const resolver = resolveNext;
|
|
61
|
+
resolveNext = null;
|
|
62
|
+
resolved = true;
|
|
63
|
+
if (unsubscribe !== null) {
|
|
64
|
+
unsubscribe();
|
|
65
|
+
unsubscribe = null;
|
|
66
|
+
}
|
|
67
|
+
resolver({ value: event, done: false });
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
pending = event;
|
|
71
|
+
}
|
|
72
|
+
function deliverDispose() {
|
|
73
|
+
if (resolved)
|
|
74
|
+
return;
|
|
75
|
+
if (resolveNext !== null) {
|
|
76
|
+
const resolver = resolveNext;
|
|
77
|
+
resolveNext = null;
|
|
78
|
+
resolved = true;
|
|
79
|
+
if (unsubscribe !== null) {
|
|
80
|
+
unsubscribe();
|
|
81
|
+
unsubscribe = null;
|
|
82
|
+
}
|
|
83
|
+
resolver({ value: undefined, done: true });
|
|
84
|
+
}
|
|
85
|
+
// No pending resolver: leave `resolved` false and rely on
|
|
86
|
+
// the next `next()` call to observe `disposed === true` and
|
|
87
|
+
// return `{done: true}`.
|
|
88
|
+
}
|
|
89
|
+
if (disposed) {
|
|
90
|
+
resolved = true;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
unsubscribe = addListener(runId, {
|
|
94
|
+
onEvent: deliverEvent,
|
|
95
|
+
onDispose: deliverDispose,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
next() {
|
|
100
|
+
if (resolved) {
|
|
101
|
+
return Promise.resolve({ value: undefined, done: true });
|
|
102
|
+
}
|
|
103
|
+
if (pending !== null) {
|
|
104
|
+
const event = pending;
|
|
105
|
+
pending = null;
|
|
106
|
+
resolved = true;
|
|
107
|
+
if (unsubscribe !== null) {
|
|
108
|
+
unsubscribe();
|
|
109
|
+
unsubscribe = null;
|
|
110
|
+
}
|
|
111
|
+
return Promise.resolve({ value: event, done: false });
|
|
112
|
+
}
|
|
113
|
+
if (disposed) {
|
|
114
|
+
resolved = true;
|
|
115
|
+
if (unsubscribe !== null) {
|
|
116
|
+
unsubscribe();
|
|
117
|
+
unsubscribe = null;
|
|
118
|
+
}
|
|
119
|
+
return Promise.resolve({ value: undefined, done: true });
|
|
120
|
+
}
|
|
121
|
+
return new Promise((resolve) => {
|
|
122
|
+
resolveNext = resolve;
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
return() {
|
|
126
|
+
resolved = true;
|
|
127
|
+
if (unsubscribe !== null) {
|
|
128
|
+
unsubscribe();
|
|
129
|
+
unsubscribe = null;
|
|
130
|
+
}
|
|
131
|
+
if (resolveNext !== null) {
|
|
132
|
+
const resolver = resolveNext;
|
|
133
|
+
resolveNext = null;
|
|
134
|
+
resolver({ value: undefined, done: true });
|
|
135
|
+
}
|
|
136
|
+
return Promise.resolve({ value: undefined, done: true });
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
return {
|
|
142
|
+
notify(runId, event) {
|
|
143
|
+
if (disposed)
|
|
144
|
+
return;
|
|
145
|
+
const set = listenersByRunId.get(runId);
|
|
146
|
+
if (set === undefined)
|
|
147
|
+
return;
|
|
148
|
+
const snapshot = [...set];
|
|
149
|
+
for (const listener of snapshot) {
|
|
150
|
+
listener.onEvent(event);
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
source,
|
|
154
|
+
dispose() {
|
|
155
|
+
if (disposed)
|
|
156
|
+
return;
|
|
157
|
+
disposed = true;
|
|
158
|
+
const snapshot = [];
|
|
159
|
+
for (const set of listenersByRunId.values()) {
|
|
160
|
+
for (const listener of set)
|
|
161
|
+
snapshot.push(listener);
|
|
162
|
+
}
|
|
163
|
+
listenersByRunId.clear();
|
|
164
|
+
for (const listener of snapshot) {
|
|
165
|
+
// Guard each `onDispose` so one throwing listener does not skip
|
|
166
|
+
// the rest: dispose must finalise every minted iterator or the
|
|
167
|
+
// supervisor's teardown can leak a consumer awaiting a `next()`
|
|
168
|
+
// that never settles. A throw here is a listener bug, so surface
|
|
169
|
+
// it at error and continue -- this is what lets the shutdown path
|
|
170
|
+
// treat `dispose()` as total.
|
|
171
|
+
try {
|
|
172
|
+
listener.onDispose();
|
|
173
|
+
}
|
|
174
|
+
catch (cause) {
|
|
175
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
176
|
+
logger.error `terminal broadcaster listener onDispose threw: ${message}`;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
get disposed() {
|
|
181
|
+
return disposed;
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
}
|