@virtengine/openfleet 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +914 -0
- package/LICENSE +190 -0
- package/README.md +500 -0
- package/agent-endpoint.mjs +918 -0
- package/agent-hook-bridge.mjs +230 -0
- package/agent-hooks.mjs +1188 -0
- package/agent-pool.mjs +2403 -0
- package/agent-prompts.mjs +689 -0
- package/agent-sdk.mjs +141 -0
- package/anomaly-detector.mjs +1195 -0
- package/autofix.mjs +1294 -0
- package/claude-shell.mjs +708 -0
- package/cli.mjs +906 -0
- package/codex-config.mjs +1274 -0
- package/codex-model-profiles.mjs +135 -0
- package/codex-shell.mjs +762 -0
- package/config-doctor.mjs +613 -0
- package/config.mjs +1720 -0
- package/conflict-resolver.mjs +248 -0
- package/container-runner.mjs +450 -0
- package/copilot-shell.mjs +827 -0
- package/daemon-restart-policy.mjs +56 -0
- package/diff-stats.mjs +282 -0
- package/error-detector.mjs +829 -0
- package/fetch-runtime.mjs +34 -0
- package/fleet-coordinator.mjs +838 -0
- package/get-telegram-chat-id.mjs +71 -0
- package/git-safety.mjs +170 -0
- package/github-reconciler.mjs +403 -0
- package/hook-profiles.mjs +651 -0
- package/kanban-adapter.mjs +4491 -0
- package/lib/logger.mjs +645 -0
- package/maintenance.mjs +828 -0
- package/merge-strategy.mjs +1171 -0
- package/monitor.mjs +12207 -0
- package/openfleet.config.example.json +115 -0
- package/openfleet.schema.json +465 -0
- package/package.json +203 -0
- package/postinstall.mjs +187 -0
- package/pr-cleanup-daemon.mjs +978 -0
- package/preflight.mjs +408 -0
- package/prepublish-check.mjs +90 -0
- package/presence.mjs +328 -0
- package/primary-agent.mjs +282 -0
- package/publish.mjs +151 -0
- package/repo-root.mjs +29 -0
- package/restart-controller.mjs +100 -0
- package/review-agent.mjs +557 -0
- package/rotate-agent-logs.sh +133 -0
- package/sdk-conflict-resolver.mjs +973 -0
- package/session-tracker.mjs +880 -0
- package/setup.mjs +3937 -0
- package/shared-knowledge.mjs +410 -0
- package/shared-state-manager.mjs +841 -0
- package/shared-workspace-cli.mjs +199 -0
- package/shared-workspace-registry.mjs +537 -0
- package/shared-workspaces.json +18 -0
- package/startup-service.mjs +1070 -0
- package/sync-engine.mjs +1063 -0
- package/task-archiver.mjs +801 -0
- package/task-assessment.mjs +550 -0
- package/task-claims.mjs +924 -0
- package/task-complexity.mjs +581 -0
- package/task-executor.mjs +5111 -0
- package/task-store.mjs +753 -0
- package/telegram-bot.mjs +9281 -0
- package/telegram-sentinel.mjs +2010 -0
- package/ui/app.js +867 -0
- package/ui/app.legacy.js +1464 -0
- package/ui/app.monolith.js +2488 -0
- package/ui/components/charts.js +226 -0
- package/ui/components/chat-view.js +567 -0
- package/ui/components/command-palette.js +587 -0
- package/ui/components/diff-viewer.js +190 -0
- package/ui/components/forms.js +327 -0
- package/ui/components/kanban-board.js +451 -0
- package/ui/components/session-list.js +305 -0
- package/ui/components/shared.js +473 -0
- package/ui/index.html +70 -0
- package/ui/modules/api.js +297 -0
- package/ui/modules/icons.js +461 -0
- package/ui/modules/router.js +81 -0
- package/ui/modules/settings-schema.js +261 -0
- package/ui/modules/state.js +679 -0
- package/ui/modules/telegram.js +331 -0
- package/ui/modules/utils.js +270 -0
- package/ui/styles/animations.css +140 -0
- package/ui/styles/base.css +98 -0
- package/ui/styles/components.css +1915 -0
- package/ui/styles/kanban.css +286 -0
- package/ui/styles/layout.css +809 -0
- package/ui/styles/sessions.css +827 -0
- package/ui/styles/variables.css +188 -0
- package/ui/styles.css +141 -0
- package/ui/styles.monolith.css +1046 -0
- package/ui/tabs/agents.js +1417 -0
- package/ui/tabs/chat.js +74 -0
- package/ui/tabs/control.js +887 -0
- package/ui/tabs/dashboard.js +515 -0
- package/ui/tabs/infra.js +537 -0
- package/ui/tabs/logs.js +783 -0
- package/ui/tabs/settings.js +1487 -0
- package/ui/tabs/tasks.js +1385 -0
- package/ui-server.mjs +4073 -0
- package/update-check.mjs +465 -0
- package/utils.mjs +172 -0
- package/ve-kanban.mjs +654 -0
- package/ve-kanban.ps1 +1365 -0
- package/ve-kanban.sh +18 -0
- package/ve-orchestrator.mjs +340 -0
- package/ve-orchestrator.ps1 +6546 -0
- package/ve-orchestrator.sh +18 -0
- package/vibe-kanban-wrapper.mjs +41 -0
- package/vk-error-resolver.mjs +470 -0
- package/vk-log-stream.mjs +914 -0
- package/whatsapp-channel.mjs +520 -0
- package/workspace-monitor.mjs +581 -0
- package/workspace-reaper.mjs +405 -0
- package/workspace-registry.mjs +238 -0
- package/worktree-manager.mjs +1266 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export function createDaemonCrashTracker(options = {}) {
|
|
2
|
+
const instantCrashWindowMs = Math.max(
|
|
3
|
+
1000,
|
|
4
|
+
Number(options.instantCrashWindowMs || 15_000) || 15_000,
|
|
5
|
+
);
|
|
6
|
+
const maxInstantCrashes = Math.max(
|
|
7
|
+
1,
|
|
8
|
+
Number(options.maxInstantCrashes || 3) || 3,
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
let lastStartAtMs = 0;
|
|
12
|
+
let instantCrashCount = 0;
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
markStart(nowMs = Date.now()) {
|
|
16
|
+
lastStartAtMs = Number(nowMs) || Date.now();
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
recordExit(nowMs = Date.now()) {
|
|
20
|
+
const now = Number(nowMs) || Date.now();
|
|
21
|
+
const runDurationMs =
|
|
22
|
+
lastStartAtMs > 0 ? Math.max(0, now - lastStartAtMs) : 0;
|
|
23
|
+
const instantCrash =
|
|
24
|
+
lastStartAtMs > 0 && runDurationMs <= instantCrashWindowMs;
|
|
25
|
+
|
|
26
|
+
if (instantCrash) {
|
|
27
|
+
instantCrashCount += 1;
|
|
28
|
+
} else {
|
|
29
|
+
instantCrashCount = 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
runDurationMs,
|
|
34
|
+
instantCrash,
|
|
35
|
+
instantCrashCount,
|
|
36
|
+
exceeded: instantCrashCount >= maxInstantCrashes,
|
|
37
|
+
instantCrashWindowMs,
|
|
38
|
+
maxInstantCrashes,
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
reset() {
|
|
43
|
+
lastStartAtMs = 0;
|
|
44
|
+
instantCrashCount = 0;
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
getState() {
|
|
48
|
+
return {
|
|
49
|
+
lastStartAtMs,
|
|
50
|
+
instantCrashCount,
|
|
51
|
+
instantCrashWindowMs,
|
|
52
|
+
maxInstantCrashes,
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
package/diff-stats.mjs
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* diff-stats.mjs — Collects git diff statistics for review handoff.
|
|
3
|
+
*
|
|
4
|
+
* Produces a tree of file changes with +/- line counts, like:
|
|
5
|
+
* monitor.mjs +890 -750
|
|
6
|
+
* task-executor.mjs +320 -180
|
|
7
|
+
* review-agent.mjs +680 -528
|
|
8
|
+
*
|
|
9
|
+
* @module diff-stats
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { spawnSync } from "node:child_process";
|
|
13
|
+
|
|
14
|
+
const TAG = "[diff-stats]";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {Object} FileChangeStats
|
|
18
|
+
* @property {string} file - Relative file path
|
|
19
|
+
* @property {number} additions - Lines added
|
|
20
|
+
* @property {number} deletions - Lines deleted
|
|
21
|
+
* @property {boolean} binary - True if binary file
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {Object} DiffStats
|
|
26
|
+
* @property {FileChangeStats[]} files
|
|
27
|
+
* @property {number} totalFiles
|
|
28
|
+
* @property {number} totalAdditions
|
|
29
|
+
* @property {number} totalDeletions
|
|
30
|
+
* @property {string} formatted - Human-readable summary string
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
// ── Main Collectors ─────────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Collect diff stats for a worktree (vs origin/main or the upstream branch).
|
|
37
|
+
*
|
|
38
|
+
* Tries three strategies in order:
|
|
39
|
+
* 1. `git diff --numstat origin/main...HEAD`
|
|
40
|
+
* 2. `git diff --numstat HEAD~10...HEAD` (last 10 commits)
|
|
41
|
+
* 3. `git diff --stat origin/main...HEAD` (parsed stat output)
|
|
42
|
+
*
|
|
43
|
+
* @param {string} worktreePath - Path to the git worktree
|
|
44
|
+
* @param {Object} [options]
|
|
45
|
+
* @param {string} [options.baseBranch="origin/main"] - Branch to diff against
|
|
46
|
+
* @param {number} [options.timeoutMs=30000]
|
|
47
|
+
* @returns {DiffStats}
|
|
48
|
+
*/
|
|
49
|
+
export function collectDiffStats(worktreePath, options = {}) {
|
|
50
|
+
const {
|
|
51
|
+
baseBranch = "origin/main",
|
|
52
|
+
timeoutMs = 30_000,
|
|
53
|
+
} = options;
|
|
54
|
+
|
|
55
|
+
// Strategy 1: --numstat (most reliable)
|
|
56
|
+
const numstat = tryNumstat(worktreePath, `${baseBranch}...HEAD`, timeoutMs);
|
|
57
|
+
if (numstat) return buildResult(numstat);
|
|
58
|
+
|
|
59
|
+
// Strategy 2: last N commits
|
|
60
|
+
const recent = tryNumstat(worktreePath, "HEAD~10...HEAD", timeoutMs);
|
|
61
|
+
if (recent) return buildResult(recent);
|
|
62
|
+
|
|
63
|
+
// Strategy 3: --stat fallback
|
|
64
|
+
const stat = tryStat(worktreePath, `${baseBranch}...HEAD`, timeoutMs);
|
|
65
|
+
if (stat) return buildResult(stat);
|
|
66
|
+
|
|
67
|
+
// Strategy 4: staged + unstaged changes
|
|
68
|
+
const working = tryNumstat(worktreePath, "HEAD", timeoutMs);
|
|
69
|
+
if (working) return buildResult(working);
|
|
70
|
+
|
|
71
|
+
// Nothing worked
|
|
72
|
+
return {
|
|
73
|
+
files: [],
|
|
74
|
+
totalFiles: 0,
|
|
75
|
+
totalAdditions: 0,
|
|
76
|
+
totalDeletions: 0,
|
|
77
|
+
formatted: "(no diff stats available)",
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get a compact string summary of diff stats.
|
|
83
|
+
*
|
|
84
|
+
* Example output:
|
|
85
|
+
* ```
|
|
86
|
+
* 5 files changed, +1250 -890
|
|
87
|
+
* review-agent.mjs +680 -528
|
|
88
|
+
* task-executor.mjs +320 -180
|
|
89
|
+
* monitor.mjs +150 -82
|
|
90
|
+
* session-tracker.mjs +370 -0
|
|
91
|
+
* diff-stats.mjs +380 -0
|
|
92
|
+
* ```
|
|
93
|
+
*
|
|
94
|
+
* @param {string} worktreePath
|
|
95
|
+
* @param {Object} [options]
|
|
96
|
+
* @returns {string}
|
|
97
|
+
*/
|
|
98
|
+
export function getCompactDiffSummary(worktreePath, options = {}) {
|
|
99
|
+
const stats = collectDiffStats(worktreePath, options);
|
|
100
|
+
return stats.formatted;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get the recent commits on the current branch (vs origin/main).
|
|
105
|
+
*
|
|
106
|
+
* @param {string} worktreePath
|
|
107
|
+
* @param {number} [maxCommits=10]
|
|
108
|
+
* @returns {string[]} - Array of one-line commit messages
|
|
109
|
+
*/
|
|
110
|
+
export function getRecentCommits(worktreePath, maxCommits = 10) {
|
|
111
|
+
try {
|
|
112
|
+
// Try vs origin/main first
|
|
113
|
+
const result = spawnSync(
|
|
114
|
+
"git",
|
|
115
|
+
["log", "--oneline", `--max-count=${maxCommits}`, "origin/main..HEAD"],
|
|
116
|
+
{ cwd: worktreePath, encoding: "utf8", timeout: 10_000 },
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
if (result.status === 0 && (result.stdout || "").trim()) {
|
|
120
|
+
return result.stdout.trim().split("\n").filter(Boolean);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Fallback: last N commits on current branch
|
|
124
|
+
const fallback = spawnSync(
|
|
125
|
+
"git",
|
|
126
|
+
["log", "--oneline", `--max-count=${maxCommits}`],
|
|
127
|
+
{ cwd: worktreePath, encoding: "utf8", timeout: 10_000 },
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
if (fallback.status === 0 && (fallback.stdout || "").trim()) {
|
|
131
|
+
return fallback.stdout.trim().split("\n").filter(Boolean);
|
|
132
|
+
}
|
|
133
|
+
} catch (err) {
|
|
134
|
+
console.warn(`${TAG} getRecentCommits error: ${err.message}`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ── Internal Strategies ─────────────────────────────────────────────────────
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Try `git diff --numstat` and parse the output.
|
|
144
|
+
* @param {string} cwd
|
|
145
|
+
* @param {string} range - e.g., "origin/main...HEAD"
|
|
146
|
+
* @param {number} timeoutMs
|
|
147
|
+
* @returns {FileChangeStats[]|null}
|
|
148
|
+
*/
|
|
149
|
+
function tryNumstat(cwd, range, timeoutMs) {
|
|
150
|
+
try {
|
|
151
|
+
const result = spawnSync(
|
|
152
|
+
"git",
|
|
153
|
+
["diff", "--numstat", range],
|
|
154
|
+
{ cwd, encoding: "utf8", timeout: timeoutMs, stdio: ["pipe", "pipe", "pipe"] },
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
if (result.status !== 0 || !(result.stdout || "").trim()) return null;
|
|
158
|
+
|
|
159
|
+
const files = [];
|
|
160
|
+
for (const line of result.stdout.trim().split("\n")) {
|
|
161
|
+
if (!line.trim()) continue;
|
|
162
|
+
|
|
163
|
+
const parts = line.split("\t");
|
|
164
|
+
if (parts.length < 3) continue;
|
|
165
|
+
|
|
166
|
+
const [addStr, delStr, ...fileParts] = parts;
|
|
167
|
+
const file = fileParts.join("\t"); // Handle filenames with tabs
|
|
168
|
+
|
|
169
|
+
if (addStr === "-" && delStr === "-") {
|
|
170
|
+
// Binary file
|
|
171
|
+
files.push({ file, additions: 0, deletions: 0, binary: true });
|
|
172
|
+
} else {
|
|
173
|
+
files.push({
|
|
174
|
+
file,
|
|
175
|
+
additions: parseInt(addStr, 10) || 0,
|
|
176
|
+
deletions: parseInt(delStr, 10) || 0,
|
|
177
|
+
binary: false,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return files.length > 0 ? files : null;
|
|
183
|
+
} catch {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Try `git diff --stat` and parse the output.
|
|
190
|
+
* Fallback when --numstat fails.
|
|
191
|
+
*
|
|
192
|
+
* @param {string} cwd
|
|
193
|
+
* @param {string} range
|
|
194
|
+
* @param {number} timeoutMs
|
|
195
|
+
* @returns {FileChangeStats[]|null}
|
|
196
|
+
*/
|
|
197
|
+
function tryStat(cwd, range, timeoutMs) {
|
|
198
|
+
try {
|
|
199
|
+
const result = spawnSync(
|
|
200
|
+
"git",
|
|
201
|
+
["diff", "--stat", range],
|
|
202
|
+
{ cwd, encoding: "utf8", timeout: timeoutMs, stdio: ["pipe", "pipe", "pipe"] },
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
if (result.status !== 0 || !(result.stdout || "").trim()) return null;
|
|
206
|
+
|
|
207
|
+
const files = [];
|
|
208
|
+
const lines = result.stdout.trim().split("\n");
|
|
209
|
+
|
|
210
|
+
// Last line is the summary — skip it
|
|
211
|
+
for (let i = 0; i < lines.length - 1; i++) {
|
|
212
|
+
const line = lines[i].trim();
|
|
213
|
+
if (!line) continue;
|
|
214
|
+
|
|
215
|
+
// Format: " filename | 123 +++---" or " filename | Bin 0 -> 1234 bytes"
|
|
216
|
+
const pipeIdx = line.lastIndexOf("|");
|
|
217
|
+
if (pipeIdx === -1) continue;
|
|
218
|
+
|
|
219
|
+
const file = line.slice(0, pipeIdx).trim();
|
|
220
|
+
const statsStr = line.slice(pipeIdx + 1).trim();
|
|
221
|
+
|
|
222
|
+
if (statsStr.startsWith("Bin")) {
|
|
223
|
+
files.push({ file, additions: 0, deletions: 0, binary: true });
|
|
224
|
+
} else {
|
|
225
|
+
// Count '+' and '-' chars
|
|
226
|
+
const additions = (statsStr.match(/\+/g) || []).length;
|
|
227
|
+
const deletions = (statsStr.match(/-/g) || []).length;
|
|
228
|
+
files.push({ file, additions, deletions, binary: false });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return files.length > 0 ? files : null;
|
|
233
|
+
} catch {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ── Result Builder ──────────────────────────────────────────────────────────
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Build a DiffStats result from parsed file changes.
|
|
242
|
+
* @param {FileChangeStats[]} files
|
|
243
|
+
* @returns {DiffStats}
|
|
244
|
+
*/
|
|
245
|
+
function buildResult(files) {
|
|
246
|
+
let totalAdditions = 0;
|
|
247
|
+
let totalDeletions = 0;
|
|
248
|
+
|
|
249
|
+
for (const f of files) {
|
|
250
|
+
totalAdditions += f.additions;
|
|
251
|
+
totalDeletions += f.deletions;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Sort by total changes (largest first)
|
|
255
|
+
const sorted = [...files].sort(
|
|
256
|
+
(a, b) => (b.additions + b.deletions) - (a.additions + a.deletions),
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
// Find max filename length for alignment
|
|
260
|
+
const maxNameLen = Math.max(...sorted.map((f) => f.file.length), 10);
|
|
261
|
+
|
|
262
|
+
const lines = sorted.map((f) => {
|
|
263
|
+
const name = f.file.padEnd(maxNameLen);
|
|
264
|
+
if (f.binary) {
|
|
265
|
+
return ` ${name} (binary)`;
|
|
266
|
+
}
|
|
267
|
+
const add = `+${f.additions}`.padStart(6);
|
|
268
|
+
const del = `-${f.deletions}`.padStart(6);
|
|
269
|
+
return ` ${name} ${add} ${del}`;
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
const header = `${files.length} file(s) changed, +${totalAdditions} -${totalDeletions}`;
|
|
273
|
+
const formatted = `${header}\n${lines.join("\n")}`;
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
files: sorted,
|
|
277
|
+
totalFiles: files.length,
|
|
278
|
+
totalAdditions,
|
|
279
|
+
totalDeletions,
|
|
280
|
+
formatted,
|
|
281
|
+
};
|
|
282
|
+
}
|