@donkeylabs/server 0.5.0 → 0.6.3

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.
@@ -0,0 +1,264 @@
1
+ #!/bin/bash
2
+ #
3
+ # Donkeylabs External Job Shell Wrapper
4
+ #
5
+ # This script provides functions for shell scripts to communicate
6
+ # with the Donkeylabs job system via Unix sockets or TCP.
7
+ #
8
+ # Usage:
9
+ # #!/bin/bash
10
+ # source /path/to/donkeylabs-job.sh
11
+ #
12
+ # # Initialize the job (reads from stdin)
13
+ # job_init
14
+ #
15
+ # # Report progress
16
+ # job_progress 50 "Halfway done"
17
+ #
18
+ # # Log messages
19
+ # job_log info "Processing data..."
20
+ #
21
+ # # Complete the job
22
+ # job_complete '{"result": "success"}'
23
+ #
24
+ # # Or fail the job
25
+ # job_fail "Something went wrong"
26
+ #
27
+
28
+ # Global variables (set by job_init)
29
+ DONKEYLABS_JOB_ID=""
30
+ DONKEYLABS_JOB_NAME=""
31
+ DONKEYLABS_JOB_DATA=""
32
+ DONKEYLABS_SOCKET_PATH=""
33
+ DONKEYLABS_HEARTBEAT_PID=""
34
+
35
+ # Get current timestamp in milliseconds
36
+ _job_timestamp() {
37
+ # Try to use date with milliseconds, fall back to seconds * 1000
38
+ if date '+%s%3N' >/dev/null 2>&1; then
39
+ date '+%s%3N'
40
+ else
41
+ echo "$(($(date '+%s') * 1000))"
42
+ fi
43
+ }
44
+
45
+ # Send a message to the socket
46
+ _job_send() {
47
+ local message="$1"
48
+
49
+ if [[ "$DONKEYLABS_SOCKET_PATH" == tcp://* ]]; then
50
+ # TCP connection
51
+ local addr="${DONKEYLABS_SOCKET_PATH#tcp://}"
52
+ local host="${addr%:*}"
53
+ local port="${addr##*:}"
54
+
55
+ # Use bash's /dev/tcp or nc
56
+ if [[ -e /dev/tcp ]]; then
57
+ echo "$message" > /dev/tcp/"$host"/"$port" 2>/dev/null
58
+ else
59
+ echo "$message" | nc -q0 "$host" "$port" 2>/dev/null || \
60
+ echo "$message" | nc -w0 "$host" "$port" 2>/dev/null
61
+ fi
62
+ else
63
+ # Unix socket
64
+ if command -v socat >/dev/null 2>&1; then
65
+ echo "$message" | socat - UNIX-CONNECT:"$DONKEYLABS_SOCKET_PATH" 2>/dev/null
66
+ elif command -v nc >/dev/null 2>&1; then
67
+ echo "$message" | nc -U "$DONKEYLABS_SOCKET_PATH" 2>/dev/null
68
+ else
69
+ echo "Error: Neither socat nor nc (netcat) found. Cannot send messages." >&2
70
+ return 1
71
+ fi
72
+ fi
73
+ }
74
+
75
+ # Build a JSON message
76
+ _job_build_message() {
77
+ local type="$1"
78
+ local extra="$2"
79
+
80
+ local timestamp
81
+ timestamp=$(_job_timestamp)
82
+
83
+ local message="{\"type\":\"$type\",\"jobId\":\"$DONKEYLABS_JOB_ID\",\"timestamp\":$timestamp"
84
+
85
+ if [[ -n "$extra" ]]; then
86
+ message="$message,$extra"
87
+ fi
88
+
89
+ message="$message}"
90
+
91
+ echo "$message"
92
+ }
93
+
94
+ # Start the heartbeat background process
95
+ _job_start_heartbeat() {
96
+ local interval="${1:-5}"
97
+
98
+ (
99
+ while true; do
100
+ sleep "$interval"
101
+ _job_send "$(_job_build_message "heartbeat")"
102
+ done
103
+ ) &
104
+
105
+ DONKEYLABS_HEARTBEAT_PID=$!
106
+ }
107
+
108
+ # Stop the heartbeat background process
109
+ _job_stop_heartbeat() {
110
+ if [[ -n "$DONKEYLABS_HEARTBEAT_PID" ]]; then
111
+ kill "$DONKEYLABS_HEARTBEAT_PID" 2>/dev/null
112
+ wait "$DONKEYLABS_HEARTBEAT_PID" 2>/dev/null
113
+ DONKEYLABS_HEARTBEAT_PID=""
114
+ fi
115
+ }
116
+
117
+ # Initialize the job by reading payload from stdin
118
+ job_init() {
119
+ local heartbeat_interval="${1:-5}"
120
+
121
+ # Read payload from stdin
122
+ local payload
123
+ read -r payload
124
+
125
+ if [[ -z "$payload" ]]; then
126
+ echo "Error: No payload received on stdin" >&2
127
+ exit 1
128
+ fi
129
+
130
+ # Parse JSON payload using jq if available, otherwise use basic grep/sed
131
+ if command -v jq >/dev/null 2>&1; then
132
+ DONKEYLABS_JOB_ID=$(echo "$payload" | jq -r '.jobId // empty')
133
+ DONKEYLABS_JOB_NAME=$(echo "$payload" | jq -r '.name // empty')
134
+ DONKEYLABS_JOB_DATA=$(echo "$payload" | jq -c '.data // {}')
135
+ DONKEYLABS_SOCKET_PATH=$(echo "$payload" | jq -r '.socketPath // empty')
136
+ else
137
+ # Basic parsing (less robust)
138
+ DONKEYLABS_JOB_ID=$(echo "$payload" | grep -o '"jobId":"[^"]*"' | cut -d'"' -f4)
139
+ DONKEYLABS_JOB_NAME=$(echo "$payload" | grep -o '"name":"[^"]*"' | cut -d'"' -f4)
140
+ DONKEYLABS_SOCKET_PATH=$(echo "$payload" | grep -o '"socketPath":"[^"]*"' | cut -d'"' -f4)
141
+ DONKEYLABS_JOB_DATA="{}"
142
+ fi
143
+
144
+ # Fall back to environment variables
145
+ DONKEYLABS_JOB_ID="${DONKEYLABS_JOB_ID:-$DONKEYLABS_JOB_ID}"
146
+ DONKEYLABS_SOCKET_PATH="${DONKEYLABS_SOCKET_PATH:-$DONKEYLABS_SOCKET_PATH}"
147
+
148
+ # If TCP port is set but not socket path, construct TCP URL
149
+ if [[ -z "$DONKEYLABS_SOCKET_PATH" && -n "$DONKEYLABS_TCP_PORT" ]]; then
150
+ DONKEYLABS_SOCKET_PATH="tcp://127.0.0.1:$DONKEYLABS_TCP_PORT"
151
+ fi
152
+
153
+ if [[ -z "$DONKEYLABS_JOB_ID" || -z "$DONKEYLABS_SOCKET_PATH" ]]; then
154
+ echo "Error: Missing jobId or socketPath" >&2
155
+ exit 1
156
+ fi
157
+
158
+ # Start heartbeat in background
159
+ _job_start_heartbeat "$heartbeat_interval"
160
+
161
+ # Send started message
162
+ _job_send "$(_job_build_message "started")"
163
+
164
+ # Set up cleanup trap
165
+ trap '_job_stop_heartbeat' EXIT
166
+ }
167
+
168
+ # Report progress
169
+ # Usage: job_progress <percent> [message]
170
+ job_progress() {
171
+ local percent="$1"
172
+ local message="${2:-}"
173
+
174
+ local extra="\"percent\":$percent"
175
+
176
+ if [[ -n "$message" ]]; then
177
+ # Escape message for JSON
178
+ message="${message//\\/\\\\}"
179
+ message="${message//\"/\\\"}"
180
+ message="${message//$'\n'/\\n}"
181
+ extra="$extra,\"message\":\"$message\""
182
+ fi
183
+
184
+ _job_send "$(_job_build_message "progress" "$extra")"
185
+ }
186
+
187
+ # Send a log message
188
+ # Usage: job_log <level> <message>
189
+ job_log() {
190
+ local level="$1"
191
+ local message="$2"
192
+
193
+ # Escape message for JSON
194
+ message="${message//\\/\\\\}"
195
+ message="${message//\"/\\\"}"
196
+ message="${message//$'\n'/\\n}"
197
+
198
+ _job_send "$(_job_build_message "log" "\"level\":\"$level\",\"message\":\"$message\"")"
199
+ }
200
+
201
+ # Convenience log functions
202
+ job_debug() { job_log "debug" "$1"; }
203
+ job_info() { job_log "info" "$1"; }
204
+ job_warn() { job_log "warn" "$1"; }
205
+ job_error() { job_log "error" "$1"; }
206
+
207
+ # Complete the job
208
+ # Usage: job_complete [result_json]
209
+ job_complete() {
210
+ local result="${1:-null}"
211
+
212
+ _job_stop_heartbeat
213
+
214
+ if [[ "$result" == "null" || -z "$result" ]]; then
215
+ _job_send "$(_job_build_message "completed")"
216
+ else
217
+ _job_send "$(_job_build_message "completed" "\"result\":$result")"
218
+ fi
219
+ }
220
+
221
+ # Fail the job
222
+ # Usage: job_fail <error_message>
223
+ job_fail() {
224
+ local error="$1"
225
+
226
+ _job_stop_heartbeat
227
+
228
+ # Escape error for JSON
229
+ error="${error//\\/\\\\}"
230
+ error="${error//\"/\\\"}"
231
+ error="${error//$'\n'/\\n}"
232
+
233
+ _job_send "$(_job_build_message "failed" "\"error\":\"$error\"")"
234
+ }
235
+
236
+ # Get a value from job data (requires jq)
237
+ # Usage: job_data_get <path>
238
+ job_data_get() {
239
+ local path="$1"
240
+
241
+ if command -v jq >/dev/null 2>&1; then
242
+ echo "$DONKEYLABS_JOB_DATA" | jq -r "$path"
243
+ else
244
+ echo "Error: jq is required to parse job data" >&2
245
+ return 1
246
+ fi
247
+ }
248
+
249
+ # Example usage (only runs if script is executed directly)
250
+ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
251
+ echo "Donkeylabs Job Shell Wrapper"
252
+ echo ""
253
+ echo "Usage: source this file in your shell script"
254
+ echo ""
255
+ echo "Example:"
256
+ echo " #!/bin/bash"
257
+ echo " source donkeylabs-job.sh"
258
+ echo ""
259
+ echo " job_init"
260
+ echo " job_progress 0 \"Starting...\""
261
+ echo " # Do work..."
262
+ echo " job_progress 100 \"Done!\""
263
+ echo " job_complete '{\"result\": \"success\"}'"
264
+ fi
@@ -0,0 +1,47 @@
1
+ #!/bin/bash
2
+ #
3
+ # Example External Job Script
4
+ #
5
+ # This script demonstrates how to use the donkeylabs-job.sh wrapper
6
+ # to create an external job that can be executed by the Donkeylabs server.
7
+ #
8
+
9
+ # Get the directory of this script
10
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
+
12
+ # Source the job wrapper
13
+ source "$SCRIPT_DIR/donkeylabs-job.sh"
14
+
15
+ # Initialize the job (reads payload from stdin, starts heartbeat)
16
+ job_init 5 # 5 second heartbeat interval
17
+
18
+ # Log that we're starting
19
+ job_info "Starting example job"
20
+ job_info "Job ID: $DONKEYLABS_JOB_ID"
21
+ job_info "Job Name: $DONKEYLABS_JOB_NAME"
22
+
23
+ # Get configuration from job data
24
+ STEPS=$(job_data_get '.steps // 5')
25
+ DELAY=$(job_data_get '.delay // 1')
26
+
27
+ job_info "Processing $STEPS steps with ${DELAY}s delay"
28
+
29
+ # Process each step
30
+ for i in $(seq 1 "$STEPS"); do
31
+ # Calculate progress
32
+ PROGRESS=$(( (i - 1) * 100 / STEPS ))
33
+
34
+ # Report progress
35
+ job_progress "$PROGRESS" "Processing step $i of $STEPS"
36
+
37
+ # Simulate work
38
+ sleep "$DELAY"
39
+
40
+ job_debug "Completed step $i"
41
+ done
42
+
43
+ # Final progress
44
+ job_progress 100 "All steps completed"
45
+
46
+ # Complete the job with result
47
+ job_complete "{\"processed\": true, \"steps\": $STEPS}"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@donkeylabs/server",
3
- "version": "0.5.0",
3
+ "version": "0.6.3",
4
4
  "type": "module",
