@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,230 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { executeBlockingHooks, executeHooks, loadHooks, registerBuiltinHooks } from "./agent-hooks.mjs";
|
|
5
|
+
|
|
6
|
+
const TAG = "[agent-hook-bridge]";
|
|
7
|
+
|
|
8
|
+
function parseArgs(argv) {
|
|
9
|
+
const args = argv.slice(2);
|
|
10
|
+
const result = {};
|
|
11
|
+
for (let i = 0; i < args.length; i++) {
|
|
12
|
+
const token = args[i];
|
|
13
|
+
if (!token.startsWith("--")) continue;
|
|
14
|
+
const key = token.slice(2);
|
|
15
|
+
const next = args[i + 1];
|
|
16
|
+
if (next && !next.startsWith("--")) {
|
|
17
|
+
result[key] = next;
|
|
18
|
+
i++;
|
|
19
|
+
} else {
|
|
20
|
+
result[key] = "1";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function readStdInJson() {
|
|
27
|
+
try {
|
|
28
|
+
const raw = process.stdin.isTTY ? "" : readFileSync(0, "utf8");
|
|
29
|
+
if (raw && raw.trim()) {
|
|
30
|
+
return JSON.parse(raw);
|
|
31
|
+
}
|
|
32
|
+
} catch {
|
|
33
|
+
/* ignore parse errors */
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function normalizeAgent(agent) {
|
|
40
|
+
const raw = String(agent || "")
|
|
41
|
+
.trim()
|
|
42
|
+
.toLowerCase();
|
|
43
|
+
if (["codex", "copilot", "claude"].includes(raw)) return raw;
|
|
44
|
+
return "codex";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function normalizeSourceEvent(event) {
|
|
48
|
+
return String(event || "").trim();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function collectStrings(value, out = []) {
|
|
52
|
+
if (typeof value === "string") {
|
|
53
|
+
if (value.trim()) out.push(value.trim());
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
if (Array.isArray(value)) {
|
|
57
|
+
for (const item of value) collectStrings(item, out);
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
if (!value || typeof value !== "object") return out;
|
|
61
|
+
for (const nested of Object.values(value)) {
|
|
62
|
+
collectStrings(nested, out);
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function extractCommand(payload) {
|
|
68
|
+
const candidates = [
|
|
69
|
+
payload?.command,
|
|
70
|
+
payload?.cmd,
|
|
71
|
+
payload?.tool_input?.command,
|
|
72
|
+
payload?.toolInput?.command,
|
|
73
|
+
payload?.input?.command,
|
|
74
|
+
payload?.eventData?.input?.command,
|
|
75
|
+
payload?.event_data?.input?.command,
|
|
76
|
+
payload?.toolArguments?.command,
|
|
77
|
+
payload?.tool_arguments?.command,
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
for (const value of candidates) {
|
|
81
|
+
if (typeof value === "string" && value.trim()) {
|
|
82
|
+
return value.trim();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const strings = collectStrings(payload);
|
|
87
|
+
const command = strings.find((text) => /\b(git|gh)\b/i.test(text));
|
|
88
|
+
return command || "";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function extractToolName(payload) {
|
|
92
|
+
const candidates = [
|
|
93
|
+
payload?.toolName,
|
|
94
|
+
payload?.tool_name,
|
|
95
|
+
payload?.tool,
|
|
96
|
+
payload?.eventData?.toolName,
|
|
97
|
+
payload?.event_data?.tool_name,
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
for (const value of candidates) {
|
|
101
|
+
if (typeof value === "string" && value.trim()) {
|
|
102
|
+
return value.trim();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return "";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function classifyCommand(command) {
|
|
109
|
+
const normalized = String(command || "")
|
|
110
|
+
.replace(/\s+/g, " ")
|
|
111
|
+
.trim()
|
|
112
|
+
.toLowerCase();
|
|
113
|
+
|
|
114
|
+
if (!normalized) return null;
|
|
115
|
+
if (/\bgit\s+push\b/.test(normalized)) return "push";
|
|
116
|
+
if (/\bgit\s+commit\b/.test(normalized)) return "commit";
|
|
117
|
+
if (/\bgh\s+pr\s+create\b/.test(normalized)) return "pr";
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function mapEvents(sourceEvent, payload) {
|
|
122
|
+
const raw = normalizeSourceEvent(sourceEvent).toLowerCase();
|
|
123
|
+
const command = extractCommand(payload);
|
|
124
|
+
const action = classifyCommand(command);
|
|
125
|
+
|
|
126
|
+
if (raw === "sessionstart" || raw === "userpromptsubmit" || raw === "promptsubmit") {
|
|
127
|
+
return [{ event: "SessionStart", blocking: false, command }];
|
|
128
|
+
}
|
|
129
|
+
if (raw === "sessionend" || raw === "stop") {
|
|
130
|
+
return [{ event: "SessionStop", blocking: false, command }];
|
|
131
|
+
}
|
|
132
|
+
if (raw === "pretooluse") {
|
|
133
|
+
if (action === "push") {
|
|
134
|
+
return [{ event: "PrePush", blocking: true, command }];
|
|
135
|
+
}
|
|
136
|
+
if (action === "commit") {
|
|
137
|
+
return [{ event: "PreCommit", blocking: true, command }];
|
|
138
|
+
}
|
|
139
|
+
if (action === "pr") {
|
|
140
|
+
return [{ event: "PrePR", blocking: true, command }];
|
|
141
|
+
}
|
|
142
|
+
return [{ event: "PreToolUse", blocking: false, command }];
|
|
143
|
+
}
|
|
144
|
+
if (raw === "posttooluse") {
|
|
145
|
+
if (action === "push") {
|
|
146
|
+
return [{ event: "PostPush", blocking: false, command }];
|
|
147
|
+
}
|
|
148
|
+
if (action === "commit") {
|
|
149
|
+
return [{ event: "PostCommit", blocking: false, command }];
|
|
150
|
+
}
|
|
151
|
+
if (action === "pr") {
|
|
152
|
+
return [{ event: "PostPR", blocking: false, command }];
|
|
153
|
+
}
|
|
154
|
+
return [{ event: "PostToolUse", blocking: false, command }];
|
|
155
|
+
}
|
|
156
|
+
if (raw === "subagentstop") {
|
|
157
|
+
return [{ event: "SubagentStop", blocking: false, command }];
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return [];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function run() {
|
|
164
|
+
// ── VE_MANAGED guard ──────────────────────────────────────────────────────
|
|
165
|
+
// Only execute hooks for sessions managed by openfleet.
|
|
166
|
+
// openfleet sets VE_MANAGED=1 in all spawned agent environments.
|
|
167
|
+
// If this env var is missing, we're running inside a standalone agent session
|
|
168
|
+
// that just happens to have the hook files in its config — exit silently.
|
|
169
|
+
if (!process.env.VE_MANAGED && !process.env.CODEX_MONITOR_HOOKS_FORCE) {
|
|
170
|
+
process.exit(0);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const args = parseArgs(process.argv);
|
|
174
|
+
const agent = normalizeAgent(args.agent || process.env.VE_SDK || "codex");
|
|
175
|
+
const sourceEvent = args.event || "";
|
|
176
|
+
const payload = readStdInJson();
|
|
177
|
+
const toolName = extractToolName(payload);
|
|
178
|
+
const mapped = mapEvents(sourceEvent, payload);
|
|
179
|
+
|
|
180
|
+
if (mapped.length === 0) {
|
|
181
|
+
process.exit(0);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
loadHooks();
|
|
185
|
+
registerBuiltinHooks();
|
|
186
|
+
|
|
187
|
+
for (const item of mapped) {
|
|
188
|
+
const context = {
|
|
189
|
+
sdk: agent,
|
|
190
|
+
taskId: process.env.VE_TASK_ID || "",
|
|
191
|
+
taskTitle: process.env.VE_TASK_TITLE || "",
|
|
192
|
+
taskDescription:
|
|
193
|
+
process.env.VE_TASK_DESCRIPTION ||
|
|
194
|
+
process.env.VE_DESCRIPTION ||
|
|
195
|
+
process.env.VK_DESCRIPTION ||
|
|
196
|
+
"",
|
|
197
|
+
branch: process.env.VE_BRANCH_NAME || "",
|
|
198
|
+
worktreePath: process.cwd(),
|
|
199
|
+
extra: {
|
|
200
|
+
source_event: sourceEvent,
|
|
201
|
+
tool_name: toolName,
|
|
202
|
+
command: item.command || "",
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
if (item.blocking) {
|
|
207
|
+
const result = await executeBlockingHooks(item.event, context);
|
|
208
|
+
if (!result.passed) {
|
|
209
|
+
const errors = result.failures
|
|
210
|
+
.map((failure) => {
|
|
211
|
+
const detail = failure.stderr || failure.error || "hook failed";
|
|
212
|
+
return `${failure.id}: ${detail}`;
|
|
213
|
+
})
|
|
214
|
+
.join(" | ");
|
|
215
|
+
console.error(`${TAG} blocking hook failure for ${item.event}: ${errors}`);
|
|
216
|
+
process.exit(2);
|
|
217
|
+
}
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
await executeHooks(item.event, context);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
process.exit(0);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
run().catch((err) => {
|
|
228
|
+
console.error(`${TAG} ${err.message}`);
|
|
229
|
+
process.exit(1);
|
|
230
|
+
});
|