@a5c-ai/genty-runtime 5.1.1-staging.5e92128884d3
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/README.md +69 -0
- package/dist/apiResult.d.ts +19 -0
- package/dist/apiResult.d.ts.map +1 -0
- package/dist/apiResult.js +16 -0
- package/dist/background/state.d.ts +20 -0
- package/dist/background/state.d.ts.map +1 -0
- package/dist/background/state.js +52 -0
- package/dist/backgroundProcessRegistry.d.ts +124 -0
- package/dist/backgroundProcessRegistry.d.ts.map +1 -0
- package/dist/backgroundProcessRegistry.js +427 -0
- package/dist/cost/claudeCodeParser.d.ts +81 -0
- package/dist/cost/claudeCodeParser.d.ts.map +1 -0
- package/dist/cost/claudeCodeParser.js +232 -0
- package/dist/cost/collector.d.ts +42 -0
- package/dist/cost/collector.d.ts.map +1 -0
- package/dist/cost/collector.js +105 -0
- package/dist/cost/effectCost.d.ts +23 -0
- package/dist/cost/effectCost.d.ts.map +1 -0
- package/dist/cost/effectCost.js +26 -0
- package/dist/cost/index.d.ts +19 -0
- package/dist/cost/index.d.ts.map +1 -0
- package/dist/cost/index.js +39 -0
- package/dist/cost/journal.d.ts +40 -0
- package/dist/cost/journal.d.ts.map +1 -0
- package/dist/cost/journal.js +137 -0
- package/dist/cost/types.d.ts +164 -0
- package/dist/cost/types.d.ts.map +1 -0
- package/dist/cost/types.js +228 -0
- package/dist/daemon/automationExecutor.d.ts +16 -0
- package/dist/daemon/automationExecutor.d.ts.map +1 -0
- package/dist/daemon/automationExecutor.js +222 -0
- package/dist/daemon/config.d.ts +8 -0
- package/dist/daemon/config.d.ts.map +1 -0
- package/dist/daemon/config.js +245 -0
- package/dist/daemon/daemonLog.d.ts +30 -0
- package/dist/daemon/daemonLog.d.ts.map +1 -0
- package/dist/daemon/daemonLog.js +140 -0
- package/dist/daemon/durableQueue.d.ts +41 -0
- package/dist/daemon/durableQueue.d.ts.map +1 -0
- package/dist/daemon/durableQueue.js +183 -0
- package/dist/daemon/fileWatcher.d.ts +9 -0
- package/dist/daemon/fileWatcher.d.ts.map +1 -0
- package/dist/daemon/fileWatcher.js +144 -0
- package/dist/daemon/index.d.ts +15 -0
- package/dist/daemon/index.d.ts.map +1 -0
- package/dist/daemon/index.js +25 -0
- package/dist/daemon/lifecycle.d.ts +13 -0
- package/dist/daemon/lifecycle.d.ts.map +1 -0
- package/dist/daemon/lifecycle.js +320 -0
- package/dist/daemon/loop.d.ts +27 -0
- package/dist/daemon/loop.d.ts.map +1 -0
- package/dist/daemon/loop.js +387 -0
- package/dist/daemon/timerScheduler.d.ts +13 -0
- package/dist/daemon/timerScheduler.d.ts.map +1 -0
- package/dist/daemon/timerScheduler.js +212 -0
- package/dist/daemon/types.d.ts +122 -0
- package/dist/daemon/types.d.ts.map +1 -0
- package/dist/daemon/types.js +25 -0
- package/dist/daemon/webhookListener.d.ts +6 -0
- package/dist/daemon/webhookListener.d.ts.map +1 -0
- package/dist/daemon/webhookListener.js +132 -0
- package/dist/execution/index.d.ts +10 -0
- package/dist/execution/index.d.ts.map +1 -0
- package/dist/execution/index.js +20 -0
- package/dist/execution/modes/docker.d.ts +26 -0
- package/dist/execution/modes/docker.d.ts.map +1 -0
- package/dist/execution/modes/docker.js +183 -0
- package/dist/execution/modes/index.d.ts +10 -0
- package/dist/execution/modes/index.d.ts.map +1 -0
- package/dist/execution/modes/index.js +14 -0
- package/dist/execution/modes/kubernetes.d.ts +46 -0
- package/dist/execution/modes/kubernetes.d.ts.map +1 -0
- package/dist/execution/modes/kubernetes.js +334 -0
- package/dist/execution/modes/local.d.ts +23 -0
- package/dist/execution/modes/local.d.ts.map +1 -0
- package/dist/execution/modes/local.js +117 -0
- package/dist/execution/modes/ssh.d.ts +23 -0
- package/dist/execution/modes/ssh.d.ts.map +1 -0
- package/dist/execution/modes/ssh.js +144 -0
- package/dist/execution/policy.d.ts +15 -0
- package/dist/execution/policy.d.ts.map +1 -0
- package/dist/execution/policy.js +121 -0
- package/dist/execution/provider.d.ts +32 -0
- package/dist/execution/provider.d.ts.map +1 -0
- package/dist/execution/provider.js +90 -0
- package/dist/execution/types.d.ts +189 -0
- package/dist/execution/types.d.ts.map +1 -0
- package/dist/execution/types.js +9 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/observability/diagnostics.d.ts +25 -0
- package/dist/observability/diagnostics.d.ts.map +1 -0
- package/dist/observability/diagnostics.js +98 -0
- package/dist/observability/health.d.ts +19 -0
- package/dist/observability/health.d.ts.map +1 -0
- package/dist/observability/health.js +145 -0
- package/dist/observability/index.d.ts +7 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +25 -0
- package/dist/observability/runStatus.d.ts +44 -0
- package/dist/observability/runStatus.d.ts.map +1 -0
- package/dist/observability/runStatus.js +169 -0
- package/dist/observability/timeline.d.ts +11 -0
- package/dist/observability/timeline.d.ts.map +1 -0
- package/dist/observability/timeline.js +176 -0
- package/dist/observability/types.d.ts +65 -0
- package/dist/observability/types.d.ts.map +1 -0
- package/dist/observability/types.js +8 -0
- package/dist/observability/webhooks.d.ts +68 -0
- package/dist/observability/webhooks.d.ts.map +1 -0
- package/dist/observability/webhooks.js +132 -0
- package/dist/resources/budget-tracker.d.ts +56 -0
- package/dist/resources/budget-tracker.d.ts.map +1 -0
- package/dist/resources/budget-tracker.js +131 -0
- package/dist/resources/concurrency-guard.d.ts +55 -0
- package/dist/resources/concurrency-guard.d.ts.map +1 -0
- package/dist/resources/concurrency-guard.js +132 -0
- package/dist/resources/index.d.ts +12 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +20 -0
- package/dist/resources/manager.d.ts +52 -0
- package/dist/resources/manager.d.ts.map +1 -0
- package/dist/resources/manager.js +150 -0
- package/dist/resources/timeout-cascade.d.ts +56 -0
- package/dist/resources/timeout-cascade.d.ts.map +1 -0
- package/dist/resources/timeout-cascade.js +145 -0
- package/dist/resources/types.d.ts +130 -0
- package/dist/resources/types.d.ts.map +1 -0
- package/dist/resources/types.js +9 -0
- package/dist/session/context.d.ts +22 -0
- package/dist/session/context.d.ts.map +1 -0
- package/dist/session/context.js +113 -0
- package/dist/session/continuityState.d.ts +39 -0
- package/dist/session/continuityState.d.ts.map +1 -0
- package/dist/session/continuityState.js +164 -0
- package/dist/session/cost.d.ts +63 -0
- package/dist/session/cost.d.ts.map +1 -0
- package/dist/session/cost.js +194 -0
- package/dist/session/discovery.d.ts +22 -0
- package/dist/session/discovery.d.ts.map +1 -0
- package/dist/session/discovery.js +35 -0
- package/dist/session/history.d.ts +30 -0
- package/dist/session/history.d.ts.map +1 -0
- package/dist/session/history.js +143 -0
- package/dist/session/index.d.ts +20 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +78 -0
- package/dist/session/memoryExtraction.d.ts +65 -0
- package/dist/session/memoryExtraction.d.ts.map +1 -0
- package/dist/session/memoryExtraction.js +201 -0
- package/dist/session/parse.d.ts +45 -0
- package/dist/session/parse.d.ts.map +1 -0
- package/dist/session/parse.js +170 -0
- package/dist/session/persistence.d.ts +46 -0
- package/dist/session/persistence.d.ts.map +1 -0
- package/dist/session/persistence.js +180 -0
- package/dist/session/types.d.ts +267 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +45 -0
- package/dist/session/write.d.ts +61 -0
- package/dist/session/write.d.ts.map +1 -0
- package/dist/session/write.js +213 -0
- package/dist/shellInvocation.d.ts +6 -0
- package/dist/shellInvocation.d.ts.map +1 -0
- package/dist/shellInvocation.js +8 -0
- package/dist/shellInvocation.test.d.ts +2 -0
- package/dist/shellInvocation.test.d.ts.map +1 -0
- package/dist/shellInvocation.test.js +18 -0
- package/dist/telemetry/audit-log.d.ts +56 -0
- package/dist/telemetry/audit-log.d.ts.map +1 -0
- package/dist/telemetry/audit-log.js +59 -0
- package/dist/telemetry/exporters.d.ts +35 -0
- package/dist/telemetry/exporters.d.ts.map +1 -0
- package/dist/telemetry/exporters.js +141 -0
- package/dist/telemetry/index.d.ts +12 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +25 -0
- package/dist/telemetry/provider.d.ts +57 -0
- package/dist/telemetry/provider.d.ts.map +1 -0
- package/dist/telemetry/provider.js +261 -0
- package/dist/telemetry/span-tree.d.ts +46 -0
- package/dist/telemetry/span-tree.d.ts.map +1 -0
- package/dist/telemetry/span-tree.js +93 -0
- package/dist/telemetry/traceContext.d.ts +10 -0
- package/dist/telemetry/traceContext.d.ts.map +1 -0
- package/dist/telemetry/traceContext.js +43 -0
- package/dist/telemetry/types.d.ts +109 -0
- package/dist/telemetry/types.d.ts.map +1 -0
- package/dist/telemetry/types.js +21 -0
- package/package.json +98 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared automation-rule execution path for daemon timer and webhook triggers.
|
|
4
|
+
*/
|
|
5
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.executeAutomationTrigger = executeAutomationTrigger;
|
|
10
|
+
const node_fs_1 = require("node:fs");
|
|
11
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
12
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
13
|
+
const node_crypto_1 = require("node:crypto");
|
|
14
|
+
const DEFAULT_BACKLOG_FILE_PATH = process.env.KANBAN_BACKLOG_FILE
|
|
15
|
+
?? node_path_1.default.join(node_os_1.default.homedir(), ".a5c", "kanban-backlog.json");
|
|
16
|
+
function cloneRecord(value) {
|
|
17
|
+
return value ? { ...value } : undefined;
|
|
18
|
+
}
|
|
19
|
+
async function readAutomationStorage(backlogFilePath) {
|
|
20
|
+
try {
|
|
21
|
+
const raw = await node_fs_1.promises.readFile(backlogFilePath, "utf8");
|
|
22
|
+
return JSON.parse(raw);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
const errno = error;
|
|
26
|
+
if (errno.code === "ENOENT") {
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async function writeAutomationStorage(backlogFilePath, payload) {
|
|
33
|
+
await node_fs_1.promises.mkdir(node_path_1.default.dirname(backlogFilePath), { recursive: true });
|
|
34
|
+
await node_fs_1.promises.writeFile(backlogFilePath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
35
|
+
}
|
|
36
|
+
function createTriggerMetadata(rule) {
|
|
37
|
+
if (rule.trigger.type === "timer") {
|
|
38
|
+
return {
|
|
39
|
+
triggerType: "timer",
|
|
40
|
+
cron: rule.trigger.cron,
|
|
41
|
+
timezone: rule.trigger.timezone,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
triggerType: "webhook",
|
|
46
|
+
port: rule.trigger.port,
|
|
47
|
+
path: rule.trigger.path ?? "/trigger",
|
|
48
|
+
method: rule.trigger.method ?? "POST",
|
|
49
|
+
sourceEvent: rule.trigger.sourceEvent,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function nextIssueNumber(project, issues) {
|
|
53
|
+
const prefix = `${project.key}-AUTO-`;
|
|
54
|
+
return issues.reduce((max, issue) => {
|
|
55
|
+
if (!issue.key.startsWith(prefix)) {
|
|
56
|
+
return max;
|
|
57
|
+
}
|
|
58
|
+
const rawNumber = issue.key.slice(prefix.length);
|
|
59
|
+
const parsed = Number.parseInt(rawNumber, 10);
|
|
60
|
+
return Number.isFinite(parsed) ? Math.max(max, parsed) : max;
|
|
61
|
+
}, 0) + 1;
|
|
62
|
+
}
|
|
63
|
+
function resolveProjectCollections(project, template) {
|
|
64
|
+
const labelsById = new Map(project.labels.map((label) => [label.id, label]));
|
|
65
|
+
const assigneesById = new Map(project.assignees.map((assignee) => [assignee.id, assignee]));
|
|
66
|
+
return {
|
|
67
|
+
labels: (template.labelIds ?? [])
|
|
68
|
+
.map((labelId) => labelsById.get(labelId))
|
|
69
|
+
.filter((label) => Boolean(label)),
|
|
70
|
+
assignees: (template.assigneeIds ?? [])
|
|
71
|
+
.map((assigneeId) => assigneesById.get(assigneeId))
|
|
72
|
+
.filter((assignee) => Boolean(assignee)),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function resolveIssueSource(rule) {
|
|
76
|
+
if (rule.template.issueSource) {
|
|
77
|
+
return { ...rule.template.issueSource };
|
|
78
|
+
}
|
|
79
|
+
if (rule.source.kind === "config-file") {
|
|
80
|
+
return {
|
|
81
|
+
kind: "file",
|
|
82
|
+
path: rule.source.path,
|
|
83
|
+
externalId: rule.source.externalId ?? rule.id,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
kind: "run-derived",
|
|
88
|
+
externalId: rule.source.externalId ?? rule.id,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function createAcceptanceCriteria(issueId, criteria) {
|
|
92
|
+
return (criteria ?? []).map((title, index) => ({
|
|
93
|
+
id: `${issueId}-ac-${index + 1}`,
|
|
94
|
+
title,
|
|
95
|
+
satisfied: false,
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
function createDecomposition(issueId, items) {
|
|
99
|
+
return (items ?? []).map((item, index) => ({
|
|
100
|
+
id: `${issueId}-decomp-${index + 1}`,
|
|
101
|
+
title: item.title,
|
|
102
|
+
kind: item.kind,
|
|
103
|
+
status: item.status,
|
|
104
|
+
issueId: undefined,
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
function createAutomationIssue(rule, project, issues, now) {
|
|
108
|
+
const sequence = nextIssueNumber(project, issues);
|
|
109
|
+
const issueKey = `${project.key}-AUTO-${String(sequence).padStart(3, "0")}`;
|
|
110
|
+
const issueId = issueKey;
|
|
111
|
+
const { labels, assignees } = resolveProjectCollections(project, rule.template);
|
|
112
|
+
return {
|
|
113
|
+
id: issueId,
|
|
114
|
+
key: issueKey,
|
|
115
|
+
projectId: rule.target.projectId,
|
|
116
|
+
title: rule.template.title,
|
|
117
|
+
summary: rule.template.summary,
|
|
118
|
+
description: rule.template.description,
|
|
119
|
+
status: rule.template.status ?? "backlog",
|
|
120
|
+
priority: rule.template.priority ?? "medium",
|
|
121
|
+
labels,
|
|
122
|
+
assignees,
|
|
123
|
+
dependencies: [],
|
|
124
|
+
acceptanceCriteria: createAcceptanceCriteria(issueId, rule.template.acceptanceCriteria),
|
|
125
|
+
decomposition: createDecomposition(issueId, rule.template.decomposition),
|
|
126
|
+
childIssueIds: [],
|
|
127
|
+
createdAt: now,
|
|
128
|
+
updatedAt: now,
|
|
129
|
+
source: resolveIssueSource(rule),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function createExecutionRecord(rule, event, now, overrides) {
|
|
133
|
+
return {
|
|
134
|
+
id: `automation-exec-${(0, node_crypto_1.randomUUID)().toLowerCase()}`,
|
|
135
|
+
ruleId: rule.id,
|
|
136
|
+
ruleName: rule.name,
|
|
137
|
+
triggerType: rule.trigger.type,
|
|
138
|
+
status: overrides.status,
|
|
139
|
+
triggeredAt: now,
|
|
140
|
+
triggeredBy: `daemon-${rule.trigger.type}`,
|
|
141
|
+
source: {
|
|
142
|
+
...rule.source,
|
|
143
|
+
metadata: cloneRecord(rule.source.metadata),
|
|
144
|
+
},
|
|
145
|
+
projectId: rule.target.projectId,
|
|
146
|
+
boardProjectId: rule.target.boardProjectId,
|
|
147
|
+
issueId: overrides.issue?.id,
|
|
148
|
+
issueKey: overrides.issue?.key,
|
|
149
|
+
issueSource: overrides.issue?.source,
|
|
150
|
+
stateAtExecution: rule.state,
|
|
151
|
+
skipReason: overrides.skipReason,
|
|
152
|
+
inputs: event.inputs ? { ...event.inputs } : undefined,
|
|
153
|
+
metadata: createTriggerMetadata(rule),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
async function executeAutomationTrigger(event, options = {}) {
|
|
157
|
+
const backlogFilePath = options.backlogFilePath ?? DEFAULT_BACKLOG_FILE_PATH;
|
|
158
|
+
const now = options.now?.() ?? new Date().toISOString();
|
|
159
|
+
const storage = await readAutomationStorage(backlogFilePath);
|
|
160
|
+
const rules = [...(storage.automationRules ?? [])];
|
|
161
|
+
const rule = rules.find((candidate) => candidate.id === event.rule.id) ?? event.rule;
|
|
162
|
+
if (rule.state !== "active") {
|
|
163
|
+
const execution = createExecutionRecord(rule, event, now, {
|
|
164
|
+
status: "skipped",
|
|
165
|
+
skipReason: `Rule ${rule.id} is ${rule.state}.`,
|
|
166
|
+
});
|
|
167
|
+
await writeAutomationStorage(backlogFilePath, {
|
|
168
|
+
...storage,
|
|
169
|
+
automationExecutions: [...(storage.automationExecutions ?? []), execution],
|
|
170
|
+
});
|
|
171
|
+
return execution;
|
|
172
|
+
}
|
|
173
|
+
const projects = [...(storage.projects ?? [])];
|
|
174
|
+
const issueProject = projects.find((candidate) => candidate.id === rule.target.projectId);
|
|
175
|
+
const boardProject = projects.find((candidate) => candidate.id === rule.target.boardProjectId);
|
|
176
|
+
if (!issueProject || !boardProject) {
|
|
177
|
+
const missingTarget = !issueProject
|
|
178
|
+
? `Project ${rule.target.projectId}`
|
|
179
|
+
: `Board project ${rule.target.boardProjectId}`;
|
|
180
|
+
const execution = createExecutionRecord(rule, event, now, {
|
|
181
|
+
status: "skipped",
|
|
182
|
+
skipReason: `${missingTarget} not found.`,
|
|
183
|
+
});
|
|
184
|
+
await writeAutomationStorage(backlogFilePath, {
|
|
185
|
+
...storage,
|
|
186
|
+
automationExecutions: [...(storage.automationExecutions ?? []), execution],
|
|
187
|
+
});
|
|
188
|
+
return execution;
|
|
189
|
+
}
|
|
190
|
+
const issues = [...(storage.issues ?? [])];
|
|
191
|
+
const issue = createAutomationIssue(rule, issueProject, issues, now);
|
|
192
|
+
const execution = createExecutionRecord(rule, event, now, {
|
|
193
|
+
status: "created",
|
|
194
|
+
issue,
|
|
195
|
+
});
|
|
196
|
+
const nextRules = rules.length > 0
|
|
197
|
+
? rules.map((candidate) => (candidate.id === rule.id
|
|
198
|
+
? {
|
|
199
|
+
...candidate,
|
|
200
|
+
audit: {
|
|
201
|
+
...candidate.audit,
|
|
202
|
+
lastTriggeredAt: now,
|
|
203
|
+
lastTriggeredBy: execution.triggeredBy,
|
|
204
|
+
},
|
|
205
|
+
}
|
|
206
|
+
: candidate))
|
|
207
|
+
: storage.automationRules;
|
|
208
|
+
const nextProjects = projects.map((candidate) => (candidate.id === issueProject.id || candidate.id === boardProject.id
|
|
209
|
+
? {
|
|
210
|
+
...candidate,
|
|
211
|
+
issueIds: Array.from(new Set([...candidate.issueIds, issue.id])),
|
|
212
|
+
}
|
|
213
|
+
: candidate));
|
|
214
|
+
await writeAutomationStorage(backlogFilePath, {
|
|
215
|
+
...storage,
|
|
216
|
+
projects: nextProjects,
|
|
217
|
+
issues: [...issues, issue],
|
|
218
|
+
automationRules: nextRules,
|
|
219
|
+
automationExecutions: [...(storage.automationExecutions ?? []), execution],
|
|
220
|
+
});
|
|
221
|
+
return execution;
|
|
222
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GAP-REMOTE-001: Daemon Config — load/validate/write daemon configuration.
|
|
3
|
+
*/
|
|
4
|
+
import type { ApiResult } from "../apiResult";
|
|
5
|
+
import type { DaemonConfig } from "./types";
|
|
6
|
+
export declare function loadDaemonConfig(configPath: string): Promise<ApiResult<DaemonConfig>>;
|
|
7
|
+
export declare function writeDaemonConfig(config: DaemonConfig, configPath: string): Promise<ApiResult<void>>;
|
|
8
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/daemon/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AA+L5C,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CA0BlC;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAgB1B"}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* GAP-REMOTE-001: Daemon Config — load/validate/write daemon configuration.
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.loadDaemonConfig = loadDaemonConfig;
|
|
40
|
+
exports.writeDaemonConfig = writeDaemonConfig;
|
|
41
|
+
const node_fs_1 = require("node:fs");
|
|
42
|
+
const path = __importStar(require("node:path"));
|
|
43
|
+
const apiResult_1 = require("../apiResult");
|
|
44
|
+
const VALID_TRIGGER_TYPES = new Set(["file", "webhook", "timer"]);
|
|
45
|
+
const VALID_AUTOMATION_STATES = new Set(["draft", "active", "paused", "disabled", "archived"]);
|
|
46
|
+
const DEFAULT_MAX_CONCURRENT_RUNS = 4;
|
|
47
|
+
function isRecord(value) {
|
|
48
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
49
|
+
}
|
|
50
|
+
function isUnknownArray(value) {
|
|
51
|
+
return Array.isArray(value);
|
|
52
|
+
}
|
|
53
|
+
function validateFileTrigger(raw, index) {
|
|
54
|
+
if (!isRecord(raw)) {
|
|
55
|
+
return `Trigger at index ${index} must be an object`;
|
|
56
|
+
}
|
|
57
|
+
if (raw.type !== "file") {
|
|
58
|
+
return `Invalid trigger type at index ${index}: ${String(raw.type)}. Must be one of: file, webhook, timer`;
|
|
59
|
+
}
|
|
60
|
+
if (typeof raw.processId !== "string" || !raw.processId) {
|
|
61
|
+
return `Missing processId at trigger index ${index}`;
|
|
62
|
+
}
|
|
63
|
+
if (typeof raw.entrypoint !== "string" || !raw.entrypoint) {
|
|
64
|
+
return `Missing entrypoint at trigger index ${index}`;
|
|
65
|
+
}
|
|
66
|
+
if (typeof raw.pattern !== "string" || !raw.pattern) {
|
|
67
|
+
return `Missing pattern at trigger index ${index}`;
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
function validateAutomationRule(raw, index) {
|
|
72
|
+
if (!isRecord(raw)) {
|
|
73
|
+
return `Trigger at index ${index} must be an object`;
|
|
74
|
+
}
|
|
75
|
+
if (typeof raw.id !== "string" || !raw.id) {
|
|
76
|
+
return `Missing automation rule id at trigger index ${index}`;
|
|
77
|
+
}
|
|
78
|
+
if (typeof raw.name !== "string" || !raw.name) {
|
|
79
|
+
return `Missing automation rule name at trigger index ${index}`;
|
|
80
|
+
}
|
|
81
|
+
if (typeof raw.state !== "string" || !VALID_AUTOMATION_STATES.has(raw.state)) {
|
|
82
|
+
return `Invalid automation state at trigger index ${index}: ${String(raw.state)}`;
|
|
83
|
+
}
|
|
84
|
+
const trigger = raw.trigger;
|
|
85
|
+
if (!isRecord(trigger) || typeof trigger.type !== "string" || !VALID_TRIGGER_TYPES.has(trigger.type)) {
|
|
86
|
+
return `Invalid automation trigger type at index ${index}: ${String(isRecord(trigger) ? trigger.type : trigger)}`;
|
|
87
|
+
}
|
|
88
|
+
if (trigger.type === "file") {
|
|
89
|
+
return `Automation rules do not support file triggers at index ${index}`;
|
|
90
|
+
}
|
|
91
|
+
if (trigger.type === "timer" && (typeof trigger.cron !== "string" || !trigger.cron)) {
|
|
92
|
+
return `Missing cron for timer automation at trigger index ${index}`;
|
|
93
|
+
}
|
|
94
|
+
if (trigger.type === "webhook") {
|
|
95
|
+
if (typeof trigger.port !== "number" || trigger.port <= 0) {
|
|
96
|
+
return `Missing port for webhook automation at trigger index ${index}`;
|
|
97
|
+
}
|
|
98
|
+
if (trigger.path !== undefined && (typeof trigger.path !== "string" || !trigger.path)) {
|
|
99
|
+
return `Invalid webhook path at trigger index ${index}`;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const target = raw.target;
|
|
103
|
+
if (!isRecord(target) || typeof target.projectId !== "string" || !target.projectId) {
|
|
104
|
+
return `Missing target.projectId at trigger index ${index}`;
|
|
105
|
+
}
|
|
106
|
+
if (typeof target.boardProjectId !== "string" || !target.boardProjectId) {
|
|
107
|
+
return `Missing target.boardProjectId at trigger index ${index}`;
|
|
108
|
+
}
|
|
109
|
+
const template = raw.template;
|
|
110
|
+
if (!isRecord(template) || typeof template.title !== "string" || !template.title) {
|
|
111
|
+
return `Missing template.title at trigger index ${index}`;
|
|
112
|
+
}
|
|
113
|
+
const routing = raw.routing;
|
|
114
|
+
if (!isRecord(routing)) {
|
|
115
|
+
return `Missing routing block at trigger index ${index}`;
|
|
116
|
+
}
|
|
117
|
+
if (!isRecord(routing.issue) || routing.issue.action !== "canonical-issue-create") {
|
|
118
|
+
return `Invalid routing.issue action at trigger index ${index}`;
|
|
119
|
+
}
|
|
120
|
+
if (!isRecord(routing.board) || routing.board.action !== "shared-board-derive") {
|
|
121
|
+
return `Invalid routing.board action at trigger index ${index}`;
|
|
122
|
+
}
|
|
123
|
+
if (routing.mutateBoardDirectly !== false) {
|
|
124
|
+
return `Automation routing must disable direct board mutation at trigger index ${index}`;
|
|
125
|
+
}
|
|
126
|
+
const source = raw.source;
|
|
127
|
+
if (!isRecord(source) || typeof source.kind !== "string" || !source.kind) {
|
|
128
|
+
return `Missing source.kind at trigger index ${index}`;
|
|
129
|
+
}
|
|
130
|
+
const audit = raw.audit;
|
|
131
|
+
if (!isRecord(audit) || typeof audit.createdAt !== "string" || !audit.createdAt) {
|
|
132
|
+
return `Missing audit.createdAt at trigger index ${index}`;
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
function validateAdmission(raw) {
|
|
137
|
+
if (raw === undefined)
|
|
138
|
+
return null;
|
|
139
|
+
if (!isRecord(raw)) {
|
|
140
|
+
return "triggerAdmission must be an object";
|
|
141
|
+
}
|
|
142
|
+
if (raw.maxPendingRuns !== undefined
|
|
143
|
+
&& (typeof raw.maxPendingRuns !== "number" || !Number.isInteger(raw.maxPendingRuns) || raw.maxPendingRuns < 0)) {
|
|
144
|
+
return "triggerAdmission.maxPendingRuns must be a non-negative integer";
|
|
145
|
+
}
|
|
146
|
+
if (raw.dedupeWindowMs !== undefined
|
|
147
|
+
&& (typeof raw.dedupeWindowMs !== "number" || !Number.isInteger(raw.dedupeWindowMs) || raw.dedupeWindowMs < 0)) {
|
|
148
|
+
return "triggerAdmission.dedupeWindowMs must be a non-negative integer";
|
|
149
|
+
}
|
|
150
|
+
if (raw.rateLimit !== undefined) {
|
|
151
|
+
if (!isRecord(raw.rateLimit)) {
|
|
152
|
+
return "triggerAdmission.rateLimit must be an object";
|
|
153
|
+
}
|
|
154
|
+
if (typeof raw.rateLimit.maxTriggers !== "number"
|
|
155
|
+
|| !Number.isInteger(raw.rateLimit.maxTriggers)
|
|
156
|
+
|| raw.rateLimit.maxTriggers <= 0) {
|
|
157
|
+
return "triggerAdmission.rateLimit.maxTriggers must be a positive integer";
|
|
158
|
+
}
|
|
159
|
+
if (typeof raw.rateLimit.windowMs !== "number"
|
|
160
|
+
|| !Number.isInteger(raw.rateLimit.windowMs)
|
|
161
|
+
|| raw.rateLimit.windowMs <= 0) {
|
|
162
|
+
return "triggerAdmission.rateLimit.windowMs must be a positive integer";
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
function validateConfig(raw) {
|
|
168
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
169
|
+
return { valid: false, error: "Config must be a JSON object" };
|
|
170
|
+
}
|
|
171
|
+
const obj = raw;
|
|
172
|
+
if (typeof obj.workspace !== "string" || !obj.workspace) {
|
|
173
|
+
return { valid: false, error: "Missing required field: workspace" };
|
|
174
|
+
}
|
|
175
|
+
if (!isUnknownArray(obj.triggers)) {
|
|
176
|
+
return { valid: false, error: "Missing required field: triggers (must be an array)" };
|
|
177
|
+
}
|
|
178
|
+
for (let i = 0; i < obj.triggers.length; i++) {
|
|
179
|
+
const trigger = obj.triggers[i];
|
|
180
|
+
const error = isRecord(trigger) && trigger.type === "file"
|
|
181
|
+
? validateFileTrigger(trigger, i)
|
|
182
|
+
: validateAutomationRule(trigger, i);
|
|
183
|
+
if (error) {
|
|
184
|
+
return { valid: false, error };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const maxConcurrent = typeof obj.maxConcurrentRuns === "number"
|
|
188
|
+
? obj.maxConcurrentRuns
|
|
189
|
+
: DEFAULT_MAX_CONCURRENT_RUNS;
|
|
190
|
+
const admissionError = validateAdmission(obj.triggerAdmission);
|
|
191
|
+
if (admissionError) {
|
|
192
|
+
return { valid: false, error: admissionError };
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
valid: true,
|
|
196
|
+
config: {
|
|
197
|
+
workspace: obj.workspace,
|
|
198
|
+
triggers: obj.triggers,
|
|
199
|
+
maxConcurrentRuns: maxConcurrent,
|
|
200
|
+
triggerAdmission: obj.triggerAdmission,
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
async function loadDaemonConfig(configPath) {
|
|
205
|
+
try {
|
|
206
|
+
let raw;
|
|
207
|
+
try {
|
|
208
|
+
raw = await node_fs_1.promises.readFile(configPath, "utf-8");
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
return (0, apiResult_1.fail)("CONFIG_NOT_FOUND", `Config file not found: ${configPath}`);
|
|
212
|
+
}
|
|
213
|
+
let parsed;
|
|
214
|
+
try {
|
|
215
|
+
parsed = JSON.parse(raw);
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
return (0, apiResult_1.fail)("INVALID_CONFIG", `Failed to parse config as JSON: ${configPath}`);
|
|
219
|
+
}
|
|
220
|
+
const result = validateConfig(parsed);
|
|
221
|
+
if (!result.valid) {
|
|
222
|
+
return (0, apiResult_1.fail)("INVALID_CONFIG", result.error);
|
|
223
|
+
}
|
|
224
|
+
return (0, apiResult_1.ok)(result.config);
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
228
|
+
return (0, apiResult_1.fail)("INTERNAL_ERROR", msg);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
async function writeDaemonConfig(config, configPath) {
|
|
232
|
+
try {
|
|
233
|
+
const dir = path.dirname(configPath);
|
|
234
|
+
await node_fs_1.promises.mkdir(dir, { recursive: true });
|
|
235
|
+
const content = JSON.stringify(config, null, 2);
|
|
236
|
+
const tmpPath = `${configPath}.tmp-${process.pid}-${Date.now()}`;
|
|
237
|
+
await node_fs_1.promises.writeFile(tmpPath, content, "utf-8");
|
|
238
|
+
await node_fs_1.promises.rename(tmpPath, configPath);
|
|
239
|
+
return (0, apiResult_1.ok)(undefined);
|
|
240
|
+
}
|
|
241
|
+
catch (error) {
|
|
242
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
243
|
+
return (0, apiResult_1.fail)("INTERNAL_ERROR", msg);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GAP-REMOTE-001: Daemon JSONL logging.
|
|
3
|
+
*
|
|
4
|
+
* Appends structured JSON lines to a daemon activation log file.
|
|
5
|
+
*/
|
|
6
|
+
export type DaemonLogLevel = "debug" | "info" | "warn" | "error" | "fatal";
|
|
7
|
+
export interface DaemonLogEntry {
|
|
8
|
+
timestamp: string;
|
|
9
|
+
event: string;
|
|
10
|
+
level?: DaemonLogLevel;
|
|
11
|
+
schemaVersion?: string;
|
|
12
|
+
sampled?: boolean;
|
|
13
|
+
sequence?: number;
|
|
14
|
+
correlationId?: string;
|
|
15
|
+
traceId?: string;
|
|
16
|
+
spanId?: string;
|
|
17
|
+
data?: Record<string, unknown>;
|
|
18
|
+
}
|
|
19
|
+
export interface DaemonLogPolicy {
|
|
20
|
+
minLevel?: DaemonLogLevel;
|
|
21
|
+
eventAllowList?: readonly string[];
|
|
22
|
+
eventDenyList?: readonly string[];
|
|
23
|
+
sampleRate?: number;
|
|
24
|
+
maxBytes?: number;
|
|
25
|
+
maxFiles?: number;
|
|
26
|
+
schemaVersion?: string;
|
|
27
|
+
}
|
|
28
|
+
export declare function appendDaemonLog(logDir: string, entry: DaemonLogEntry, policy?: DaemonLogPolicy): Promise<void>;
|
|
29
|
+
export declare function readDaemonLog(logDir: string): Promise<DaemonLogEntry[]>;
|
|
30
|
+
//# sourceMappingURL=daemonLog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemonLog.d.ts","sourceRoot":"","sources":["../../src/daemon/daemonLog.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAE3E,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAsED,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,cAAc,EACrB,MAAM,CAAC,EAAE,eAAe,GACvB,OAAO,CAAC,IAAI,CAAC,CAkBf;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,cAAc,EAAE,CAAC,CAY3B"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* GAP-REMOTE-001: Daemon JSONL logging.
|
|
4
|
+
*
|
|
5
|
+
* Appends structured JSON lines to a daemon activation log file.
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.appendDaemonLog = appendDaemonLog;
|
|
42
|
+
exports.readDaemonLog = readDaemonLog;
|
|
43
|
+
const node_fs_1 = require("node:fs");
|
|
44
|
+
const path = __importStar(require("node:path"));
|
|
45
|
+
const DEFAULT_SCHEMA_VERSION = "2026.05.daemon-log-v1";
|
|
46
|
+
const LEVEL_RANK = {
|
|
47
|
+
debug: 10,
|
|
48
|
+
info: 20,
|
|
49
|
+
warn: 30,
|
|
50
|
+
error: 40,
|
|
51
|
+
fatal: 50,
|
|
52
|
+
};
|
|
53
|
+
function shouldWriteEntry(entry, policy) {
|
|
54
|
+
if (!policy)
|
|
55
|
+
return true;
|
|
56
|
+
if (policy.eventAllowList && !policy.eventAllowList.includes(entry.event)) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
if (policy.eventDenyList?.includes(entry.event)) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
const minLevel = policy.minLevel;
|
|
63
|
+
if (minLevel) {
|
|
64
|
+
const entryLevel = entry.level ?? "info";
|
|
65
|
+
if (LEVEL_RANK[entryLevel] < LEVEL_RANK[minLevel]) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const sampleRate = policy.sampleRate;
|
|
70
|
+
if (sampleRate !== undefined) {
|
|
71
|
+
if (sampleRate <= 0)
|
|
72
|
+
return false;
|
|
73
|
+
if (sampleRate < 1 && deterministicSample(entry, sampleRate) === false) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
function deterministicSample(entry, sampleRate) {
|
|
80
|
+
const key = `${entry.timestamp}:${entry.event}:${entry.correlationId ?? ""}:${entry.traceId ?? ""}`;
|
|
81
|
+
let hash = 0;
|
|
82
|
+
for (let i = 0; i < key.length; i++) {
|
|
83
|
+
hash = (hash * 31 + key.charCodeAt(i)) >>> 0;
|
|
84
|
+
}
|
|
85
|
+
return hash / 0xffffffff < sampleRate;
|
|
86
|
+
}
|
|
87
|
+
async function rotateIfNeeded(logPath, nextLineBytes, policy) {
|
|
88
|
+
const maxBytes = policy?.maxBytes ?? 0;
|
|
89
|
+
if (maxBytes <= 0)
|
|
90
|
+
return;
|
|
91
|
+
let currentSize = 0;
|
|
92
|
+
try {
|
|
93
|
+
currentSize = (await node_fs_1.promises.stat(logPath)).size;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (currentSize + nextLineBytes <= maxBytes)
|
|
99
|
+
return;
|
|
100
|
+
const maxFiles = Math.max(1, policy?.maxFiles ?? 1);
|
|
101
|
+
for (let index = maxFiles - 1; index >= 1; index--) {
|
|
102
|
+
const from = `${logPath}.${index}`;
|
|
103
|
+
const to = `${logPath}.${index + 1}`;
|
|
104
|
+
await node_fs_1.promises.rename(from, to).catch(() => { });
|
|
105
|
+
}
|
|
106
|
+
await node_fs_1.promises.rename(logPath, `${logPath}.1`).catch(() => { });
|
|
107
|
+
}
|
|
108
|
+
async function appendDaemonLog(logDir, entry, policy) {
|
|
109
|
+
if (!shouldWriteEntry(entry, policy))
|
|
110
|
+
return;
|
|
111
|
+
await node_fs_1.promises.mkdir(logDir, { recursive: true });
|
|
112
|
+
const logPath = path.join(logDir, "daemon.jsonl");
|
|
113
|
+
const serializedEntry = {
|
|
114
|
+
...entry,
|
|
115
|
+
...(policy?.schemaVersion && !entry.schemaVersion
|
|
116
|
+
? { schemaVersion: policy.schemaVersion }
|
|
117
|
+
: {}),
|
|
118
|
+
...(policy?.sampleRate !== undefined ? { sampled: true } : {}),
|
|
119
|
+
};
|
|
120
|
+
if (policy && !serializedEntry.schemaVersion) {
|
|
121
|
+
serializedEntry.schemaVersion = DEFAULT_SCHEMA_VERSION;
|
|
122
|
+
}
|
|
123
|
+
const line = JSON.stringify(serializedEntry) + "\n";
|
|
124
|
+
await rotateIfNeeded(logPath, Buffer.byteLength(line), policy);
|
|
125
|
+
await node_fs_1.promises.appendFile(logPath, line, "utf-8");
|
|
126
|
+
}
|
|
127
|
+
async function readDaemonLog(logDir) {
|
|
128
|
+
const logPath = path.join(logDir, "daemon.jsonl");
|
|
129
|
+
try {
|
|
130
|
+
const content = await node_fs_1.promises.readFile(logPath, "utf-8");
|
|
131
|
+
return content
|
|
132
|
+
.trim()
|
|
133
|
+
.split("\n")
|
|
134
|
+
.filter((line) => line.trim())
|
|
135
|
+
.map((line) => JSON.parse(line));
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
140
|
+
}
|