@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.
Files changed (81) hide show
  1. package/LICENSE +176 -0
  2. package/README.md +287 -0
  3. package/dist/adapters/blob-substrate.d.ts +49 -0
  4. package/dist/adapters/blob-substrate.js +140 -0
  5. package/dist/adapters/repo-store.d.ts +39 -0
  6. package/dist/adapters/repo-store.js +344 -0
  7. package/dist/adapters/spawn-child.d.ts +74 -0
  8. package/dist/adapters/spawn-child.js +152 -0
  9. package/dist/adapters/step-invoker.d.ts +114 -0
  10. package/dist/adapters/step-invoker.js +360 -0
  11. package/dist/child/env-bootstrap.d.ts +56 -0
  12. package/dist/child/env-bootstrap.js +120 -0
  13. package/dist/child/from-process-env.d.ts +127 -0
  14. package/dist/child/from-process-env.js +183 -0
  15. package/dist/child/index.d.ts +9 -0
  16. package/dist/child/index.js +9 -0
  17. package/dist/child/outbound-mail-bridge.d.ts +36 -0
  18. package/dist/child/outbound-mail-bridge.js +143 -0
  19. package/dist/child/proxy-repo-store.d.ts +27 -0
  20. package/dist/child/proxy-repo-store.js +200 -0
  21. package/dist/child/run-child.d.ts +320 -0
  22. package/dist/child/run-child.js +900 -0
  23. package/dist/child/self-discovery.d.ts +29 -0
  24. package/dist/child/self-discovery.js +57 -0
  25. package/dist/child/substrate-write-bridge.d.ts +72 -0
  26. package/dist/child/substrate-write-bridge.js +188 -0
  27. package/dist/child/supervisor-backed-transport.d.ts +10 -0
  28. package/dist/child/supervisor-backed-transport.js +113 -0
  29. package/dist/child/warm-agent-cache.d.ts +78 -0
  30. package/dist/child/warm-agent-cache.js +112 -0
  31. package/dist/drain-controller.d.ts +37 -0
  32. package/dist/drain-controller.js +46 -0
  33. package/dist/index.d.ts +10 -0
  34. package/dist/index.js +10 -0
  35. package/dist/ipc/control-channel.d.ts +336 -0
  36. package/dist/ipc/control-channel.js +532 -0
  37. package/dist/ipc/crypto.d.ts +46 -0
  38. package/dist/ipc/crypto.js +126 -0
  39. package/dist/ipc/envelope.d.ts +53 -0
  40. package/dist/ipc/envelope.js +88 -0
  41. package/dist/ipc/event-channel.d.ts +677 -0
  42. package/dist/ipc/event-channel.js +278 -0
  43. package/dist/ipc/index.d.ts +4 -0
  44. package/dist/ipc/index.js +143 -0
  45. package/dist/mail-bus/hub-transport-adapter.d.ts +30 -0
  46. package/dist/mail-bus/hub-transport-adapter.js +76 -0
  47. package/dist/mail-bus/index.d.ts +1 -0
  48. package/dist/mail-bus/index.js +1 -0
  49. package/dist/seams/index.d.ts +3 -0
  50. package/dist/seams/index.js +3 -0
  51. package/dist/seams/scheduler-adapter.d.ts +3 -0
  52. package/dist/seams/scheduler-adapter.js +24 -0
  53. package/dist/seams/scheduler.d.ts +94 -0
  54. package/dist/seams/scheduler.js +397 -0
  55. package/dist/seams/signal-channel.d.ts +74 -0
  56. package/dist/seams/signal-channel.js +304 -0
  57. package/dist/supervisor/cancel-signing.d.ts +68 -0
  58. package/dist/supervisor/cancel-signing.js +144 -0
  59. package/dist/supervisor/child-termination.d.ts +51 -0
  60. package/dist/supervisor/child-termination.js +76 -0
  61. package/dist/supervisor/credentials.d.ts +101 -0
  62. package/dist/supervisor/credentials.js +153 -0
  63. package/dist/supervisor/dispatch-attribution.d.ts +37 -0
  64. package/dist/supervisor/dispatch-attribution.js +114 -0
  65. package/dist/supervisor/drain-timeout.d.ts +127 -0
  66. package/dist/supervisor/drain-timeout.js +231 -0
  67. package/dist/supervisor/index.d.ts +7 -0
  68. package/dist/supervisor/index.js +6 -0
  69. package/dist/supervisor/recycle.d.ts +212 -0
  70. package/dist/supervisor/recycle.js +440 -0
  71. package/dist/supervisor/run-event-compaction.d.ts +34 -0
  72. package/dist/supervisor/run-event-compaction.js +115 -0
  73. package/dist/supervisor/spawn-env.d.ts +39 -0
  74. package/dist/supervisor/spawn-env.js +36 -0
  75. package/dist/supervisor/supervisor.d.ts +202 -0
  76. package/dist/supervisor/supervisor.js +2244 -0
  77. package/dist/supervisor/terminal-broadcaster.d.ts +45 -0
  78. package/dist/supervisor/terminal-broadcaster.js +184 -0
  79. package/dist/supervisor/types.d.ts +542 -0
  80. package/dist/supervisor/types.js +10 -0
  81. 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
+ }