@codemation/eventbus-redis 0.0.44 → 0.1.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @codemation/eventbus-redis
2
2
 
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#281](https://github.com/MadeRelevant/codemation/pull/281) [`b0b1b5e`](https://github.com/MadeRelevant/codemation/commit/b0b1b5e945e93d43230c9e2bfe935ea59f48e10e) Thanks [@cblokland90](https://github.com/cblokland90)! - feat(relay): publish HMAC-signed run events to redis for the control-plane realtime relay ([#318](https://github.com/MadeRelevant/codemation/issues/318))
8
+
9
+ In managed mode (a paired workspace running the redis-backed event bus), the host now relays every `RunEvent` to a workspace+workflow-keyed redis channel `codemation.ws.<workspaceId>.run-events.<workflowId>` as an HMAC-signed envelope (`SignedRunEventRelayPublisher`). The signature is keyed by the workspace pairing secret so the control plane can verify authenticity and which workspace published the event. This lets run events escape the originating HTTP request so externally-triggered (loop/webhook) runs can stream live to the canvas. Part of scale-to-zero ([#261](https://github.com/MadeRelevant/codemation/issues/261)); the control-plane subscribe/verify/SSE side and the canvas subscription land separately.
10
+
11
+ ### Patch Changes
12
+
13
+ - [#247](https://github.com/MadeRelevant/codemation/pull/247) [`bfdd759`](https://github.com/MadeRelevant/codemation/commit/bfdd7590b4903676b223c2f302b9bcd0f4a4583c) Thanks [@cblokland90](https://github.com/cblokland90)! - Remove all human-written comments from TypeScript source files and add `codemation/no-comments` ESLint rule to enforce self-describing code going forward.
14
+
15
+ - Updated dependencies [[`60f23a3`](https://github.com/MadeRelevant/codemation/commit/60f23a37660cdda34e3d61acca8b2bf581a9db0e), [`a5457bb`](https://github.com/MadeRelevant/codemation/commit/a5457bb58edafec01e85cdcf6d30f9ca54521e27), [`510e0d8`](https://github.com/MadeRelevant/codemation/commit/510e0d8a5f137a4fe10f43cbf46e40d05fea4977), [`d5f49f7`](https://github.com/MadeRelevant/codemation/commit/d5f49f7f80deafc109dbc29f16ff0d571ee8591a), [`364507a`](https://github.com/MadeRelevant/codemation/commit/364507a9fba45fd66cb208d5a41ee1f3bdc68311), [`0dc8a9d`](https://github.com/MadeRelevant/codemation/commit/0dc8a9def376e4cd1821f3b92b0f887720e0c842), [`3facadf`](https://github.com/MadeRelevant/codemation/commit/3facadf3bc09d21f33e698213a521fb64168d5dd), [`c08cf33`](https://github.com/MadeRelevant/codemation/commit/c08cf33ead44fec64d3d43b9294fccfdf6674156), [`597fa8f`](https://github.com/MadeRelevant/codemation/commit/597fa8f697300d8d861a02c11e9819892f7947fa), [`b15f8c6`](https://github.com/MadeRelevant/codemation/commit/b15f8c68125e3ec3082bab8d79414871f942e409), [`bfdd759`](https://github.com/MadeRelevant/codemation/commit/bfdd7590b4903676b223c2f302b9bcd0f4a4583c), [`0a681b3`](https://github.com/MadeRelevant/codemation/commit/0a681b357afd7b15ba3925788c732fd439d8e6b0), [`ab5185a`](https://github.com/MadeRelevant/codemation/commit/ab5185a839118868eda1aab42b82f7a921037f37), [`fd188a2`](https://github.com/MadeRelevant/codemation/commit/fd188a2bac65c86dd62cf2f540034769c5bc0ac7), [`f2aa0c4`](https://github.com/MadeRelevant/codemation/commit/f2aa0c42b2f9d2e9eb7bc4f3393f74342f7ef460), [`cf2b146`](https://github.com/MadeRelevant/codemation/commit/cf2b146b452317e1ccaa1dfeaaa1a0e153181e30)]:
16
+ - @codemation/core@0.15.0
17
+
3
18
  ## 0.0.44
4
19
 
5
20
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -1,19 +1,9 @@
1
1
  import IORedis from "ioredis";
2
2
 
3
3
  //#region ../core/src/contracts/testTriggerTypes.d.ts
4
-
5
- /**
6
- * Identifier minted by the host (or in-memory test runner) for one execution of a test suite.
7
- * One TestSuiteRun produces N child workflow runs, one per item yielded by `generateItems`.
8
- */
9
4
  type TestSuiteRunId = string;
10
5
  //#endregion
11
6
  //#region ../core/src/contracts/baseTypes.d.ts
12
- /**
13
- * Minimal base types that have no dependencies on other contracts.
14
- * Used by credentialTypes, workflowTypes, and other contract layers
15
- * to avoid circular dependencies.
16
- */
17
7
  type WorkflowId = string;
18
8
  type NodeId = string;
19
9
  type OutputPortKey = string;
@@ -22,15 +12,7 @@ type PersistedTokenId = string;
22
12
  type NodeConnectionName = string;
23
13
  //#endregion
24
14
  //#region ../core/src/events/runEvents.d.ts
25
- /**
26
- * Outcome of a single test case (one workflow run dispatched by the test-suite orchestrator).
27
- * - `running`: workflow still in flight
28
- * - `succeeded`: workflow completed AND all assertions passed (or no assertions)
29
- * - `failed`: workflow failed OR (workflow completed but ≥1 assertion failed)
30
- * - `errored` / `cancelled`: workflow itself errored or was cancelled
31
- */
32
15
  type TestCaseRunStatus = "running" | "succeeded" | "failed" | "errored" | "cancelled";
33
- /** Aggregate outcome of a TestSuiteRun. */
34
16
  type TestSuiteRunStatus = "succeeded" | "failed" | "partial" | "errored" | "cancelled";
35
17
  type RunEvent = Readonly<{
36
18
  kind: "runCreated";
@@ -138,42 +120,24 @@ interface RunEventBus {
138
120
  }
139
121
  //#endregion
140
122
  //#region ../core/src/contracts/runTypes.d.ts
141
- /**
142
- * Test-suite linkage for a run. When set, this run was started by a TestSuiteOrchestrator
143
- * as one test case inside a TestSuiteRun. The `IsTestRun` node and host-side persisters key
144
- * off the presence of this field. Subworkflow runs inherit it from their parent run.
145
- */
146
123
  interface RunTestContext {
147
124
  readonly testSuiteRunId: string;
148
125
  readonly testCaseIndex: number;
149
- /**
150
- * Optional human-friendly label for this test case (e.g. an email subject when fixtures
151
- * are loaded from a mailbox). Resolved per item by `TestTrigger.caseLabel(item)` if set,
152
- * persisted on `Run.test_case_label` so the Tests-tab tree-table can show "RFQ for batch 14"
153
- * instead of "run_1777755971399_bbb86beac1396".
154
- */
155
126
  readonly testCaseLabel?: string;
156
127
  }
157
128
  interface RunExecutionOptions {
158
- /** Run-intent override: force the inline scheduler and bypass node-level offload decisions. */
159
129
  localOnly?: boolean;
160
- /** Marks runs started from webhook handling so orchestration can apply webhook-specific continuation rules. */
161
130
  webhook?: boolean;
162
131
  mode?: "manual" | "debug";
163
132
  sourceWorkflowId?: WorkflowId;
164
133
  sourceRunId?: RunId;
165
134
  derivedFromRunId?: RunId;
166
135
  isMutable?: boolean;
167
- /** Set by the engine for this run: 0 = root, 1 = first child subworkflow, … */
168
136
  subworkflowDepth?: number;
169
- /** Effective cap after engine policy merge (successful node completions per run). */
170
137
  maxNodeActivations?: number;
171
- /** Effective cap after engine policy merge (subworkflow nesting). */
172
138
  maxSubworkflowDepth?: number;
173
- /** Present iff started by a TestSuiteOrchestrator; propagates to subworkflow runs via {@link ParentExecutionRef.testContext}. */
174
139
  testContext?: RunTestContext;
175
140
  }
176
- /** Engine-owned counters persisted with the run (worker-safe). */
177
141
  interface EngineRunCounters {
178
142
  completedNodeActivations: number;
179
143
  }
@@ -195,7 +159,6 @@ interface PersistedWorkflowSnapshotNode {
195
159
  tokenName?: string;
196
160
  configTokenName?: string;
197
161
  config: unknown;
198
- /** Pre-computed static configuration summary; populated by WorkflowSnapshotCodec. */
199
162
  inspectorSummary?: ReadonlyArray<Readonly<{
200
163
  label: string;
201
164
  value: string;
@@ -206,9 +169,7 @@ interface PersistedWorkflowSnapshot {
206
169
  name: string;
207
170
  nodes: ReadonlyArray<PersistedWorkflowSnapshotNode>;
208
171
  edges: ReadonlyArray<Edge>;
209
- /** When the snapshot was built from a live workflow definition that configured a workflow error handler. */
210
172
  workflowErrorHandlerConfigured?: boolean;
211
- /** Connection metadata for child nodes not in the execution graph (e.g. AI agent attachments). */
212
173
  connections?: ReadonlyArray<WorkflowNodeConnection>;
213
174
  }
214
175
  type PinnedNodeOutputsByPort = Readonly<Record<OutputPortKey, Items>>;
@@ -256,18 +217,9 @@ interface NodeExecutionSnapshot {
256
217
  inputsByPort?: NodeInputsByPort;
257
218
  outputs?: NodeOutputs;
258
219
  error?: NodeExecutionError;
259
- /**
260
- * When the node is a SubWorkflow invocation, the run id of the child run it spawned.
261
- * Populated after the child run completes so the UI can deep-link to that specific execution.
262
- */
263
220
  childRunId?: RunId;
264
221
  }
265
- /** Stable id for a single connection invocation row in {@link ConnectionInvocationRecord}. */
266
222
  type ConnectionInvocationId = string;
267
- /**
268
- * One logical LLM or tool call under an owning workflow node (e.g. AI agent).
269
- * The owning node defines what {@link managedInput} and {@link managedOutput} contain.
270
- */
271
223
  interface ConnectionInvocationRecord {
272
224
  readonly invocationId: ConnectionInvocationId;
273
225
  readonly runId: RunId;
@@ -278,24 +230,18 @@ interface ConnectionInvocationRecord {
278
230
  readonly status: NodeExecutionStatus;
279
231
  readonly managedInput?: JsonValue;
280
232
  readonly managedOutput?: JsonValue;
281
- /** Short human-readable description of what this invocation is doing right now (e.g. `"calling search_messages"`). Rendered as a sub-line on the canvas node card. */
282
233
  readonly statusLabel?: string;
283
- /** Stable identifier for the thing this invocation acts on (e.g. an MCP tool name like `"search_messages"`). Persists across status transitions so the inspector can show it on completed/failed entries too. Connection nodes that ARE the tool (e.g. node-backed agent tools) leave this unset — the parent node id already identifies the subject. */
284
234
  readonly subjectName?: string;
285
235
  readonly error?: NodeExecutionError;
286
236
  readonly queuedAt?: string;
287
237
  readonly startedAt?: string;
288
238
  readonly finishedAt?: string;
289
239
  readonly updatedAt: string;
290
- /** Per-item iteration id minted by the engine when this invocation occurred inside a runnable node's per-item loop. */
291
240
  readonly iterationId?: NodeIterationId;
292
- /** Item index (0-based) of the iteration that produced this invocation. */
293
241
  readonly itemIndex?: number;
294
- /** When set, this invocation was produced inside a sub-agent triggered by the named parent invocation. */
295
242
  readonly parentInvocationId?: ConnectionInvocationId;
296
243
  }
297
244
  type RunStatus = "running" | "pending" | "completed" | "failed" | "suspended" | "halted";
298
- /** Reason a run transitioned to {@link RunStatus} `"halted"`. */
299
245
  type RunHaltReason = "hitl-rejected" | "hitl-timeout" | "hitl-cancelled";
300
246
  interface PendingNodeExecution {
301
247
  runId: RunId;
@@ -309,70 +255,42 @@ interface PendingNodeExecution {
309
255
  batchId?: string;
310
256
  enqueuedAt: string;
311
257
  }
312
- /** One persisted suspension entry per suspended item. */
313
258
  interface PersistedSuspensionEntry {
314
- /** Opaque task identifier (UUID v4). */
315
259
  readonly taskId: string;
316
260
  readonly nodeId: NodeId;
317
261
  readonly activationId: NodeActivationId;
318
262
  readonly itemIndex: number;
319
- /** SHA-256 hex digest of the decision schema JSON (for schema-drift detection). */
320
263
  readonly decisionSchemaHash: string;
321
- /** Serialized return value from `SuspensionRequest.deliver` (stored on the HumanTask row). */
322
264
  readonly deliveryRef: JsonValue;
323
- /** ISO timestamp when the task expires. */
324
265
  readonly timeoutAt: string;
325
266
  readonly onTimeout: "halt" | "auto-accept";
326
267
  }
327
- /**
328
- * When a node is re-activated after suspension, the engine writes the resume context here
329
- * so `NodeExecutionRequestHandlerService` can splice `resumeContext` into ctx.
330
- * Cleared once the re-activation is consumed.
331
- */
332
268
  interface PendingResumeEntry {
333
269
  readonly activationId: NodeActivationId;
334
270
  readonly nodeId: NodeId;
335
- /**
336
- * Typed as `unknown` here to avoid a circular import between runTypes ↔ runtimeTypes.
337
- * `NodeExecutionRequestHandlerService` casts this to `ResumeContext` from runtimeTypes.
338
- */
339
271
  readonly resumeContext: unknown;
340
272
  }
341
273
  interface PersistedRunState {
342
274
  runId: RunId;
343
275
  workflowId: WorkflowId;
344
276
  startedAt: string;
345
- /** Canonical terminal time for listings and retention when persisted on the run root. */
346
277
  finishedAt?: string;
347
- /** Optimistic concurrency / CAS on the run aggregate (repository may increment on save). */
348
278
  revision?: number;
349
279
  parent?: ParentExecutionRef;
350
280
  executionOptions?: RunExecutionOptions;
351
281
  control?: PersistedRunControlState;
352
282
  workflowSnapshot?: PersistedWorkflowSnapshot;
353
283
  mutableState?: PersistedMutableRunState;
354
- /** Frozen at createRun from workflow + runtime defaults for prune/storage decisions. */
355
284
  policySnapshot?: PersistedRunPolicySnapshot;
356
- /** Successful node completions so far (for activation budget). */
357
285
  engineCounters?: EngineRunCounters;
358
286
  status: RunStatus;
359
- /** Populated when `status === "halted"` to discriminate why the run was halted. */
360
287
  reason?: RunHaltReason;
361
288
  pending?: PendingNodeExecution;
362
289
  queue: RunQueueEntry[];
363
290
  outputsByNode: Record<NodeId, NodeOutputs>;
364
291
  nodeSnapshotsByNodeId: Record<NodeId, NodeExecutionSnapshot>;
365
- /** Append-only history of connection invocations (LLM/tool) nested under owning nodes. */
366
292
  connectionInvocations?: ReadonlyArray<ConnectionInvocationRecord>;
367
- /**
368
- * One entry per outstanding HITL suspension (per-item).
369
- * Present and non-empty iff `status === "suspended"`.
370
- */
371
293
  suspension?: ReadonlyArray<PersistedSuspensionEntry>;
372
- /**
373
- * Written by `resumeRun()` so `NodeExecutionRequestHandlerService` can splice `resumeContext`
374
- * into the ctx when re-executing the suspended node. Cleared once consumed.
375
- */
376
294
  pendingResume?: PendingResumeEntry;
377
295
  }
378
296
  //#endregion
@@ -394,11 +312,6 @@ interface Edge {
394
312
  input: InputPortKey;
395
313
  };
396
314
  }
397
- /**
398
- * Named connection from a parent node to child nodes that exist in {@link WorkflowDefinition.nodes}
399
- * but are not traversed by the main execution graph. Parents are commonly executable nodes, but may
400
- * also be connection-owned nodes for recursive agent attachments.
401
- */
402
315
  interface WorkflowNodeConnection {
403
316
  readonly parentNodeId: NodeId;
404
317
  readonly connectionName: NodeConnectionName;
@@ -436,30 +349,16 @@ type Items<TJson = unknown> = ReadonlyArray<Item<TJson>>;
436
349
  type NodeOutputs = Partial<Record<OutputPortKey, Items>>;
437
350
  type RunId = string;
438
351
  type NodeActivationId = string;
439
- /**
440
- * One per-item iteration of a runnable node's execute loop. Refines `NodeActivationId` for
441
- * per-item connection invocations and telemetry. Undefined when the executing node is a batch
442
- * node or trigger that does not iterate items.
443
- */
444
352
  type NodeIterationId = string;
445
353
  interface ParentExecutionRef {
446
354
  runId: RunId;
447
355
  workflowId: WorkflowId;
448
356
  nodeId: NodeId;
449
- /** Subworkflow depth of the **spawning** run (0 = root). Passed when starting a child run. */
450
357
  subworkflowDepth?: number;
451
- /** Effective max node activations from the parent run (propagated to child policy merge). */
452
358
  engineMaxNodeActivations?: number;
453
- /** Effective max subworkflow depth from the parent run (propagated to child policy merge). */
454
359
  engineMaxSubworkflowDepth?: number;
455
- /**
456
- * Test-suite linkage inherited by the child subworkflow run. Set by whichever node
457
- * spawns the subworkflow when its own `ctx.testContext` is present, so assertions
458
- * emitted inside a subworkflow land under the correct parent test case.
459
- */
460
360
  testContext?: RunTestContext;
461
361
  }
462
- /** Whether to persist run execution data after the workflow finishes. */
463
362
  type WorkflowStoragePolicyMode = "ALL" | "SUCCESS" | "ERROR" | "NEVER";
464
363
  interface PersistedRunPolicySnapshot {
465
364
  readonly retentionSeconds?: number;
@@ -486,4 +385,32 @@ declare class RedisRunEventBus implements RunEventBus {
486
385
  private parseEvent;
487
386
  }
488
387
  //#endregion
489
- export { RedisRunEventBus };
388
+ //#region src/signedRelay.types.d.ts
389
+ declare const SIGNED_RUN_EVENT_CHANNEL_PREFIX = "codemation";
390
+ interface SignedRunEventEnvelope {
391
+ readonly workspaceId: string;
392
+ readonly ts: number;
393
+ readonly sig: string;
394
+ readonly payload: string;
395
+ }
396
+ interface RunEventEnvelopeSigner {
397
+ readonly workspaceId: string;
398
+ sign(payload: string): SignedRunEventEnvelope;
399
+ }
400
+ declare function signedRunEventChannel(channelPrefix: string, workspaceId: string, workflowId: string): string;
401
+ //#endregion
402
+ //#region src/SignedRunEventRelayPublisher.d.ts
403
+ declare class SignedRunEventRelayPublisher {
404
+ private readonly redisUrl;
405
+ private readonly localBus;
406
+ private readonly signer;
407
+ private publisher;
408
+ private subscription;
409
+ constructor(redisUrl: string, localBus: RunEventBus, signer: RunEventEnvelopeSigner);
410
+ start(): Promise<void>;
411
+ stop(): Promise<void>;
412
+ private relay;
413
+ private ensurePublisher;
414
+ }
415
+ //#endregion
416
+ export { RedisRunEventBus, type RunEventEnvelopeSigner, SIGNED_RUN_EVENT_CHANNEL_PREFIX, type SignedRunEventEnvelope, SignedRunEventRelayPublisher, signedRunEventChannel };
package/dist/index.js CHANGED
@@ -61,4 +61,50 @@ var RedisRunEventBus = class {
61
61
  };
62
62
 
63
63
  //#endregion
64
- export { RedisRunEventBus };
64
+ //#region src/signedRelay.types.ts
65
+ const SIGNED_RUN_EVENT_CHANNEL_PREFIX = "codemation";
66
+ function signedRunEventChannel(channelPrefix, workspaceId, workflowId) {
67
+ return `${channelPrefix}.ws.${workspaceId}.run-events.${workflowId}`;
68
+ }
69
+
70
+ //#endregion
71
+ //#region src/SignedRunEventRelayPublisher.ts
72
+ var SignedRunEventRelayPublisher = class {
73
+ publisher;
74
+ subscription;
75
+ constructor(redisUrl, localBus, signer) {
76
+ this.redisUrl = redisUrl;
77
+ this.localBus = localBus;
78
+ this.signer = signer;
79
+ }
80
+ async start() {
81
+ if (this.subscription) return;
82
+ this.subscription = await this.localBus.subscribe((event) => {
83
+ this.relay(event);
84
+ });
85
+ }
86
+ async stop() {
87
+ if (this.subscription) {
88
+ await this.subscription.close();
89
+ this.subscription = void 0;
90
+ }
91
+ if (this.publisher) {
92
+ this.publisher.disconnect();
93
+ this.publisher = void 0;
94
+ }
95
+ }
96
+ async relay(event) {
97
+ const payload = JSON.stringify(event);
98
+ const envelope = this.signer.sign(payload);
99
+ const channel = signedRunEventChannel(SIGNED_RUN_EVENT_CHANNEL_PREFIX, this.signer.workspaceId, event.workflowId);
100
+ await this.ensurePublisher().publish(channel, JSON.stringify(envelope));
101
+ }
102
+ ensurePublisher() {
103
+ if (this.publisher) return this.publisher;
104
+ this.publisher = new IORedis(this.redisUrl);
105
+ return this.publisher;
106
+ }
107
+ };
108
+
109
+ //#endregion
110
+ export { RedisRunEventBus, SIGNED_RUN_EVENT_CHANNEL_PREFIX, SignedRunEventRelayPublisher, signedRunEventChannel };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemation/eventbus-redis",
3
- "version": "0.0.44",
3
+ "version": "0.1.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -29,7 +29,7 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "ioredis": "^5.7.0",
32
- "@codemation/core": "0.14.0"
32
+ "@codemation/core": "0.15.0"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/node": "^25.3.5",
@@ -0,0 +1,49 @@
1
+ import type { RunEvent, RunEventBus, RunEventSubscription } from "@codemation/core";
2
+
3
+ import IORedis from "ioredis";
4
+
5
+ import type { RunEventEnvelopeSigner } from "./signedRelay.types";
6
+ import { signedRunEventChannel, SIGNED_RUN_EVENT_CHANNEL_PREFIX } from "./signedRelay.types";
7
+
8
+ export class SignedRunEventRelayPublisher {
9
+ private publisher: IORedis | undefined;
10
+ private subscription: RunEventSubscription | undefined;
11
+
12
+ constructor(
13
+ private readonly redisUrl: string,
14
+ private readonly localBus: RunEventBus,
15
+ private readonly signer: RunEventEnvelopeSigner,
16
+ ) {}
17
+
18
+ async start(): Promise<void> {
19
+ if (this.subscription) return;
20
+ this.subscription = await this.localBus.subscribe((event) => {
21
+ void this.relay(event);
22
+ });
23
+ }
24
+
25
+ async stop(): Promise<void> {
26
+ if (this.subscription) {
27
+ await this.subscription.close();
28
+ this.subscription = undefined;
29
+ }
30
+ if (this.publisher) {
31
+ this.publisher.disconnect();
32
+ this.publisher = undefined;
33
+ }
34
+ }
35
+
36
+ private async relay(event: RunEvent): Promise<void> {
37
+ const payload = JSON.stringify(event);
38
+ const envelope = this.signer.sign(payload);
39
+ const channel = signedRunEventChannel(SIGNED_RUN_EVENT_CHANNEL_PREFIX, this.signer.workspaceId, event.workflowId);
40
+ await this.ensurePublisher().publish(channel, JSON.stringify(envelope));
41
+ }
42
+
43
+ private ensurePublisher(): IORedis {
44
+ if (this.publisher) return this.publisher;
45
+ // eslint-disable-next-line codemation/no-manual-di-new -- IORedis is an external connection client, not a DI-managed class (same pattern as RedisRunEventBus).
46
+ this.publisher = new IORedis(this.redisUrl);
47
+ return this.publisher;
48
+ }
49
+ }
package/src/index.ts CHANGED
@@ -1 +1,4 @@
1
1
  export { RedisRunEventBus } from "./RedisRunEventBusRegistry";
2
+ export { SignedRunEventRelayPublisher } from "./SignedRunEventRelayPublisher";
3
+ export { signedRunEventChannel, SIGNED_RUN_EVENT_CHANNEL_PREFIX } from "./signedRelay.types";
4
+ export type { RunEventEnvelopeSigner, SignedRunEventEnvelope } from "./signedRelay.types";
@@ -0,0 +1,17 @@
1
+ export const SIGNED_RUN_EVENT_CHANNEL_PREFIX = "codemation";
2
+
3
+ export interface SignedRunEventEnvelope {
4
+ readonly workspaceId: string;
5
+ readonly ts: number;
6
+ readonly sig: string;
7
+ readonly payload: string;
8
+ }
9
+
10
+ export interface RunEventEnvelopeSigner {
11
+ readonly workspaceId: string;
12
+ sign(payload: string): SignedRunEventEnvelope;
13
+ }
14
+
15
+ export function signedRunEventChannel(channelPrefix: string, workspaceId: string, workflowId: string): string {
16
+ return `${channelPrefix}.ws.${workspaceId}.run-events.${workflowId}`;
17
+ }