@telora/daemon-core 0.2.5

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/dist/activity-tracker.d.ts +69 -0
  2. package/dist/activity-tracker.d.ts.map +1 -0
  3. package/dist/activity-tracker.js +155 -0
  4. package/dist/activity-tracker.js.map +1 -0
  5. package/dist/api-client.d.ts +94 -0
  6. package/dist/api-client.d.ts.map +1 -0
  7. package/dist/api-client.js +145 -0
  8. package/dist/api-client.js.map +1 -0
  9. package/dist/config.d.ts +117 -0
  10. package/dist/config.d.ts.map +1 -0
  11. package/dist/config.js +348 -0
  12. package/dist/config.js.map +1 -0
  13. package/dist/engine.d.ts +120 -0
  14. package/dist/engine.d.ts.map +1 -0
  15. package/dist/engine.js +18 -0
  16. package/dist/engine.js.map +1 -0
  17. package/dist/escalation-types.d.ts +31 -0
  18. package/dist/escalation-types.d.ts.map +1 -0
  19. package/dist/escalation-types.js +24 -0
  20. package/dist/escalation-types.js.map +1 -0
  21. package/dist/event-logger.d.ts +13 -0
  22. package/dist/event-logger.d.ts.map +1 -0
  23. package/dist/event-logger.js +82 -0
  24. package/dist/event-logger.js.map +1 -0
  25. package/dist/git.d.ts +39 -0
  26. package/dist/git.d.ts.map +1 -0
  27. package/dist/git.js +72 -0
  28. package/dist/git.js.map +1 -0
  29. package/dist/index.d.ts +20 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +36 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/lifecycle.d.ts +104 -0
  34. package/dist/lifecycle.d.ts.map +1 -0
  35. package/dist/lifecycle.js +192 -0
  36. package/dist/lifecycle.js.map +1 -0
  37. package/dist/log-manager.d.ts +83 -0
  38. package/dist/log-manager.d.ts.map +1 -0
  39. package/dist/log-manager.js +217 -0
  40. package/dist/log-manager.js.map +1 -0
  41. package/dist/otel-env.d.ts +29 -0
  42. package/dist/otel-env.d.ts.map +1 -0
  43. package/dist/otel-env.js +44 -0
  44. package/dist/otel-env.js.map +1 -0
  45. package/dist/resilience.d.ts +127 -0
  46. package/dist/resilience.d.ts.map +1 -0
  47. package/dist/resilience.js +300 -0
  48. package/dist/resilience.js.map +1 -0
  49. package/dist/resource-governor.d.ts +83 -0
  50. package/dist/resource-governor.d.ts.map +1 -0
  51. package/dist/resource-governor.js +184 -0
  52. package/dist/resource-governor.js.map +1 -0
  53. package/dist/spawn.d.ts +72 -0
  54. package/dist/spawn.d.ts.map +1 -0
  55. package/dist/spawn.js +82 -0
  56. package/dist/spawn.js.map +1 -0
  57. package/dist/stream-json.d.ts +885 -0
  58. package/dist/stream-json.d.ts.map +1 -0
  59. package/dist/stream-json.js +298 -0
  60. package/dist/stream-json.js.map +1 -0
  61. package/dist/token-usage.d.ts +67 -0
  62. package/dist/token-usage.d.ts.map +1 -0
  63. package/dist/token-usage.js +150 -0
  64. package/dist/token-usage.js.map +1 -0
  65. package/dist/transforms.d.ts +64 -0
  66. package/dist/transforms.d.ts.map +1 -0
  67. package/dist/transforms.js +78 -0
  68. package/dist/transforms.js.map +1 -0
  69. package/dist/unified-config.d.ts +62 -0
  70. package/dist/unified-config.d.ts.map +1 -0
  71. package/dist/unified-config.js +155 -0
  72. package/dist/unified-config.js.map +1 -0
  73. package/dist/workflow-types.d.ts +202 -0
  74. package/dist/workflow-types.d.ts.map +1 -0
  75. package/dist/workflow-types.js +15 -0
  76. package/dist/workflow-types.js.map +1 -0
  77. package/dist/worktree.d.ts +92 -0
  78. package/dist/worktree.d.ts.map +1 -0
  79. package/dist/worktree.js +221 -0
  80. package/dist/worktree.js.map +1 -0
  81. package/package.json +57 -0
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Event logger - formats stream-json events for human-readable log output.
3
+ *
4
+ * Converts structured NDJSON events from Claude Code's stream-json output
5
+ * into concise log lines for the agent's stdout log file.
6
+ */
7
+ /**
8
+ * Format a stream-json event as a human-readable log line.
9
+ * Returns null for events that don't need logging.
10
+ */
11
+ export function formatEventForLog(event) {
12
+ switch (event.type) {
13
+ case 'system':
14
+ if (event.subtype === 'init') {
15
+ const init = event;
16
+ return `[init] Session started (model: ${init.model}, tools: ${init.tools.length})`;
17
+ }
18
+ if (event.subtype === 'teammate_spawned') {
19
+ const te = event;
20
+ return `[team] Teammate spawned: ${te.agent_name}`;
21
+ }
22
+ if (event.subtype === 'teammate_completed') {
23
+ const te = event;
24
+ const status = te.is_error ? 'FAILED' : 'completed';
25
+ return `[team] Teammate ${status}: ${te.agent_name}${te.result ? ` — ${te.result.slice(0, 200)}` : ''}`;
26
+ }
27
+ if (event.subtype === 'teammate_idle') {
28
+ const te = event;
29
+ return `[team] Teammate idle: ${te.agent_name}`;
30
+ }
31
+ if (event.subtype === 'compact_boundary') {
32
+ const se = event;
33
+ const preTokens = typeof se['compact_metadata'] === 'object' && se['compact_metadata'] !== null
34
+ ? se['compact_metadata']['pre_tokens']
35
+ : undefined;
36
+ return `[system] Context compaction${preTokens != null ? ` (pre_tokens: ${preTokens})` : ''}`;
37
+ }
38
+ if (event.subtype === 'status') {
39
+ // Status events are debug-level -- return a debug-prefixed line.
40
+ // Callers can filter by prefix if they only want info-level logs.
41
+ const se = event;
42
+ return `[debug:system] Status: ${typeof se['status'] === 'string' ? se['status'] : JSON.stringify(se['status'] ?? 'unknown')}`;
43
+ }
44
+ return null;
45
+ case 'assistant': {
46
+ const msg = event;
47
+ const textParts = msg.message.content
48
+ .filter((c) => c.type === 'text')
49
+ .map(c => c.text);
50
+ const toolParts = msg.message.content
51
+ .filter((c) => c.type === 'tool_use')
52
+ .map(c => c.name);
53
+ const parts = [];
54
+ if (textParts.length > 0) {
55
+ const text = textParts.join(' ').slice(0, 300);
56
+ parts.push(`text: "${text}${textParts.join(' ').length > 300 ? '...' : ''}"`);
57
+ }
58
+ if (toolParts.length > 0) {
59
+ parts.push(`tools: [${toolParts.join(', ')}]`);
60
+ }
61
+ return `[assistant] ${parts.join(' | ')}`;
62
+ }
63
+ case 'result': {
64
+ const r = event;
65
+ const cost = r.total_cost_usd != null ? `$${r.total_cost_usd.toFixed(4)}` : '$?';
66
+ if (r.is_error) {
67
+ return `[result] ERROR (${r.subtype}): ${r.num_turns} turns, ${cost}`;
68
+ }
69
+ const result = r.result ?? '';
70
+ return `[result] Success: ${r.num_turns} turns, ${cost} — ${result.slice(0, 200)}`;
71
+ }
72
+ case 'rate_limit_event': {
73
+ const rl = event;
74
+ const info = rl.rate_limit_info;
75
+ return `[rate_limit] ${info.status}${info.resetsAt ? ` (resets: ${info.resetsAt})` : ''}${info.rateLimitType ? ` [${info.rateLimitType}]` : ''}`;
76
+ }
77
+ // stream_event events are too granular for the human-readable log
78
+ default:
79
+ return null;
80
+ }
81
+ }
82
+ //# sourceMappingURL=event-logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-logger.js","sourceRoot":"","sources":["../src/event-logger.ts"],"names":[],"mappings":"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"}
package/dist/git.d.ts ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Shared git utilities for daemon and factory.
3
+ *
4
+ * Provides a synchronous git command runner and common branch helpers.
5
+ * Both engines (daemon and factory) implement functionally identical
6
+ * versions of these; this module is the single canonical source.
7
+ */
8
+ /** Default timeout for git CLI operations (60 seconds). */
9
+ export declare const DEFAULT_GIT_TIMEOUT_MS = 60000;
10
+ /** Structured result from a synchronous git command invocation. */
11
+ export interface GitResult {
12
+ success: boolean;
13
+ output: string;
14
+ error: string;
15
+ }
16
+ /**
17
+ * Run a git command synchronously and return a structured result.
18
+ *
19
+ * Never throws -- callers inspect `result.success` instead. All invocations
20
+ * have a configurable timeout (default 60s) to prevent the process from
21
+ * hanging indefinitely on stuck git operations.
22
+ *
23
+ * @param args Arguments to pass to `git` (e.g. `['status', '--porcelain']`).
24
+ * @param cwd Working directory for the git command.
25
+ * @param timeoutMs Timeout in milliseconds (default: {@link DEFAULT_GIT_TIMEOUT_MS}).
26
+ */
27
+ export declare function runGit(args: string[], cwd: string, timeoutMs?: number): GitResult;
28
+ /**
29
+ * Check if a local git branch exists.
30
+ *
31
+ * Uses `git show-ref --verify` which is the most reliable way to check
32
+ * for a specific local branch ref without pattern matching ambiguity.
33
+ *
34
+ * @param branchName Branch name to check (e.g. `main`, `feature/foo`).
35
+ * @param repoPath Path to the git repository.
36
+ * @returns `true` if the branch exists locally, `false` otherwise.
37
+ */
38
+ export declare function branchExists(branchName: string, repoPath: string): boolean;
39
+ //# sourceMappingURL=git.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,2DAA2D;AAC3D,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAM7C,mEAAmE;AACnE,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,MAAM,CACpB,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,EACX,SAAS,GAAE,MAA+B,GACzC,SAAS,CAiCX;AAMD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAM1E"}
package/dist/git.js ADDED
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Shared git utilities for daemon and factory.
3
+ *
4
+ * Provides a synchronous git command runner and common branch helpers.
5
+ * Both engines (daemon and factory) implement functionally identical
6
+ * versions of these; this module is the single canonical source.
7
+ */
8
+ import { execFileSync } from 'node:child_process';
9
+ // ---------------------------------------------------------------------------
10
+ // Constants
11
+ // ---------------------------------------------------------------------------
12
+ /** Default timeout for git CLI operations (60 seconds). */
13
+ export const DEFAULT_GIT_TIMEOUT_MS = 60_000;
14
+ // ---------------------------------------------------------------------------
15
+ // Core git runner
16
+ // ---------------------------------------------------------------------------
17
+ /**
18
+ * Run a git command synchronously and return a structured result.
19
+ *
20
+ * Never throws -- callers inspect `result.success` instead. All invocations
21
+ * have a configurable timeout (default 60s) to prevent the process from
22
+ * hanging indefinitely on stuck git operations.
23
+ *
24
+ * @param args Arguments to pass to `git` (e.g. `['status', '--porcelain']`).
25
+ * @param cwd Working directory for the git command.
26
+ * @param timeoutMs Timeout in milliseconds (default: {@link DEFAULT_GIT_TIMEOUT_MS}).
27
+ */
28
+ export function runGit(args, cwd, timeoutMs = DEFAULT_GIT_TIMEOUT_MS) {
29
+ try {
30
+ const output = execFileSync('git', args, {
31
+ cwd,
32
+ encoding: 'utf-8',
33
+ stdio: ['pipe', 'pipe', 'pipe'],
34
+ timeout: timeoutMs,
35
+ });
36
+ return { success: true, output: output.trim(), error: '' };
37
+ }
38
+ catch (err) {
39
+ const execErr = err;
40
+ // Detect timeout: Node kills the process and sets killed=true
41
+ if (execErr.killed || execErr.signal === 'SIGTERM') {
42
+ return {
43
+ success: false,
44
+ output: execErr.stdout?.toString() ?? '',
45
+ error: `Git operation timed out after ${timeoutMs}ms: git ${args.join(' ')}`,
46
+ };
47
+ }
48
+ return {
49
+ success: false,
50
+ output: execErr.stdout?.toString() ?? '',
51
+ error: execErr.stderr?.toString() ?? execErr.message ?? 'Unknown git error',
52
+ };
53
+ }
54
+ }
55
+ // ---------------------------------------------------------------------------
56
+ // Branch helpers
57
+ // ---------------------------------------------------------------------------
58
+ /**
59
+ * Check if a local git branch exists.
60
+ *
61
+ * Uses `git show-ref --verify` which is the most reliable way to check
62
+ * for a specific local branch ref without pattern matching ambiguity.
63
+ *
64
+ * @param branchName Branch name to check (e.g. `main`, `feature/foo`).
65
+ * @param repoPath Path to the git repository.
66
+ * @returns `true` if the branch exists locally, `false` otherwise.
67
+ */
68
+ export function branchExists(branchName, repoPath) {
69
+ const result = runGit(['show-ref', '--verify', '--quiet', `refs/heads/${branchName}`], repoPath);
70
+ return result.success;
71
+ }
72
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,2DAA2D;AAC3D,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC;AAa7C,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E;;;;;;;;;;GAUG;AACH,MAAM,UAAU,MAAM,CACpB,IAAc,EACd,GAAW,EACX,YAAoB,sBAAsB;IAE1C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE;YACvC,GAAG;YACH,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;YAC/B,OAAO,EAAE,SAAS;SACnB,CAAC,CAAC;QACH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IAC7D,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,GAMf,CAAC;QAEF,8DAA8D;QAC9D,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACnD,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;gBACxC,KAAK,EAAE,iCAAiC,SAAS,WAAW,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;aAC7E,CAAC;QACJ,CAAC;QAED,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;YACxC,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,OAAO,CAAC,OAAO,IAAI,mBAAmB;SAC5E,CAAC;IACJ,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAC,UAAkB,EAAE,QAAgB;IAC/D,MAAM,MAAM,GAAG,MAAM,CACnB,CAAC,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,UAAU,EAAE,CAAC,EAC/D,QAAQ,CACT,CAAC;IACF,OAAO,MAAM,CAAC,OAAO,CAAC;AACxB,CAAC"}
@@ -0,0 +1,20 @@
1
+ export { ApiError, CircuitBreaker, CircuitOpenError, type CircuitBreakerOptions, type CircuitState, type RetryOptions, classifyHttpStatus, forceCloseAllCircuitBreakers, getCircuitBreakers, isRetryableError, safeInterval, withRetry, } from './resilience.js';
2
+ export { createApiClient, type BaseApiConfig, type ApiClient, } from './api-client.js';
3
+ export { createTransform, mapDefinedKeys, } from './transforms.js';
4
+ export { TokenSniffingTransform, parseTokenUsageFromOutput, sumTokenUsage, type ParsedTokenUsage, } from './token-usage.js';
5
+ export { OutputEventSchema, StreamJsonParser, sendMessage, type AssistantMessage, type GenericSystemEvent, type InputJsonDelta, type OutputEvent, type RateLimitEvent, type RawStreamEvent, type ResultError, type ResultSuccess, type StreamEvent, type StreamJsonParserEvents, type SystemInitEvent, type TeammateEvent, type TextBlock, type TextDelta, type ThinkingBlock, type TokenUsage, type ToolUseBlock, type UserMessage, type UserTextMessageEvent, type UserToolResultEvent, } from './stream-json.js';
6
+ export { UUID_REGEX, loadConfigFile, productLabel, resolveBaseConfig, validateBaseConfig, type BaseConfig, type ProductEntry, } from './config.js';
7
+ export { PidLockError, ShutdownController, acquirePidLock, isProcessRunning, releasePidLock, setupSignalHandlers, type ShutdownHandler, } from './lifecycle.js';
8
+ export { DEFAULT_GIT_TIMEOUT_MS, branchExists, runGit, type GitResult, } from './git.js';
9
+ export { commitWip, createWorktreeBase, pruneWorktrees, removeWorktreeBase, worktreeExistsForBranch, type CreateWorktreeResult, } from './worktree.js';
10
+ export { buildStreamJsonArgs, createLogStream, stripClaudeCodeEnvVars, type StreamJsonArgsOptions, type StripEnvOptions, } from './spawn.js';
11
+ export { type ExecutionEngine, type EngineHealth, type ResourceUsage, } from './engine.js';
12
+ export { buildEngineConfig, loadUnifiedConfig, type EngineSection, type UnifiedConfig, } from './unified-config.js';
13
+ export { ResourceGovernor, type ResourceGovernorConfig, type ResourceUtilization, } from './resource-governor.js';
14
+ export { formatEventForLog, } from './event-logger.js';
15
+ export { ActivityTracker, type ActivitySnapshot, } from './activity-tracker.js';
16
+ export { buildOtelEnv, type BuildOtelEnvOptions, } from './otel-env.js';
17
+ export { ESCALATION_REASONS, type EscalationReasonType, } from './escalation-types.js';
18
+ export { extractSessionKey, pruneOldLogs, scanLogDirectory, type LogFileInfo, type LogManagerDeps, type LogRetentionConfig, type PruneResult, } from './log-manager.js';
19
+ export type { GuardEnforcement, TriggerEvent, TriggerActionType, WorkflowStage, WorkflowTransition, DecisionPath, WorkflowGuard, WorkflowTrigger, GuardConditionNode, ContextDirective, TriggerExecutionTarget, FactoryTemplateContext, ConditionEvalResult, GuardEvalResult, TransitionGuardResult, TransitionEvaluationRecord, TransitionBlockRecord, EvaluationContext, } from './workflow-types.js';
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,36 @@
1
+ // @telora/daemon-core - Shared infrastructure for Telora daemon and factory
2
+ // This package provides the common foundation that both engines build upon.
3
+ export { ApiError, CircuitBreaker, CircuitOpenError, classifyHttpStatus, forceCloseAllCircuitBreakers, getCircuitBreakers, isRetryableError, safeInterval, withRetry, } from './resilience.js';
4
+ // API client factory (creates engine-specific API clients)
5
+ export { createApiClient, } from './api-client.js';
6
+ // Transform utilities (camelCase <-> snake_case key mapping)
7
+ export { createTransform, mapDefinedKeys, } from './transforms.js';
8
+ // Token usage parsing (NDJSON result-event sniffing)
9
+ export { TokenSniffingTransform, parseTokenUsageFromOutput, sumTokenUsage, } from './token-usage.js';
10
+ // Stream-JSON protocol (NDJSON parser, schemas, writer for Claude Code sessions)
11
+ export { OutputEventSchema, StreamJsonParser, sendMessage, } from './stream-json.js';
12
+ // Shared config infrastructure (BaseConfig, loaders, validators)
13
+ export { UUID_REGEX, loadConfigFile, productLabel, resolveBaseConfig, validateBaseConfig, } from './config.js';
14
+ // Process lifecycle (PID locking, signal handling, shutdown coordination)
15
+ export { PidLockError, ShutdownController, acquirePidLock, isProcessRunning, releasePidLock, setupSignalHandlers, } from './lifecycle.js';
16
+ // Git utilities (synchronous git runner, branch helpers)
17
+ export { DEFAULT_GIT_TIMEOUT_MS, branchExists, runGit, } from './git.js';
18
+ // Worktree operations (prune, WIP commit, remove, create)
19
+ export { commitWip, createWorktreeBase, pruneWorktrees, removeWorktreeBase, worktreeExistsForBranch, } from './worktree.js';
20
+ // Spawn utilities (CLI arg builder, env stripping, log streams)
21
+ export { buildStreamJsonArgs, createLogStream, stripClaudeCodeEnvVars, } from './spawn.js';
22
+ // Unified config (loads single daemon.json with engine sections)
23
+ export { buildEngineConfig, loadUnifiedConfig, } from './unified-config.js';
24
+ // Resource governor (global concurrency limiter for Claude Code processes)
25
+ export { ResourceGovernor, } from './resource-governor.js';
26
+ // Event logger (human-readable log formatter for StreamJsonParser events)
27
+ export { formatEventForLog, } from './event-logger.js';
28
+ // Activity tracking (session observer for StreamJsonParser)
29
+ export { ActivityTracker, } from './activity-tracker.js';
30
+ // OTEL environment variable builder (shared OTLP env injection for spawned processes)
31
+ export { buildOtelEnv, } from './otel-env.js';
32
+ // Escalation reason types (shared enum/constants for agent_escalations.reason_type)
33
+ export { ESCALATION_REASONS, } from './escalation-types.js';
34
+ // Log manager (retention policy and pruning for agent log files)
35
+ export { extractSessionKey, pruneOldLogs, scanLogDirectory, } from './log-manager.js';
36
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,4EAA4E;AAE5E,OAAO,EACL,QAAQ,EACR,cAAc,EACd,gBAAgB,EAIhB,kBAAkB,EAClB,4BAA4B,EAC5B,kBAAkB,EAClB,gBAAgB,EAChB,YAAY,EACZ,SAAS,GACV,MAAM,iBAAiB,CAAC;AAEzB,2DAA2D;AAC3D,OAAO,EACL,eAAe,GAGhB,MAAM,iBAAiB,CAAC;AAEzB,6DAA6D;AAC7D,OAAO,EACL,eAAe,EACf,cAAc,GACf,MAAM,iBAAiB,CAAC;AAEzB,qDAAqD;AACrD,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,aAAa,GAEd,MAAM,kBAAkB,CAAC;AAE1B,iFAAiF;AACjF,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,WAAW,GAqBZ,MAAM,kBAAkB,CAAC;AAE1B,iEAAiE;AACjE,OAAO,EACL,UAAU,EACV,cAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,GAGnB,MAAM,aAAa,CAAC;AAErB,0EAA0E;AAC1E,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,mBAAmB,GAEpB,MAAM,gBAAgB,CAAC;AAExB,yDAAyD;AACzD,OAAO,EACL,sBAAsB,EACtB,YAAY,EACZ,MAAM,GAEP,MAAM,UAAU,CAAC;AAElB,0DAA0D;AAC1D,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,uBAAuB,GAExB,MAAM,eAAe,CAAC;AAEvB,gEAAgE;AAChE,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,sBAAsB,GAGvB,MAAM,YAAY,CAAC;AASpB,iEAAiE;AACjE,OAAO,EACL,iBAAiB,EACjB,iBAAiB,GAGlB,MAAM,qBAAqB,CAAC;AAE7B,2EAA2E;AAC3E,OAAO,EACL,gBAAgB,GAGjB,MAAM,wBAAwB,CAAC;AAEhC,0EAA0E;AAC1E,OAAO,EACL,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAE3B,4DAA4D;AAC5D,OAAO,EACL,eAAe,GAEhB,MAAM,uBAAuB,CAAC;AAE/B,sFAAsF;AACtF,OAAO,EACL,YAAY,GAEb,MAAM,eAAe,CAAC;AAEvB,oFAAoF;AACpF,OAAO,EACL,kBAAkB,GAEnB,MAAM,uBAAuB,CAAC;AAE/B,iEAAiE;AACjE,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,GAKjB,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Process lifecycle utilities shared by daemon and factory.
3
+ *
4
+ * Provides PID file locking (acquire/release with stale detection),
5
+ * process liveness checks, signal handler registration with debounce,
6
+ * and a ShutdownController for coordinating graceful shutdown across modules.
7
+ */
8
+ /**
9
+ * Check if a process with the given PID is currently running.
10
+ *
11
+ * Uses the POSIX `kill(pid, 0)` technique: sending signal 0 checks for
12
+ * process existence without actually delivering a signal. Returns false
13
+ * when the process does not exist or we lack permission to signal it.
14
+ */
15
+ export declare function isProcessRunning(pid: number): boolean;
16
+ /**
17
+ * Error thrown when another instance already holds the PID lock.
18
+ */
19
+ export declare class PidLockError extends Error {
20
+ /** PID of the process that already holds the lock. */
21
+ readonly existingPid: number;
22
+ /** Path to the PID file. */
23
+ readonly pidFilePath: string;
24
+ constructor(existingPid: number, pidFilePath: string);
25
+ }
26
+ /**
27
+ * Acquire an exclusive PID file lock.
28
+ *
29
+ * - If the PID file already exists and the recorded process is alive,
30
+ * throws {@link PidLockError}.
31
+ * - If the PID file exists but the process is no longer running (stale),
32
+ * removes the stale file and proceeds.
33
+ * - Creates the parent directory if it does not exist.
34
+ * - Writes the current process PID to the file.
35
+ *
36
+ * @param pidFilePath - Absolute path to the PID file.
37
+ * @param logger - Optional logging callbacks. Defaults to console.
38
+ */
39
+ export declare function acquirePidLock(pidFilePath: string, logger?: {
40
+ log?: (msg: string) => void;
41
+ }): void;
42
+ /**
43
+ * Release the PID file lock.
44
+ *
45
+ * Only removes the file if it still contains the current process's PID.
46
+ * This prevents a newly-started instance's PID file from being removed
47
+ * by a dying predecessor.
48
+ *
49
+ * Errors are silently caught -- this is best-effort cleanup.
50
+ *
51
+ * @param pidFilePath - Absolute path to the PID file.
52
+ */
53
+ export declare function releasePidLock(pidFilePath: string): void;
54
+ /** Callback invoked when a termination signal is received. */
55
+ export type ShutdownHandler = (signal: string) => void | Promise<void>;
56
+ /**
57
+ * Register SIGTERM and SIGINT handlers that delegate to the provided callback.
58
+ *
59
+ * The handler is debounced: rapid repeated signals (e.g. user pressing Ctrl+C
60
+ * multiple times) will only invoke the callback once.
61
+ *
62
+ * @param onShutdown - Async-safe callback triggered on the first signal.
63
+ * The signal name ('SIGTERM' or 'SIGINT') is passed as an argument.
64
+ * @returns A cleanup function that removes the registered handlers.
65
+ */
66
+ export declare function setupSignalHandlers(onShutdown: ShutdownHandler): () => void;
67
+ /**
68
+ * Coordinates graceful shutdown state across modules.
69
+ *
70
+ * Both the daemon and factory use a boolean flag to prevent new work from
71
+ * being started once shutdown begins. ShutdownController provides this flag
72
+ * along with a simple callback-based notification mechanism.
73
+ *
74
+ * Usage:
75
+ * ```ts
76
+ * const ctl = new ShutdownController();
77
+ * ctl.onShutdown(() => stopAcceptingWork());
78
+ *
79
+ * // In the signal handler:
80
+ * ctl.requestShutdown();
81
+ *
82
+ * // In polling loops:
83
+ * if (ctl.isShuttingDown) return;
84
+ * ```
85
+ */
86
+ export declare class ShutdownController {
87
+ private _shuttingDown;
88
+ private readonly _callbacks;
89
+ /** Whether shutdown has been requested. */
90
+ get isShuttingDown(): boolean;
91
+ /**
92
+ * Register a callback to be invoked when shutdown is requested.
93
+ * If shutdown has already been requested, the callback fires immediately.
94
+ */
95
+ onShutdown(callback: () => void | Promise<void>): void;
96
+ /**
97
+ * Signal that shutdown has started.
98
+ *
99
+ * Sets the flag and invokes all registered callbacks (in order).
100
+ * Calling this multiple times is safe -- subsequent calls are no-ops.
101
+ */
102
+ requestShutdown(): void;
103
+ }
104
+ //# sourceMappingURL=lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOrD;AAMD;;GAEG;AACH,qBAAa,YAAa,SAAQ,KAAK;IACrC,sDAAsD;IACtD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,4BAA4B;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;gBAEjB,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;CAUrD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,GACvC,IAAI,CAoBN;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAWxD;AAMD,8DAA8D;AAC9D,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEvE;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,eAAe,GAAG,MAAM,IAAI,CAoB3E;AAMD;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAyC;IAEpE,2CAA2C;IAC3C,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED;;;OAGG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAQtD;;;;;OAKG;IACH,eAAe,IAAI,IAAI;CAYxB"}
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Process lifecycle utilities shared by daemon and factory.
3
+ *
4
+ * Provides PID file locking (acquire/release with stale detection),
5
+ * process liveness checks, signal handler registration with debounce,
6
+ * and a ShutdownController for coordinating graceful shutdown across modules.
7
+ */
8
+ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
9
+ import { resolve } from 'node:path';
10
+ // ---------------------------------------------------------------------------
11
+ // Process liveness check
12
+ // ---------------------------------------------------------------------------
13
+ /**
14
+ * Check if a process with the given PID is currently running.
15
+ *
16
+ * Uses the POSIX `kill(pid, 0)` technique: sending signal 0 checks for
17
+ * process existence without actually delivering a signal. Returns false
18
+ * when the process does not exist or we lack permission to signal it.
19
+ */
20
+ export function isProcessRunning(pid) {
21
+ try {
22
+ process.kill(pid, 0);
23
+ return true;
24
+ }
25
+ catch {
26
+ return false;
27
+ }
28
+ }
29
+ // ---------------------------------------------------------------------------
30
+ // PID file locking
31
+ // ---------------------------------------------------------------------------
32
+ /**
33
+ * Error thrown when another instance already holds the PID lock.
34
+ */
35
+ export class PidLockError extends Error {
36
+ /** PID of the process that already holds the lock. */
37
+ existingPid;
38
+ /** Path to the PID file. */
39
+ pidFilePath;
40
+ constructor(existingPid, pidFilePath) {
41
+ super(`Another process is already running (PID ${existingPid}). ` +
42
+ `PID file: ${pidFilePath}. ` +
43
+ `To force restart, kill the existing process first: kill ${existingPid}`);
44
+ this.name = 'PidLockError';
45
+ this.existingPid = existingPid;
46
+ this.pidFilePath = pidFilePath;
47
+ }
48
+ }
49
+ /**
50
+ * Acquire an exclusive PID file lock.
51
+ *
52
+ * - If the PID file already exists and the recorded process is alive,
53
+ * throws {@link PidLockError}.
54
+ * - If the PID file exists but the process is no longer running (stale),
55
+ * removes the stale file and proceeds.
56
+ * - Creates the parent directory if it does not exist.
57
+ * - Writes the current process PID to the file.
58
+ *
59
+ * @param pidFilePath - Absolute path to the PID file.
60
+ * @param logger - Optional logging callbacks. Defaults to console.
61
+ */
62
+ export function acquirePidLock(pidFilePath, logger) {
63
+ const log = logger?.log ?? console.log;
64
+ if (existsSync(pidFilePath)) {
65
+ const existingPid = parseInt(readFileSync(pidFilePath, 'utf-8').trim(), 10);
66
+ if (!isNaN(existingPid) && isProcessRunning(existingPid)) {
67
+ throw new PidLockError(existingPid, pidFilePath);
68
+ }
69
+ // Stale PID file -- previous process crashed without cleanup
70
+ log(`Removing stale PID file (PID ${existingPid} is no longer running)`);
71
+ unlinkSync(pidFilePath);
72
+ }
73
+ // Ensure parent directory exists
74
+ const dir = resolve(pidFilePath, '..');
75
+ if (!existsSync(dir)) {
76
+ mkdirSync(dir, { recursive: true });
77
+ }
78
+ writeFileSync(pidFilePath, String(process.pid) + '\n');
79
+ }
80
+ /**
81
+ * Release the PID file lock.
82
+ *
83
+ * Only removes the file if it still contains the current process's PID.
84
+ * This prevents a newly-started instance's PID file from being removed
85
+ * by a dying predecessor.
86
+ *
87
+ * Errors are silently caught -- this is best-effort cleanup.
88
+ *
89
+ * @param pidFilePath - Absolute path to the PID file.
90
+ */
91
+ export function releasePidLock(pidFilePath) {
92
+ try {
93
+ if (existsSync(pidFilePath)) {
94
+ const contents = readFileSync(pidFilePath, 'utf-8').trim();
95
+ if (parseInt(contents, 10) === process.pid) {
96
+ unlinkSync(pidFilePath);
97
+ }
98
+ }
99
+ }
100
+ catch {
101
+ // Best-effort cleanup -- file may already be removed
102
+ }
103
+ }
104
+ /**
105
+ * Register SIGTERM and SIGINT handlers that delegate to the provided callback.
106
+ *
107
+ * The handler is debounced: rapid repeated signals (e.g. user pressing Ctrl+C
108
+ * multiple times) will only invoke the callback once.
109
+ *
110
+ * @param onShutdown - Async-safe callback triggered on the first signal.
111
+ * The signal name ('SIGTERM' or 'SIGINT') is passed as an argument.
112
+ * @returns A cleanup function that removes the registered handlers.
113
+ */
114
+ export function setupSignalHandlers(onShutdown) {
115
+ let invoked = false;
116
+ const handler = (signal) => {
117
+ if (invoked)
118
+ return;
119
+ invoked = true;
120
+ // Fire-and-forget: the shutdown callback manages its own exit
121
+ void Promise.resolve(onShutdown(signal));
122
+ };
123
+ const sigintHandler = () => handler('SIGINT');
124
+ const sigtermHandler = () => handler('SIGTERM');
125
+ process.on('SIGINT', sigintHandler);
126
+ process.on('SIGTERM', sigtermHandler);
127
+ return () => {
128
+ process.removeListener('SIGINT', sigintHandler);
129
+ process.removeListener('SIGTERM', sigtermHandler);
130
+ };
131
+ }
132
+ // ---------------------------------------------------------------------------
133
+ // Shutdown coordination
134
+ // ---------------------------------------------------------------------------
135
+ /**
136
+ * Coordinates graceful shutdown state across modules.
137
+ *
138
+ * Both the daemon and factory use a boolean flag to prevent new work from
139
+ * being started once shutdown begins. ShutdownController provides this flag
140
+ * along with a simple callback-based notification mechanism.
141
+ *
142
+ * Usage:
143
+ * ```ts
144
+ * const ctl = new ShutdownController();
145
+ * ctl.onShutdown(() => stopAcceptingWork());
146
+ *
147
+ * // In the signal handler:
148
+ * ctl.requestShutdown();
149
+ *
150
+ * // In polling loops:
151
+ * if (ctl.isShuttingDown) return;
152
+ * ```
153
+ */
154
+ export class ShutdownController {
155
+ _shuttingDown = false;
156
+ _callbacks = [];
157
+ /** Whether shutdown has been requested. */
158
+ get isShuttingDown() {
159
+ return this._shuttingDown;
160
+ }
161
+ /**
162
+ * Register a callback to be invoked when shutdown is requested.
163
+ * If shutdown has already been requested, the callback fires immediately.
164
+ */
165
+ onShutdown(callback) {
166
+ if (this._shuttingDown) {
167
+ void Promise.resolve(callback());
168
+ return;
169
+ }
170
+ this._callbacks.push(callback);
171
+ }
172
+ /**
173
+ * Signal that shutdown has started.
174
+ *
175
+ * Sets the flag and invokes all registered callbacks (in order).
176
+ * Calling this multiple times is safe -- subsequent calls are no-ops.
177
+ */
178
+ requestShutdown() {
179
+ if (this._shuttingDown)
180
+ return;
181
+ this._shuttingDown = true;
182
+ for (const cb of this._callbacks) {
183
+ try {
184
+ void Promise.resolve(cb());
185
+ }
186
+ catch {
187
+ // Swallow errors -- shutdown must not be interrupted
188
+ }
189
+ }
190
+ }
191
+ }
192
+ //# sourceMappingURL=lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACzF,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,8EAA8E;AAC9E,yBAAyB;AACzB,8EAA8E;AAE9E;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E;;GAEG;AACH,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,sDAAsD;IAC7C,WAAW,CAAS;IAC7B,4BAA4B;IACnB,WAAW,CAAS;IAE7B,YAAY,WAAmB,EAAE,WAAmB;QAClD,KAAK,CACH,2CAA2C,WAAW,KAAK;YAC3D,aAAa,WAAW,IAAI;YAC5B,2DAA2D,WAAW,EAAE,CACzE,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAC3B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,CAAC;CACF;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,cAAc,CAC5B,WAAmB,EACnB,MAAwC;IAExC,MAAM,GAAG,GAAG,MAAM,EAAE,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IAEvC,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC5B,MAAM,WAAW,GAAG,QAAQ,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAC5E,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,gBAAgB,CAAC,WAAW,CAAC,EAAE,CAAC;YACzD,MAAM,IAAI,YAAY,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;QACnD,CAAC;QACD,6DAA6D;QAC7D,GAAG,CAAC,gCAAgC,WAAW,wBAAwB,CAAC,CAAC;QACzE,UAAU,CAAC,WAAW,CAAC,CAAC;IAC1B,CAAC;IAED,iCAAiC;IACjC,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IACvC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,aAAa,CAAC,WAAW,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;AACzD,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAAC,WAAmB;IAChD,IAAI,CAAC;QACH,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAC5B,MAAM,QAAQ,GAAG,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;YAC3D,IAAI,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,KAAK,OAAO,CAAC,GAAG,EAAE,CAAC;gBAC3C,UAAU,CAAC,WAAW,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,qDAAqD;IACvD,CAAC;AACH,CAAC;AASD;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CAAC,UAA2B;IAC7D,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,MAAM,OAAO,GAAG,CAAC,MAAc,EAAQ,EAAE;QACvC,IAAI,OAAO;YAAE,OAAO;QACpB,OAAO,GAAG,IAAI,CAAC;QACf,8DAA8D;QAC9D,KAAK,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3C,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC9C,MAAM,cAAc,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhD,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;IACpC,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IAEtC,OAAO,GAAG,EAAE;QACV,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;QAChD,OAAO,CAAC,cAAc,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IACpD,CAAC,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,wBAAwB;AACxB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,kBAAkB;IACrB,aAAa,GAAG,KAAK,CAAC;IACb,UAAU,GAAsC,EAAE,CAAC;IAEpE,2CAA2C;IAC3C,IAAI,cAAc;QAChB,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED;;;OAGG;IACH,UAAU,CAAC,QAAoC;QAC7C,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,KAAK,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;YACjC,OAAO;QACT,CAAC;QACD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACjC,CAAC;IAED;;;;;OAKG;IACH,eAAe;QACb,IAAI,IAAI,CAAC,aAAa;YAAE,OAAO;QAC/B,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAE1B,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACjC,IAAI,CAAC;gBACH,KAAK,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;YAC7B,CAAC;YAAC,MAAM,CAAC;gBACP,qDAAqD;YACvD,CAAC;QACH,CAAC;IACH,CAAC;CACF"}