@herdctl/core 0.0.1 → 0.0.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/dist/config/__tests__/agent.test.js +31 -13
- package/dist/config/__tests__/agent.test.js.map +1 -1
- package/dist/config/__tests__/merge.test.js +9 -2
- package/dist/config/__tests__/merge.test.js.map +1 -1
- package/dist/config/__tests__/schema.test.js +350 -1
- package/dist/config/__tests__/schema.test.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +3 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/schema.d.ts +828 -24
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +118 -6
- package/dist/config/schema.js.map +1 -1
- package/dist/fleet-manager/__tests__/coverage.test.js +11 -332
- package/dist/fleet-manager/__tests__/coverage.test.js.map +1 -1
- package/dist/fleet-manager/__tests__/errors.test.js +1 -49
- package/dist/fleet-manager/__tests__/errors.test.js.map +1 -1
- package/dist/fleet-manager/__tests__/integration.test.js +109 -0
- package/dist/fleet-manager/__tests__/integration.test.js.map +1 -1
- package/dist/fleet-manager/__tests__/reload.test.js +1 -1
- package/dist/fleet-manager/__tests__/reload.test.js.map +1 -1
- package/dist/fleet-manager/config-reload.d.ts +164 -0
- package/dist/fleet-manager/config-reload.d.ts.map +1 -0
- package/dist/fleet-manager/config-reload.js +445 -0
- package/dist/fleet-manager/config-reload.js.map +1 -0
- package/dist/fleet-manager/context.d.ts +76 -0
- package/dist/fleet-manager/context.d.ts.map +1 -0
- package/dist/fleet-manager/context.js +11 -0
- package/dist/fleet-manager/context.js.map +1 -0
- package/dist/fleet-manager/errors.d.ts +0 -25
- package/dist/fleet-manager/errors.d.ts.map +1 -1
- package/dist/fleet-manager/errors.js +0 -38
- package/dist/fleet-manager/errors.js.map +1 -1
- package/dist/fleet-manager/event-emitters.d.ts +123 -0
- package/dist/fleet-manager/event-emitters.d.ts.map +1 -0
- package/dist/fleet-manager/event-emitters.js +136 -0
- package/dist/fleet-manager/event-emitters.js.map +1 -0
- package/dist/fleet-manager/event-types.d.ts +0 -15
- package/dist/fleet-manager/event-types.d.ts.map +1 -1
- package/dist/fleet-manager/fleet-manager.d.ts +40 -653
- package/dist/fleet-manager/fleet-manager.d.ts.map +1 -1
- package/dist/fleet-manager/fleet-manager.js +95 -1720
- package/dist/fleet-manager/fleet-manager.js.map +1 -1
- package/dist/fleet-manager/index.d.ts +13 -2
- package/dist/fleet-manager/index.d.ts.map +1 -1
- package/dist/fleet-manager/index.js +19 -6
- package/dist/fleet-manager/index.js.map +1 -1
- package/dist/fleet-manager/job-control.d.ts +64 -0
- package/dist/fleet-manager/job-control.d.ts.map +1 -0
- package/dist/fleet-manager/job-control.js +296 -0
- package/dist/fleet-manager/job-control.js.map +1 -0
- package/dist/fleet-manager/log-streaming.d.ts +171 -0
- package/dist/fleet-manager/log-streaming.d.ts.map +1 -0
- package/dist/fleet-manager/log-streaming.js +503 -0
- package/dist/fleet-manager/log-streaming.js.map +1 -0
- package/dist/fleet-manager/schedule-executor.d.ts +63 -0
- package/dist/fleet-manager/schedule-executor.d.ts.map +1 -0
- package/dist/fleet-manager/schedule-executor.js +209 -0
- package/dist/fleet-manager/schedule-executor.js.map +1 -0
- package/dist/fleet-manager/schedule-management.d.ts +71 -0
- package/dist/fleet-manager/schedule-management.d.ts.map +1 -0
- package/dist/fleet-manager/schedule-management.js +171 -0
- package/dist/fleet-manager/schedule-management.js.map +1 -0
- package/dist/fleet-manager/status-queries.d.ts +105 -0
- package/dist/fleet-manager/status-queries.d.ts.map +1 -0
- package/dist/fleet-manager/status-queries.js +247 -0
- package/dist/fleet-manager/status-queries.js.map +1 -0
- package/dist/fleet-manager/types.d.ts +0 -39
- package/dist/fleet-manager/types.d.ts.map +1 -1
- package/dist/runner/__tests__/job-executor.test.js +206 -1
- package/dist/runner/__tests__/job-executor.test.js.map +1 -1
- package/dist/runner/job-executor.d.ts +9 -0
- package/dist/runner/job-executor.d.ts.map +1 -1
- package/dist/runner/job-executor.js +78 -4
- package/dist/runner/job-executor.js.map +1 -1
- package/dist/runner/types.d.ts +2 -0
- package/dist/runner/types.d.ts.map +1 -1
- package/dist/scheduler/__tests__/cron.test.d.ts +2 -0
- package/dist/scheduler/__tests__/cron.test.d.ts.map +1 -0
- package/dist/scheduler/__tests__/cron.test.js +867 -0
- package/dist/scheduler/__tests__/cron.test.js.map +1 -0
- package/dist/scheduler/__tests__/scheduler.test.js +164 -5
- package/dist/scheduler/__tests__/scheduler.test.js.map +1 -1
- package/dist/scheduler/cron.d.ts +126 -0
- package/dist/scheduler/cron.d.ts.map +1 -0
- package/dist/scheduler/cron.js +390 -0
- package/dist/scheduler/cron.js.map +1 -0
- package/dist/scheduler/errors.d.ts +81 -1
- package/dist/scheduler/errors.d.ts.map +1 -1
- package/dist/scheduler/errors.js +81 -6
- package/dist/scheduler/errors.js.map +1 -1
- package/dist/scheduler/index.d.ts +1 -0
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +2 -0
- package/dist/scheduler/index.js.map +1 -1
- package/dist/scheduler/schedule-runner.d.ts +2 -2
- package/dist/scheduler/schedule-runner.d.ts.map +1 -1
- package/dist/scheduler/schedule-runner.js +20 -8
- package/dist/scheduler/schedule-runner.js.map +1 -1
- package/dist/scheduler/scheduler.d.ts +4 -4
- package/dist/scheduler/scheduler.d.ts.map +1 -1
- package/dist/scheduler/scheduler.js +86 -20
- package/dist/scheduler/scheduler.js.map +1 -1
- package/dist/scheduler/types.d.ts +1 -1
- package/dist/scheduler/types.d.ts.map +1 -1
- package/dist/state/schemas/job-metadata.d.ts +2 -2
- package/package.json +33 -8
- package/.turbo/turbo-build.log +0 -4
- package/.turbo/turbo-test.log +0 -219
- package/.turbo/turbo-typecheck.log +0 -4
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/coverage-final.json +0 -51
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -251
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -210
- package/coverage/src/config/index.html +0 -191
- package/coverage/src/config/index.ts.html +0 -442
- package/coverage/src/config/interpolate.ts.html +0 -652
- package/coverage/src/config/loader.ts.html +0 -1501
- package/coverage/src/config/merge.ts.html +0 -823
- package/coverage/src/config/parser.ts.html +0 -1213
- package/coverage/src/config/schema.ts.html +0 -1123
- package/coverage/src/fleet-manager/errors.ts.html +0 -2326
- package/coverage/src/fleet-manager/event-types.ts.html +0 -1219
- package/coverage/src/fleet-manager/fleet-manager.ts.html +0 -7030
- package/coverage/src/fleet-manager/index.html +0 -206
- package/coverage/src/fleet-manager/index.ts.html +0 -469
- package/coverage/src/fleet-manager/job-manager.ts.html +0 -2074
- package/coverage/src/fleet-manager/job-queue.ts.html +0 -2479
- package/coverage/src/fleet-manager/types.ts.html +0 -2602
- package/coverage/src/index.html +0 -116
- package/coverage/src/index.ts.html +0 -181
- package/coverage/src/runner/errors.ts.html +0 -1006
- package/coverage/src/runner/index.html +0 -191
- package/coverage/src/runner/index.ts.html +0 -256
- package/coverage/src/runner/job-executor.ts.html +0 -1429
- package/coverage/src/runner/message-processor.ts.html +0 -1150
- package/coverage/src/runner/sdk-adapter.ts.html +0 -658
- package/coverage/src/runner/types.ts.html +0 -559
- package/coverage/src/scheduler/errors.ts.html +0 -388
- package/coverage/src/scheduler/index.html +0 -206
- package/coverage/src/scheduler/index.ts.html +0 -244
- package/coverage/src/scheduler/interval.ts.html +0 -652
- package/coverage/src/scheduler/schedule-runner.ts.html +0 -1411
- package/coverage/src/scheduler/schedule-state.ts.html +0 -718
- package/coverage/src/scheduler/scheduler.ts.html +0 -1795
- package/coverage/src/scheduler/types.ts.html +0 -733
- package/coverage/src/state/directory.ts.html +0 -736
- package/coverage/src/state/errors.ts.html +0 -376
- package/coverage/src/state/fleet-state.ts.html +0 -937
- package/coverage/src/state/index.html +0 -221
- package/coverage/src/state/index.ts.html +0 -322
- package/coverage/src/state/job-metadata.ts.html +0 -1420
- package/coverage/src/state/job-output.ts.html +0 -1033
- package/coverage/src/state/schemas/fleet-state.ts.html +0 -445
- package/coverage/src/state/schemas/index.html +0 -176
- package/coverage/src/state/schemas/index.ts.html +0 -286
- package/coverage/src/state/schemas/job-metadata.ts.html +0 -628
- package/coverage/src/state/schemas/job-output.ts.html +0 -616
- package/coverage/src/state/schemas/session-info.ts.html +0 -361
- package/coverage/src/state/session.ts.html +0 -844
- package/coverage/src/state/types.ts.html +0 -262
- package/coverage/src/state/utils/atomic.ts.html +0 -748
- package/coverage/src/state/utils/index.html +0 -146
- package/coverage/src/state/utils/index.ts.html +0 -103
- package/coverage/src/state/utils/reads.ts.html +0 -1621
- package/coverage/src/work-sources/adapters/github.ts.html +0 -3583
- package/coverage/src/work-sources/adapters/index.html +0 -131
- package/coverage/src/work-sources/adapters/index.ts.html +0 -277
- package/coverage/src/work-sources/errors.ts.html +0 -298
- package/coverage/src/work-sources/index.html +0 -176
- package/coverage/src/work-sources/index.ts.html +0 -529
- package/coverage/src/work-sources/manager.ts.html +0 -1324
- package/coverage/src/work-sources/registry.ts.html +0 -619
- package/coverage/src/work-sources/types.ts.html +0 -568
- package/dist/fleet-manager/__tests__/event-helpers.test.d.ts +0 -7
- package/dist/fleet-manager/__tests__/event-helpers.test.d.ts.map +0 -1
- package/dist/fleet-manager/__tests__/event-helpers.test.js +0 -368
- package/dist/fleet-manager/__tests__/event-helpers.test.js.map +0 -1
- package/src/config/__tests__/agent.test.ts +0 -864
- package/src/config/__tests__/interpolate.test.ts +0 -644
- package/src/config/__tests__/loader.test.ts +0 -784
- package/src/config/__tests__/merge.test.ts +0 -751
- package/src/config/__tests__/parser.test.ts +0 -533
- package/src/config/__tests__/schema.test.ts +0 -873
- package/src/config/index.ts +0 -119
- package/src/config/interpolate.ts +0 -189
- package/src/config/loader.ts +0 -472
- package/src/config/merge.ts +0 -246
- package/src/config/parser.ts +0 -376
- package/src/config/schema.ts +0 -346
- package/src/fleet-manager/__tests__/coverage.test.ts +0 -2869
- package/src/fleet-manager/__tests__/errors.test.ts +0 -660
- package/src/fleet-manager/__tests__/event-helpers.test.ts +0 -448
- package/src/fleet-manager/__tests__/integration.test.ts +0 -1209
- package/src/fleet-manager/__tests__/job-control.test.ts +0 -283
- package/src/fleet-manager/__tests__/job-manager.test.ts +0 -869
- package/src/fleet-manager/__tests__/job-queue.test.ts +0 -401
- package/src/fleet-manager/__tests__/reload.test.ts +0 -751
- package/src/fleet-manager/__tests__/status-queries.test.ts +0 -595
- package/src/fleet-manager/__tests__/trigger.test.ts +0 -601
- package/src/fleet-manager/errors.ts +0 -747
- package/src/fleet-manager/event-types.ts +0 -378
- package/src/fleet-manager/fleet-manager.ts +0 -2315
- package/src/fleet-manager/index.ts +0 -128
- package/src/fleet-manager/job-manager.ts +0 -663
- package/src/fleet-manager/job-queue.ts +0 -798
- package/src/fleet-manager/types.ts +0 -839
- package/src/index.ts +0 -32
- package/src/runner/__tests__/errors.test.ts +0 -382
- package/src/runner/__tests__/job-executor.test.ts +0 -1708
- package/src/runner/__tests__/message-processor.test.ts +0 -960
- package/src/runner/__tests__/sdk-adapter.test.ts +0 -626
- package/src/runner/errors.ts +0 -307
- package/src/runner/index.ts +0 -57
- package/src/runner/job-executor.ts +0 -448
- package/src/runner/message-processor.ts +0 -355
- package/src/runner/sdk-adapter.ts +0 -191
- package/src/runner/types.ts +0 -158
- package/src/scheduler/__tests__/errors.test.ts +0 -159
- package/src/scheduler/__tests__/interval.test.ts +0 -515
- package/src/scheduler/__tests__/schedule-runner.test.ts +0 -798
- package/src/scheduler/__tests__/schedule-state.test.ts +0 -671
- package/src/scheduler/__tests__/scheduler.test.ts +0 -1280
- package/src/scheduler/errors.ts +0 -101
- package/src/scheduler/index.ts +0 -53
- package/src/scheduler/interval.ts +0 -189
- package/src/scheduler/schedule-runner.ts +0 -442
- package/src/scheduler/schedule-state.ts +0 -211
- package/src/scheduler/scheduler.ts +0 -570
- package/src/scheduler/types.ts +0 -216
- package/src/state/__tests__/directory.test.ts +0 -595
- package/src/state/__tests__/fleet-state.test.ts +0 -868
- package/src/state/__tests__/job-metadata-schema.test.ts +0 -414
- package/src/state/__tests__/job-metadata.test.ts +0 -831
- package/src/state/__tests__/job-output.test.ts +0 -856
- package/src/state/__tests__/session-schema.test.ts +0 -378
- package/src/state/__tests__/session.test.ts +0 -604
- package/src/state/directory.ts +0 -217
- package/src/state/errors.ts +0 -97
- package/src/state/fleet-state.ts +0 -284
- package/src/state/index.ts +0 -79
- package/src/state/job-metadata.ts +0 -445
- package/src/state/job-output.ts +0 -316
- package/src/state/schemas/__tests__/job-output.test.ts +0 -338
- package/src/state/schemas/fleet-state.ts +0 -120
- package/src/state/schemas/index.ts +0 -67
- package/src/state/schemas/job-metadata.ts +0 -181
- package/src/state/schemas/job-output.ts +0 -177
- package/src/state/schemas/session-info.ts +0 -92
- package/src/state/session.ts +0 -253
- package/src/state/types.ts +0 -59
- package/src/state/utils/__tests__/atomic.test.ts +0 -723
- package/src/state/utils/__tests__/reads.test.ts +0 -1071
- package/src/state/utils/atomic.ts +0 -221
- package/src/state/utils/index.ts +0 -6
- package/src/state/utils/reads.ts +0 -512
- package/src/work-sources/__tests__/github.test.ts +0 -1800
- package/src/work-sources/__tests__/manager.test.ts +0 -529
- package/src/work-sources/__tests__/registry.test.ts +0 -477
- package/src/work-sources/__tests__/types.test.ts +0 -479
- package/src/work-sources/adapters/github.ts +0 -1166
- package/src/work-sources/adapters/index.ts +0 -64
- package/src/work-sources/errors.ts +0 -71
- package/src/work-sources/index.ts +0 -148
- package/src/work-sources/manager.ts +0 -413
- package/src/work-sources/registry.ts +0 -178
- package/src/work-sources/types.ts +0 -161
- package/tsconfig.json +0 -9
- package/vitest.config.ts +0 -19
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log Streaming Module (US-8)
|
|
3
|
+
*
|
|
4
|
+
* Provides log streaming functionality as async generators for the FleetManager.
|
|
5
|
+
* Isolates the async generator patterns for streaming logs from agents, jobs,
|
|
6
|
+
* and the fleet.
|
|
7
|
+
*
|
|
8
|
+
* This module provides three main streaming functions:
|
|
9
|
+
* - `streamLogs` - Stream all fleet logs with filtering options
|
|
10
|
+
* - `streamJobOutput` - Stream output from a specific job
|
|
11
|
+
* - `streamAgentLogs` - Stream logs for a specific agent
|
|
12
|
+
*/
|
|
13
|
+
import type { JobMetadata } from "../state/schemas/job-metadata.js";
|
|
14
|
+
import type { LogLevel, LogEntry, LogStreamOptions } from "./types.js";
|
|
15
|
+
import type { FleetManagerContext } from "./context.js";
|
|
16
|
+
/**
|
|
17
|
+
* LogStreaming provides log streaming operations for the FleetManager.
|
|
18
|
+
*
|
|
19
|
+
* This class encapsulates the logic for streaming logs from agents, jobs,
|
|
20
|
+
* and the fleet using the FleetManagerContext pattern.
|
|
21
|
+
*/
|
|
22
|
+
export declare class LogStreaming {
|
|
23
|
+
private ctx;
|
|
24
|
+
constructor(ctx: FleetManagerContext);
|
|
25
|
+
private getDeps;
|
|
26
|
+
/**
|
|
27
|
+
* Stream all fleet logs as an async iterable
|
|
28
|
+
*
|
|
29
|
+
* Provides a unified stream of logs from all sources in the fleet including
|
|
30
|
+
* agents, jobs, and the scheduler. Logs can be filtered by level and optionally
|
|
31
|
+
* by agent or job.
|
|
32
|
+
*
|
|
33
|
+
* @param options - Options for filtering and configuring the stream
|
|
34
|
+
* @returns An async iterable of LogEntry objects
|
|
35
|
+
*/
|
|
36
|
+
streamLogs(options?: LogStreamOptions): AsyncIterable<LogEntry>;
|
|
37
|
+
/**
|
|
38
|
+
* Stream output from a specific job as an async iterable
|
|
39
|
+
*
|
|
40
|
+
* @param jobId - The ID of the job to stream output from
|
|
41
|
+
* @returns An async iterable of LogEntry objects
|
|
42
|
+
* @throws {JobNotFoundError} If the job doesn't exist
|
|
43
|
+
*/
|
|
44
|
+
streamJobOutput(jobId: string): AsyncIterable<LogEntry>;
|
|
45
|
+
/**
|
|
46
|
+
* Stream logs for a specific agent as an async iterable
|
|
47
|
+
*
|
|
48
|
+
* @param agentName - The name of the agent to stream logs for
|
|
49
|
+
* @returns An async iterable of LogEntry objects
|
|
50
|
+
* @throws {AgentNotFoundError} If the agent doesn't exist in the configuration
|
|
51
|
+
*/
|
|
52
|
+
streamAgentLogs(agentName: string): AsyncIterable<LogEntry>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Convert a job output message to a LogEntry
|
|
56
|
+
*
|
|
57
|
+
* Transforms raw job output (as stored in JSONL files) into a structured
|
|
58
|
+
* LogEntry for the streaming API.
|
|
59
|
+
*
|
|
60
|
+
* @param job - The job metadata
|
|
61
|
+
* @param msg - The raw output message
|
|
62
|
+
* @returns A LogEntry representing the message
|
|
63
|
+
*/
|
|
64
|
+
export declare function jobOutputToLogEntry(job: JobMetadata, msg: {
|
|
65
|
+
type: string;
|
|
66
|
+
content?: string;
|
|
67
|
+
timestamp?: string;
|
|
68
|
+
}): LogEntry;
|
|
69
|
+
/**
|
|
70
|
+
* Determine if a log entry should be yielded based on filters
|
|
71
|
+
*
|
|
72
|
+
* Checks log level, agent name, and job ID filters to determine
|
|
73
|
+
* if an entry should be included in the stream.
|
|
74
|
+
*
|
|
75
|
+
* @param entry - The log entry to check
|
|
76
|
+
* @param minLevel - Minimum log level to include
|
|
77
|
+
* @param agentFilter - Optional agent name filter
|
|
78
|
+
* @param jobFilter - Optional job ID filter
|
|
79
|
+
* @returns True if the entry should be yielded
|
|
80
|
+
*/
|
|
81
|
+
export declare function shouldYieldLog(entry: LogEntry, minLevel: LogLevel, agentFilter?: string, jobFilter?: string): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Get the log level order value
|
|
84
|
+
*
|
|
85
|
+
* Utility function to get the numeric ordering of a log level.
|
|
86
|
+
*
|
|
87
|
+
* @param level - The log level
|
|
88
|
+
* @returns The numeric ordering (0=debug, 1=info, 2=warn, 3=error)
|
|
89
|
+
*/
|
|
90
|
+
export declare function getLogLevelOrder(level: LogLevel): number;
|
|
91
|
+
/**
|
|
92
|
+
* Compare two log levels
|
|
93
|
+
*
|
|
94
|
+
* @param a - First log level
|
|
95
|
+
* @param b - Second log level
|
|
96
|
+
* @returns Negative if a < b, 0 if equal, positive if a > b
|
|
97
|
+
*/
|
|
98
|
+
export declare function compareLogLevels(a: LogLevel, b: LogLevel): number;
|
|
99
|
+
/**
|
|
100
|
+
* Check if a log level meets the minimum threshold
|
|
101
|
+
*
|
|
102
|
+
* @param level - The log level to check
|
|
103
|
+
* @param minLevel - The minimum required level
|
|
104
|
+
* @returns True if level >= minLevel
|
|
105
|
+
*/
|
|
106
|
+
export declare function meetsLogLevel(level: LogLevel, minLevel: LogLevel): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Filter log entries by level
|
|
109
|
+
*
|
|
110
|
+
* Creates a filtering function that only passes logs at or above
|
|
111
|
+
* the specified level.
|
|
112
|
+
*
|
|
113
|
+
* @param minLevel - Minimum log level to include
|
|
114
|
+
* @returns A filter function
|
|
115
|
+
*/
|
|
116
|
+
export declare function createLogLevelFilter(minLevel: LogLevel): (entry: LogEntry) => boolean;
|
|
117
|
+
/**
|
|
118
|
+
* Filter log entries by agent
|
|
119
|
+
*
|
|
120
|
+
* Creates a filtering function that only passes logs from
|
|
121
|
+
* the specified agent.
|
|
122
|
+
*
|
|
123
|
+
* @param agentName - Agent name to filter by
|
|
124
|
+
* @returns A filter function
|
|
125
|
+
*/
|
|
126
|
+
export declare function createAgentFilter(agentName: string): (entry: LogEntry) => boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Filter log entries by job
|
|
129
|
+
*
|
|
130
|
+
* Creates a filtering function that only passes logs from
|
|
131
|
+
* the specified job.
|
|
132
|
+
*
|
|
133
|
+
* @param jobId - Job ID to filter by
|
|
134
|
+
* @returns A filter function
|
|
135
|
+
*/
|
|
136
|
+
export declare function createJobFilter(jobId: string): (entry: LogEntry) => boolean;
|
|
137
|
+
/**
|
|
138
|
+
* Combine multiple log filters
|
|
139
|
+
*
|
|
140
|
+
* Creates a filter that passes only if all provided filters pass.
|
|
141
|
+
*
|
|
142
|
+
* @param filters - Array of filter functions
|
|
143
|
+
* @returns A combined filter function
|
|
144
|
+
*/
|
|
145
|
+
export declare function combineLogFilters(...filters: ((entry: LogEntry) => boolean)[]): (entry: LogEntry) => boolean;
|
|
146
|
+
/**
|
|
147
|
+
* Create a log entry from raw data
|
|
148
|
+
*
|
|
149
|
+
* Factory function for creating LogEntry objects with proper defaults.
|
|
150
|
+
*
|
|
151
|
+
* @param data - Partial log entry data
|
|
152
|
+
* @returns A complete LogEntry
|
|
153
|
+
*/
|
|
154
|
+
export declare function createLogEntry(data: Partial<LogEntry> & {
|
|
155
|
+
message: string;
|
|
156
|
+
}): LogEntry;
|
|
157
|
+
/**
|
|
158
|
+
* Format a log entry as a string
|
|
159
|
+
*
|
|
160
|
+
* Creates a human-readable string representation of a log entry.
|
|
161
|
+
*
|
|
162
|
+
* @param entry - The log entry to format
|
|
163
|
+
* @param options - Formatting options
|
|
164
|
+
* @returns A formatted string
|
|
165
|
+
*/
|
|
166
|
+
export declare function formatLogEntry(entry: LogEntry, options?: {
|
|
167
|
+
includeTimestamp?: boolean;
|
|
168
|
+
includeSource?: boolean;
|
|
169
|
+
includeContext?: boolean;
|
|
170
|
+
}): string;
|
|
171
|
+
//# sourceMappingURL=log-streaming.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-streaming.d.ts","sourceRoot":"","sources":["../../src/fleet-manager/log-streaming.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,KAAK,EAEV,QAAQ,EACR,QAAQ,EACR,gBAAgB,EAEjB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AA+BxD;;;;;GAKG;AACH,qBAAa,YAAY;IACX,OAAO,CAAC,GAAG;gBAAH,GAAG,EAAE,mBAAmB;IAE5C,OAAO,CAAC,OAAO;IASf;;;;;;;;;OASG;IACI,UAAU,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,aAAa,CAAC,QAAQ,CAAC;IAItE;;;;;;OAMG;IACI,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC;IAI9D;;;;;;OAMG;IACI,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC;CAGnE;AAMD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1D,QAAQ,CAmBV;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,QAAQ,EACf,QAAQ,EAAE,QAAQ,EAClB,WAAW,CAAC,EAAE,MAAM,EACpB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAiBT;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAExD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAEjE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAE1E;AAgSD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,QAAQ,GACjB,CAAC,KAAK,EAAE,QAAQ,KAAK,OAAO,CAE9B;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,GAChB,CAAC,KAAK,EAAE,QAAQ,KAAK,OAAO,CAE9B;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,KAAK,EAAE,QAAQ,KAAK,OAAO,CAE3E;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,QAAQ,KAAK,OAAO,CAAC,EAAE,GAC3C,CAAC,KAAK,EAAE,QAAQ,KAAK,OAAO,CAE9B;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAC5C,QAAQ,CAWV;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,QAAQ,EACf,OAAO,CAAC,EAAE;IACR,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,GACA,MAAM,CAyBR"}
|
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log Streaming Module (US-8)
|
|
3
|
+
*
|
|
4
|
+
* Provides log streaming functionality as async generators for the FleetManager.
|
|
5
|
+
* Isolates the async generator patterns for streaming logs from agents, jobs,
|
|
6
|
+
* and the fleet.
|
|
7
|
+
*
|
|
8
|
+
* This module provides three main streaming functions:
|
|
9
|
+
* - `streamLogs` - Stream all fleet logs with filtering options
|
|
10
|
+
* - `streamJobOutput` - Stream output from a specific job
|
|
11
|
+
* - `streamAgentLogs` - Stream logs for a specific agent
|
|
12
|
+
*/
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { AgentNotFoundError, JobNotFoundError } from "./errors.js";
|
|
15
|
+
// =============================================================================
|
|
16
|
+
// Constants
|
|
17
|
+
// =============================================================================
|
|
18
|
+
/**
|
|
19
|
+
* Log level severity ordering for filtering
|
|
20
|
+
*/
|
|
21
|
+
const LOG_LEVEL_ORDER = {
|
|
22
|
+
debug: 0,
|
|
23
|
+
info: 1,
|
|
24
|
+
warn: 2,
|
|
25
|
+
error: 3,
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* LogStreaming provides log streaming operations for the FleetManager.
|
|
29
|
+
*
|
|
30
|
+
* This class encapsulates the logic for streaming logs from agents, jobs,
|
|
31
|
+
* and the fleet using the FleetManagerContext pattern.
|
|
32
|
+
*/
|
|
33
|
+
export class LogStreaming {
|
|
34
|
+
ctx;
|
|
35
|
+
constructor(ctx) {
|
|
36
|
+
this.ctx = ctx;
|
|
37
|
+
}
|
|
38
|
+
getDeps() {
|
|
39
|
+
return {
|
|
40
|
+
stateDir: this.ctx.getStateDir(),
|
|
41
|
+
config: this.ctx.getConfig(),
|
|
42
|
+
logger: this.ctx.getLogger(),
|
|
43
|
+
emitter: this.ctx.getEmitter(),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Stream all fleet logs as an async iterable
|
|
48
|
+
*
|
|
49
|
+
* Provides a unified stream of logs from all sources in the fleet including
|
|
50
|
+
* agents, jobs, and the scheduler. Logs can be filtered by level and optionally
|
|
51
|
+
* by agent or job.
|
|
52
|
+
*
|
|
53
|
+
* @param options - Options for filtering and configuring the stream
|
|
54
|
+
* @returns An async iterable of LogEntry objects
|
|
55
|
+
*/
|
|
56
|
+
async *streamLogs(options) {
|
|
57
|
+
yield* streamLogsImpl(this.getDeps(), options);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Stream output from a specific job as an async iterable
|
|
61
|
+
*
|
|
62
|
+
* @param jobId - The ID of the job to stream output from
|
|
63
|
+
* @returns An async iterable of LogEntry objects
|
|
64
|
+
* @throws {JobNotFoundError} If the job doesn't exist
|
|
65
|
+
*/
|
|
66
|
+
async *streamJobOutput(jobId) {
|
|
67
|
+
yield* streamJobOutputImpl(this.getDeps(), jobId);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Stream logs for a specific agent as an async iterable
|
|
71
|
+
*
|
|
72
|
+
* @param agentName - The name of the agent to stream logs for
|
|
73
|
+
* @returns An async iterable of LogEntry objects
|
|
74
|
+
* @throws {AgentNotFoundError} If the agent doesn't exist in the configuration
|
|
75
|
+
*/
|
|
76
|
+
async *streamAgentLogs(agentName) {
|
|
77
|
+
yield* streamAgentLogsImpl(this.getDeps(), agentName);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// =============================================================================
|
|
81
|
+
// Helper Functions
|
|
82
|
+
// =============================================================================
|
|
83
|
+
/**
|
|
84
|
+
* Convert a job output message to a LogEntry
|
|
85
|
+
*
|
|
86
|
+
* Transforms raw job output (as stored in JSONL files) into a structured
|
|
87
|
+
* LogEntry for the streaming API.
|
|
88
|
+
*
|
|
89
|
+
* @param job - The job metadata
|
|
90
|
+
* @param msg - The raw output message
|
|
91
|
+
* @returns A LogEntry representing the message
|
|
92
|
+
*/
|
|
93
|
+
export function jobOutputToLogEntry(job, msg) {
|
|
94
|
+
// Determine log level based on message type
|
|
95
|
+
let level = "info";
|
|
96
|
+
if (msg.type === "error") {
|
|
97
|
+
level = "error";
|
|
98
|
+
}
|
|
99
|
+
else if (msg.type === "system") {
|
|
100
|
+
level = "debug";
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
timestamp: msg.timestamp ?? new Date().toISOString(),
|
|
104
|
+
level,
|
|
105
|
+
source: "job",
|
|
106
|
+
agentName: job.agent,
|
|
107
|
+
jobId: job.id,
|
|
108
|
+
scheduleName: job.schedule ?? undefined,
|
|
109
|
+
message: msg.content ?? "",
|
|
110
|
+
data: { type: msg.type },
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Determine if a log entry should be yielded based on filters
|
|
115
|
+
*
|
|
116
|
+
* Checks log level, agent name, and job ID filters to determine
|
|
117
|
+
* if an entry should be included in the stream.
|
|
118
|
+
*
|
|
119
|
+
* @param entry - The log entry to check
|
|
120
|
+
* @param minLevel - Minimum log level to include
|
|
121
|
+
* @param agentFilter - Optional agent name filter
|
|
122
|
+
* @param jobFilter - Optional job ID filter
|
|
123
|
+
* @returns True if the entry should be yielded
|
|
124
|
+
*/
|
|
125
|
+
export function shouldYieldLog(entry, minLevel, agentFilter, jobFilter) {
|
|
126
|
+
// Check log level
|
|
127
|
+
if (LOG_LEVEL_ORDER[entry.level] < LOG_LEVEL_ORDER[minLevel]) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
// Check agent filter
|
|
131
|
+
if (agentFilter && entry.agentName !== agentFilter) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
// Check job filter
|
|
135
|
+
if (jobFilter && entry.jobId !== jobFilter) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get the log level order value
|
|
142
|
+
*
|
|
143
|
+
* Utility function to get the numeric ordering of a log level.
|
|
144
|
+
*
|
|
145
|
+
* @param level - The log level
|
|
146
|
+
* @returns The numeric ordering (0=debug, 1=info, 2=warn, 3=error)
|
|
147
|
+
*/
|
|
148
|
+
export function getLogLevelOrder(level) {
|
|
149
|
+
return LOG_LEVEL_ORDER[level];
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Compare two log levels
|
|
153
|
+
*
|
|
154
|
+
* @param a - First log level
|
|
155
|
+
* @param b - Second log level
|
|
156
|
+
* @returns Negative if a < b, 0 if equal, positive if a > b
|
|
157
|
+
*/
|
|
158
|
+
export function compareLogLevels(a, b) {
|
|
159
|
+
return LOG_LEVEL_ORDER[a] - LOG_LEVEL_ORDER[b];
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Check if a log level meets the minimum threshold
|
|
163
|
+
*
|
|
164
|
+
* @param level - The log level to check
|
|
165
|
+
* @param minLevel - The minimum required level
|
|
166
|
+
* @returns True if level >= minLevel
|
|
167
|
+
*/
|
|
168
|
+
export function meetsLogLevel(level, minLevel) {
|
|
169
|
+
return LOG_LEVEL_ORDER[level] >= LOG_LEVEL_ORDER[minLevel];
|
|
170
|
+
}
|
|
171
|
+
// =============================================================================
|
|
172
|
+
// Internal Streaming Implementation Functions
|
|
173
|
+
// =============================================================================
|
|
174
|
+
/**
|
|
175
|
+
* Internal implementation for streaming fleet logs
|
|
176
|
+
*/
|
|
177
|
+
async function* streamLogsImpl(deps, options) {
|
|
178
|
+
const level = options?.level ?? "info";
|
|
179
|
+
const includeHistory = options?.includeHistory ?? true;
|
|
180
|
+
const historyLimit = options?.historyLimit ?? 1000;
|
|
181
|
+
const agentFilter = options?.agentName;
|
|
182
|
+
const jobFilter = options?.jobId;
|
|
183
|
+
const jobsDir = join(deps.stateDir, "jobs");
|
|
184
|
+
const { readJobOutputAll } = await import("../state/job-output.js");
|
|
185
|
+
const { listJobs } = await import("../state/index.js");
|
|
186
|
+
// Replay historical logs if requested
|
|
187
|
+
if (includeHistory) {
|
|
188
|
+
// Get jobs to replay history from
|
|
189
|
+
const jobsResult = await listJobs(jobsDir, agentFilter ? { agent: agentFilter } : {}, { logger: deps.logger });
|
|
190
|
+
// Filter by job ID if specified
|
|
191
|
+
let jobs = jobsResult.jobs;
|
|
192
|
+
if (jobFilter) {
|
|
193
|
+
jobs = jobs.filter((j) => j.id === jobFilter);
|
|
194
|
+
}
|
|
195
|
+
// Sort by started_at ascending to replay in chronological order
|
|
196
|
+
jobs.sort((a, b) => new Date(a.started_at).getTime() - new Date(b.started_at).getTime());
|
|
197
|
+
let yielded = 0;
|
|
198
|
+
for (const job of jobs) {
|
|
199
|
+
if (yielded >= historyLimit)
|
|
200
|
+
break;
|
|
201
|
+
// Read job output and convert to log entries
|
|
202
|
+
const output = await readJobOutputAll(jobsDir, job.id, {
|
|
203
|
+
skipInvalidLines: true,
|
|
204
|
+
logger: deps.logger,
|
|
205
|
+
});
|
|
206
|
+
for (const msg of output) {
|
|
207
|
+
if (yielded >= historyLimit)
|
|
208
|
+
break;
|
|
209
|
+
const logEntry = jobOutputToLogEntry(job, msg);
|
|
210
|
+
if (shouldYieldLog(logEntry, level, agentFilter, jobFilter)) {
|
|
211
|
+
yield logEntry;
|
|
212
|
+
yielded++;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// For running jobs, subscribe to job:output events
|
|
218
|
+
const outputQueue = [];
|
|
219
|
+
let resolveWait = null;
|
|
220
|
+
let stopped = false;
|
|
221
|
+
const outputHandler = (payload) => {
|
|
222
|
+
if (stopped)
|
|
223
|
+
return;
|
|
224
|
+
const logEntry = {
|
|
225
|
+
timestamp: payload.timestamp,
|
|
226
|
+
level: "info",
|
|
227
|
+
source: "job",
|
|
228
|
+
agentName: payload.agentName,
|
|
229
|
+
jobId: payload.jobId,
|
|
230
|
+
message: payload.output,
|
|
231
|
+
};
|
|
232
|
+
if (shouldYieldLog(logEntry, level, agentFilter, jobFilter)) {
|
|
233
|
+
outputQueue.push(logEntry);
|
|
234
|
+
if (resolveWait) {
|
|
235
|
+
resolveWait();
|
|
236
|
+
resolveWait = null;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
deps.emitter.on("job:output", outputHandler);
|
|
241
|
+
try {
|
|
242
|
+
// Yield queued entries as they arrive
|
|
243
|
+
while (!stopped) {
|
|
244
|
+
while (outputQueue.length > 0) {
|
|
245
|
+
const entry = outputQueue.shift();
|
|
246
|
+
yield entry;
|
|
247
|
+
}
|
|
248
|
+
// Wait for more entries
|
|
249
|
+
await new Promise((resolve) => {
|
|
250
|
+
resolveWait = resolve;
|
|
251
|
+
// Add timeout to prevent hanging forever
|
|
252
|
+
setTimeout(resolve, 1000);
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
finally {
|
|
257
|
+
stopped = true;
|
|
258
|
+
deps.emitter.off("job:output", outputHandler);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Internal implementation for streaming job output
|
|
263
|
+
*/
|
|
264
|
+
async function* streamJobOutputImpl(deps, jobId) {
|
|
265
|
+
const jobsDir = join(deps.stateDir, "jobs");
|
|
266
|
+
const { getJob } = await import("../state/index.js");
|
|
267
|
+
// Verify job exists
|
|
268
|
+
const job = await getJob(jobsDir, jobId, { logger: deps.logger });
|
|
269
|
+
if (!job) {
|
|
270
|
+
throw new JobNotFoundError(jobId);
|
|
271
|
+
}
|
|
272
|
+
const { readJobOutputAll, getJobOutputPath } = await import("../state/job-output.js");
|
|
273
|
+
const { watch } = await import("node:fs");
|
|
274
|
+
const { stat } = await import("node:fs/promises");
|
|
275
|
+
const { createReadStream } = await import("node:fs");
|
|
276
|
+
const { createInterface } = await import("node:readline");
|
|
277
|
+
const outputPath = getJobOutputPath(jobsDir, jobId);
|
|
278
|
+
// First, replay all existing output
|
|
279
|
+
const existingOutput = await readJobOutputAll(jobsDir, jobId, {
|
|
280
|
+
skipInvalidLines: true,
|
|
281
|
+
logger: deps.logger,
|
|
282
|
+
});
|
|
283
|
+
for (const msg of existingOutput) {
|
|
284
|
+
yield jobOutputToLogEntry(job, msg);
|
|
285
|
+
}
|
|
286
|
+
// If job is already completed, we're done
|
|
287
|
+
if (job.status !== "running" && job.status !== "pending") {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
// For running jobs, watch for new output
|
|
291
|
+
const outputQueue = [];
|
|
292
|
+
let resolveWait = null;
|
|
293
|
+
let stopped = false;
|
|
294
|
+
let lastReadPosition = 0;
|
|
295
|
+
// Get current file position
|
|
296
|
+
try {
|
|
297
|
+
const stats = await stat(outputPath);
|
|
298
|
+
lastReadPosition = stats.size;
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
// File doesn't exist yet
|
|
302
|
+
}
|
|
303
|
+
// Watch for file changes
|
|
304
|
+
let watcher = null;
|
|
305
|
+
try {
|
|
306
|
+
watcher = watch(outputPath, async (eventType) => {
|
|
307
|
+
if (stopped || eventType !== "change")
|
|
308
|
+
return;
|
|
309
|
+
try {
|
|
310
|
+
const currentStats = await stat(outputPath);
|
|
311
|
+
if (currentStats.size > lastReadPosition) {
|
|
312
|
+
// Read new content
|
|
313
|
+
const fileStream = createReadStream(outputPath, {
|
|
314
|
+
encoding: "utf-8",
|
|
315
|
+
start: lastReadPosition,
|
|
316
|
+
});
|
|
317
|
+
const rl = createInterface({
|
|
318
|
+
input: fileStream,
|
|
319
|
+
crlfDelay: Infinity,
|
|
320
|
+
});
|
|
321
|
+
for await (const line of rl) {
|
|
322
|
+
if (stopped)
|
|
323
|
+
break;
|
|
324
|
+
const trimmedLine = line.trim();
|
|
325
|
+
if (trimmedLine === "")
|
|
326
|
+
continue;
|
|
327
|
+
try {
|
|
328
|
+
const parsed = JSON.parse(trimmedLine);
|
|
329
|
+
const logEntry = jobOutputToLogEntry(job, parsed);
|
|
330
|
+
outputQueue.push(logEntry);
|
|
331
|
+
if (resolveWait) {
|
|
332
|
+
resolveWait();
|
|
333
|
+
resolveWait = null;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
catch {
|
|
337
|
+
// Skip malformed lines
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
rl.close();
|
|
341
|
+
fileStream.destroy();
|
|
342
|
+
lastReadPosition = currentStats.size;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
catch (err) {
|
|
346
|
+
deps.logger.warn(`Error reading output file: ${err.message}`);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
catch {
|
|
351
|
+
// Can't watch file - might not exist yet
|
|
352
|
+
}
|
|
353
|
+
// Poll for job completion
|
|
354
|
+
const checkJobComplete = async () => {
|
|
355
|
+
const { getJob: getJobFn } = await import("../state/index.js");
|
|
356
|
+
const currentJob = await getJobFn(jobsDir, jobId, { logger: deps.logger });
|
|
357
|
+
return (!currentJob ||
|
|
358
|
+
(currentJob.status !== "running" && currentJob.status !== "pending"));
|
|
359
|
+
};
|
|
360
|
+
try {
|
|
361
|
+
// Yield queued entries as they arrive
|
|
362
|
+
while (!stopped) {
|
|
363
|
+
while (outputQueue.length > 0) {
|
|
364
|
+
const entry = outputQueue.shift();
|
|
365
|
+
yield entry;
|
|
366
|
+
}
|
|
367
|
+
// Check if job is complete
|
|
368
|
+
if (await checkJobComplete()) {
|
|
369
|
+
stopped = true;
|
|
370
|
+
break;
|
|
371
|
+
}
|
|
372
|
+
// Wait for more entries
|
|
373
|
+
await new Promise((resolve) => {
|
|
374
|
+
resolveWait = resolve;
|
|
375
|
+
setTimeout(resolve, 1000);
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
finally {
|
|
380
|
+
stopped = true;
|
|
381
|
+
if (watcher) {
|
|
382
|
+
watcher.close();
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Internal implementation for streaming agent logs
|
|
388
|
+
*/
|
|
389
|
+
async function* streamAgentLogsImpl(deps, agentName) {
|
|
390
|
+
// Verify agent exists
|
|
391
|
+
const agents = deps.config?.agents ?? [];
|
|
392
|
+
const agent = agents.find((a) => a.name === agentName);
|
|
393
|
+
if (!agent) {
|
|
394
|
+
throw new AgentNotFoundError(agentName, {
|
|
395
|
+
availableAgents: agents.map((a) => a.name),
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
// Delegate to streamLogsImpl with agent filter
|
|
399
|
+
yield* streamLogsImpl(deps, {
|
|
400
|
+
agentName,
|
|
401
|
+
includeHistory: true,
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
// =============================================================================
|
|
405
|
+
// Utility Functions for Log Processing
|
|
406
|
+
// =============================================================================
|
|
407
|
+
/**
|
|
408
|
+
* Filter log entries by level
|
|
409
|
+
*
|
|
410
|
+
* Creates a filtering function that only passes logs at or above
|
|
411
|
+
* the specified level.
|
|
412
|
+
*
|
|
413
|
+
* @param minLevel - Minimum log level to include
|
|
414
|
+
* @returns A filter function
|
|
415
|
+
*/
|
|
416
|
+
export function createLogLevelFilter(minLevel) {
|
|
417
|
+
return (entry) => meetsLogLevel(entry.level, minLevel);
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Filter log entries by agent
|
|
421
|
+
*
|
|
422
|
+
* Creates a filtering function that only passes logs from
|
|
423
|
+
* the specified agent.
|
|
424
|
+
*
|
|
425
|
+
* @param agentName - Agent name to filter by
|
|
426
|
+
* @returns A filter function
|
|
427
|
+
*/
|
|
428
|
+
export function createAgentFilter(agentName) {
|
|
429
|
+
return (entry) => entry.agentName === agentName;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Filter log entries by job
|
|
433
|
+
*
|
|
434
|
+
* Creates a filtering function that only passes logs from
|
|
435
|
+
* the specified job.
|
|
436
|
+
*
|
|
437
|
+
* @param jobId - Job ID to filter by
|
|
438
|
+
* @returns A filter function
|
|
439
|
+
*/
|
|
440
|
+
export function createJobFilter(jobId) {
|
|
441
|
+
return (entry) => entry.jobId === jobId;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Combine multiple log filters
|
|
445
|
+
*
|
|
446
|
+
* Creates a filter that passes only if all provided filters pass.
|
|
447
|
+
*
|
|
448
|
+
* @param filters - Array of filter functions
|
|
449
|
+
* @returns A combined filter function
|
|
450
|
+
*/
|
|
451
|
+
export function combineLogFilters(...filters) {
|
|
452
|
+
return (entry) => filters.every((f) => f(entry));
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Create a log entry from raw data
|
|
456
|
+
*
|
|
457
|
+
* Factory function for creating LogEntry objects with proper defaults.
|
|
458
|
+
*
|
|
459
|
+
* @param data - Partial log entry data
|
|
460
|
+
* @returns A complete LogEntry
|
|
461
|
+
*/
|
|
462
|
+
export function createLogEntry(data) {
|
|
463
|
+
return {
|
|
464
|
+
timestamp: data.timestamp ?? new Date().toISOString(),
|
|
465
|
+
level: data.level ?? "info",
|
|
466
|
+
source: data.source ?? "fleet",
|
|
467
|
+
agentName: data.agentName,
|
|
468
|
+
jobId: data.jobId,
|
|
469
|
+
scheduleName: data.scheduleName,
|
|
470
|
+
message: data.message,
|
|
471
|
+
data: data.data,
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Format a log entry as a string
|
|
476
|
+
*
|
|
477
|
+
* Creates a human-readable string representation of a log entry.
|
|
478
|
+
*
|
|
479
|
+
* @param entry - The log entry to format
|
|
480
|
+
* @param options - Formatting options
|
|
481
|
+
* @returns A formatted string
|
|
482
|
+
*/
|
|
483
|
+
export function formatLogEntry(entry, options) {
|
|
484
|
+
const parts = [];
|
|
485
|
+
if (options?.includeTimestamp !== false) {
|
|
486
|
+
parts.push(`[${entry.timestamp}]`);
|
|
487
|
+
}
|
|
488
|
+
parts.push(`[${entry.level.toUpperCase()}]`);
|
|
489
|
+
if (options?.includeSource !== false && entry.source) {
|
|
490
|
+
parts.push(`[${entry.source}]`);
|
|
491
|
+
}
|
|
492
|
+
if (options?.includeContext !== false) {
|
|
493
|
+
if (entry.agentName) {
|
|
494
|
+
parts.push(`[${entry.agentName}]`);
|
|
495
|
+
}
|
|
496
|
+
if (entry.jobId) {
|
|
497
|
+
parts.push(`[${entry.jobId}]`);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
parts.push(entry.message);
|
|
501
|
+
return parts.join(" ");
|
|
502
|
+
}
|
|
503
|
+
//# sourceMappingURL=log-streaming.js.map
|