5
5
  "description": "Type-safe plugin system for building RPC-style APIs with Bun",
6
6
  "main": "./src/index.ts",
@@ -30,6 +30,7 @@
30
30
  "files": [
31
31
  "src",
32
32
  "docs",
33
+ "examples",
33
34
  "CLAUDE.md",
34
35
  "context.d.ts",
35
36
  "registry.d.ts",
@@ -74,7 +75,7 @@
74
75
  ],
75
76
  "repository": {
76
77
  "type": "git",
77
- "url": "https://github.com/donkeylabs/server"
78
+ "url": "https://github.com/donkeylabs-io/donkeylabs"
78
79
  },
79
80
  "license": "MIT"
80
81
  }
@@ -248,19 +248,21 @@ export class ApiClientBase<TEvents extends Record<string, any> = Record<string,
248
248
  }
249
249
 
250
250
  /**
251
- * Make a raw request (for non-JSON endpoints)
251
+ * Make a raw request (for non-JSON endpoints like streaming)
252
252
  */
253
253
  protected async rawRequest(
254
254
  route: string,
255
- init: RequestInit = {}
255
+ init?: RequestInit
256
256
  ): Promise<Response> {
257
257
  const fetchFn = this.options.fetch || fetch;
258
+ const requestInit = init ?? {};
258
259
 
259
260
  return fetchFn(`${this.baseUrl}/${route}`, {
260
- ...init,
261
+ method: "POST",
262
+ ...requestInit,
261
263
  headers: {
262
264
  ...this.options.headers,
263
- ...init.headers,
265
+ ...requestInit.headers,
264
266
  },
265
267
  credentials: this.options.credentials,
266
268
  });
@@ -34,12 +34,18 @@ export interface SocketServerOptions {
34
34
  export interface ExternalJobSocketServer {
35
35
  /** Create a new socket for a job (returns socket path or TCP port) */
36
36
  createSocket(jobId: string): Promise<{ socketPath?: string; tcpPort?: number }>;
37
- /** Close a specific job's socket */
37
+ /** Close a specific job's socket and release reservations */
38
38
  closeSocket(jobId: string): Promise<void>;
39
39
  /** Get all active job connections */
40
40
  getActiveConnections(): string[];
41
41
  /** Attempt to reconnect to an existing socket */
42
42
  reconnect(jobId: string, socketPath?: string, tcpPort?: number): Promise<boolean>;
43
+ /** Reserve a socket path/port for an orphaned job (prevents reuse until released) */
44
+ reserve(jobId: string, socketPath?: string, tcpPort?: number): void;
45
+ /** Release reservation for a job (called when job is cleaned up) */
46
+ release(jobId: string): void;
47
+ /** Check if a socket path or port is reserved */
48
+ isReserved(socketPath?: string, tcpPort?: number): boolean;
43
49
  /** Shutdown all sockets and cleanup */
44
50
  shutdown(): Promise<void>;
45
51
  /** Clean orphaned socket files from a previous run */
@@ -68,6 +74,14 @@ export class ExternalJobSocketServerImpl implements ExternalJobSocketServer {
68
74
  private tcpPorts = new Map<string, number>();
69
75
  // Track used TCP ports
70
76
  private usedPorts = new Set<number>();
77
+ // Track reserved socket paths (for jobs that might reconnect)
78
+ private reservedSocketPaths = new Set<string>();
79
+ // Track reserved TCP ports (for jobs that might reconnect)
80
+ private reservedTcpPorts = new Set<number>();
81
+ // Map jobId -> reserved socket path (for release by jobId)
82
+ private jobReservedSocketPath = new Map<string, string>();
83
+ // Map jobId -> reserved TCP port (for release by jobId)
84
+ private jobReservedTcpPort = new Map<string, number>();
71
85
 
72
86
  private isWindows = process.platform === "win32";
73
87
 
@@ -96,6 +110,11 @@ export class ExternalJobSocketServerImpl implements ExternalJobSocketServer {
96
110
  private async createUnixServer(jobId: string): Promise<{ socketPath: string }> {
97
111
  const socketPath = join(this.socketDir, `job_${jobId}.sock`);
98
112
 
113
+ // Check if this socket path is reserved by another job
114
+ if (this.reservedSocketPaths.has(socketPath) && !this.jobReservedSocketPath.has(jobId)) {
115
+ throw new Error(`Socket path ${socketPath} is reserved by another job`);
116
+ }
117
+
99
118
  // Remove existing socket file if it exists
100
119
  if (existsSync(socketPath)) {
101
120
  await unlink(socketPath);
@@ -148,12 +167,14 @@ export class ExternalJobSocketServerImpl implements ExternalJobSocketServer {
148
167
  // Try random ports within range
149
168
  for (let i = 0; i < 100; i++) {
150
169
  const port = minPort + Math.floor(Math.random() * (maxPort - minPort));
151
- if (!this.usedPorts.has(port)) {
152
- // Check if port is actually available
153
- const isAvailable = await this.checkPortAvailable(port);
154
- if (isAvailable) {
155
- return port;
156
- }
170
+ // Skip if port is already in use or reserved by another job
171
+ if (this.usedPorts.has(port) || this.reservedTcpPorts.has(port)) {
172
+ continue;
173
+ }
174
+ // Check if port is actually available
175
+ const isAvailable = await this.checkPortAvailable(port);
176
+ if (isAvailable) {
177
+ return port;
157
178
  }
158
179
  }
159
180
 
@@ -241,12 +262,62 @@ export class ExternalJobSocketServerImpl implements ExternalJobSocketServer {
241
262
  this.usedPorts.delete(port);
242
263
  this.tcpPorts.delete(jobId);
243
264
  }
265
+
266
+ // Release any reservations for this job
267
+ this.release(jobId);
244
268
  }
245
269
 
246
270
  getActiveConnections(): string[] {
247
271
  return Array.from(this.clientSockets.keys());
248
272
  }
249
273
 
274
+ reserve(jobId: string, socketPath?: string, tcpPort?: number): void {
275
+ if (socketPath) {
276
+ this.reservedSocketPaths.add(socketPath);
277
+ this.jobReservedSocketPath.set(jobId, socketPath);
278
+ }
279
+ if (tcpPort) {
280
+ this.reservedTcpPorts.add(tcpPort);
281
+ this.jobReservedTcpPort.set(jobId, tcpPort);
282
+ }
283
+ }
284
+
285
+ release(jobId: string): void {
286
+ // Release socket path reservation
287
+ const socketPath = this.jobReservedSocketPath.get(jobId);
288
+ if (socketPath) {
289
+ this.reservedSocketPaths.delete(socketPath);
290
+ this.jobReservedSocketPath.delete(jobId);
291
+ }
292
+ // Also check socketPaths map (for active jobs)
293
+ const activeSocketPath = this.socketPaths.get(jobId);
294
+ if (activeSocketPath) {
295
+ this.reservedSocketPaths.delete(activeSocketPath);
296
+ }
297
+
298
+ // Release TCP port reservation
299
+ const tcpPort = this.jobReservedTcpPort.get(jobId);
300
+ if (tcpPort) {
301
+ this.reservedTcpPorts.delete(tcpPort);
302
+ this.jobReservedTcpPort.delete(jobId);
303
+ }
304
+ // Also check tcpPorts map (for active jobs)
305
+ const activeTcpPort = this.tcpPorts.get(jobId);
306
+ if (activeTcpPort) {
307
+ this.reservedTcpPorts.delete(activeTcpPort);
308
+ }
309
+ }
310
+
311
+ isReserved(socketPath?: string, tcpPort?: number): boolean {
312
+ if (socketPath && this.reservedSocketPaths.has(socketPath)) {
313
+ return true;
314
+ }
315
+ if (tcpPort && this.reservedTcpPorts.has(tcpPort)) {
316
+ return true;
317
+ }
318
+ return false;
319
+ }
320
+
250
321
  async reconnect(
251
322
  jobId: string,
252
323
  socketPath?: string,
@@ -257,19 +328,67 @@ export class ExternalJobSocketServerImpl implements ExternalJobSocketServer {
257
328
  return true;
258
329
  }
259
330
 
260
- // For Unix sockets, check if the socket file exists
261
- if (socketPath && existsSync(socketPath)) {
262
- // The external process should still be connected to this socket
263
- // We need to create a server to listen for the existing connection
264
- // However, this is tricky because the original server is gone
265
- // The reconnection strategy depends on the external process behavior
266
-
267
- // For now, we'll just check if the socket exists
268
- // The external process should attempt to reconnect if configured
269
- return false;
331
+ // For Unix sockets, recreate the server on the same path
332
+ // The external process should be retrying to connect
333
+ if (socketPath && !this.isWindows) {
334
+ try {
335
+ // Remove old socket file if it exists
336
+ if (existsSync(socketPath)) {
337
+ await unlink(socketPath);
338
+ }
339
+
340
+ // Create new server on the same path
341
+ return new Promise((resolve) => {
342
+ const server = createNetServer((socket) => {
343
+ this.handleConnection(jobId, socket);
344
+ });
345
+
346
+ server.on("error", (err) => {
347
+ this.onError?.(err, jobId);
348
+ resolve(false);
349
+ });
350
+
351
+ server.listen(socketPath, () => {
352
+ this.servers.set(jobId, server);
353
+ this.socketPaths.set(jobId, socketPath);
354
+ console.log(`[SocketServer] Recreated socket for job ${jobId} at ${socketPath}`);
355
+ // Return true - the server is ready, external process should reconnect
356
+ resolve(true);
357
+ });
358
+ });
359
+ } catch (err) {
360
+ this.onError?.(err as Error, jobId);
361
+ return false;
362
+ }
363
+ }
364
+
365
+ // For TCP, recreate the server on the same port
366
+ if (tcpPort && this.isWindows) {
367
+ try {
368
+ return new Promise((resolve) => {
369
+ const server = createNetServer((socket) => {
370
+ this.handleConnection(jobId, socket);
371
+ });
372
+
373
+ server.on("error", (err) => {
374
+ this.onError?.(err, jobId);
375
+ resolve(false);
376
+ });
377
+
378
+ server.listen(tcpPort, "127.0.0.1", () => {
379
+ this.servers.set(jobId, server);
380
+ this.tcpPorts.set(jobId, tcpPort);
381
+ this.usedPorts.add(tcpPort);
382
+ console.log(`[SocketServer] Recreated TCP server for job ${jobId} on port ${tcpPort}`);
383
+ resolve(true);
384
+ });
385
+ });
386
+ } catch (err) {
387
+ this.onError?.(err as Error, jobId);
388
+ return false;
389
+ }
270
390
  }
271
391
 
272
- // For TCP, we can't easily reconnect without the process knowing
273
392
  return false;
274
393
  }
275
394
 
@@ -319,9 +438,11 @@ export class ExternalJobSocketServerImpl implements ExternalJobSocketServer {
319
438
  const match = file.match(/^job_(.+)\.sock$/);
320
439
  if (match) {
321
440
  const jobId = match[1]!;
322
- if (!activeJobIds.has(jobId)) {
323
- // This socket file doesn't correspond to any active job
324
- const socketPath = join(this.socketDir, file);
441
+ const socketPath = join(this.socketDir, file);
442
+
443
+ // Don't clean if job is active or socket path is reserved
444
+ if (!activeJobIds.has(jobId) && !this.reservedSocketPaths.has(socketPath)) {
445
+ // This socket file doesn't correspond to any active job and isn't reserved
325
446
  await unlink(socketPath).catch(() => {});
326
447
  }
327
448
  }
package/src/core/index.ts CHANGED
@@ -47,6 +47,11 @@ export {
47
47
  createJobs,
48
48
  } from "./jobs";
49
49
 
50
+ export {
51
+ SqliteJobAdapter,
52
+ type SqliteJobAdapterConfig,
53
+ } from "./job-adapter-sqlite";
54
+
50
55
  export {
51
56
  type ExternalJobConfig,
52
57
  type ExternalJob,
@@ -141,3 +146,27 @@ export {
141
146
  workflow,
142
147
  createWorkflows,
143
148
  } from "./workflows";
149
+
150
+ export {
151
+ type Processes,
152
+ type ProcessesConfig,
153
+ type ProcessStatus,
154
+ type ProcessConfig,
155
+ type ProcessDefinition,
156
+ type ManagedProcess,
157
+ type SpawnOptions,
158
+ createProcesses,
159
+ } from "./processes";
160
+
161
+ export {
162
+ SqliteProcessAdapter,
163
+ type SqliteProcessAdapterConfig,
164
+ type ProcessAdapter,
165
+ } from "./process-adapter-sqlite";
166
+
167
+ export {
168
+ type ProcessSocketServer,
169
+ type ProcessMessage,
170
+ type ProcessSocketConfig,
171
+ createProcessSocketServer,
172
+ } from "./process-socket";