@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
package/publish.mjs
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { join, resolve } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
|
|
9
|
+
const SCRIPT_DIR = resolve(fileURLToPath(new URL(".", import.meta.url)));
|
|
10
|
+
|
|
11
|
+
function hasArg(flag) {
|
|
12
|
+
return process.argv.includes(flag);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getRegistryUrl() {
|
|
16
|
+
const raw =
|
|
17
|
+
process.env.NPM_REGISTRY_URL ||
|
|
18
|
+
process.env.npm_config_registry ||
|
|
19
|
+
"https://registry.npmjs.org/";
|
|
20
|
+
const parsed = new URL(raw);
|
|
21
|
+
if (!parsed.pathname.endsWith("/")) {
|
|
22
|
+
parsed.pathname = `${parsed.pathname}/`;
|
|
23
|
+
}
|
|
24
|
+
return parsed.toString();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function createEphemeralNpmrc(registryUrl, token) {
|
|
28
|
+
const folder = mkdtempSync(join(tmpdir(), "openfleet-npmrc-"));
|
|
29
|
+
const npmrcPath = join(folder, ".npmrc");
|
|
30
|
+
const parsed = new URL(registryUrl);
|
|
31
|
+
const authPath = parsed.pathname || "/";
|
|
32
|
+
const authLine = `//${parsed.host}${authPath}:_authToken=${token}`;
|
|
33
|
+
const content = [
|
|
34
|
+
`registry=${registryUrl}`,
|
|
35
|
+
"always-auth=true",
|
|
36
|
+
authLine,
|
|
37
|
+
].join("\n");
|
|
38
|
+
writeFileSync(npmrcPath, `${content}\n`, "utf8");
|
|
39
|
+
return { folder, npmrcPath };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function run(command, args, env) {
|
|
43
|
+
const result = spawnSync(command, args, {
|
|
44
|
+
stdio: "inherit",
|
|
45
|
+
cwd: SCRIPT_DIR,
|
|
46
|
+
env,
|
|
47
|
+
shell: true,
|
|
48
|
+
});
|
|
49
|
+
if (result.error) {
|
|
50
|
+
console.error(`[publish] Failed to execute ${command}: ${result.error.message}`);
|
|
51
|
+
return 1;
|
|
52
|
+
}
|
|
53
|
+
return result.status ?? 1;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function runCheck(label, command, args, env) {
|
|
57
|
+
console.log(`[publish] Running ${label}...`);
|
|
58
|
+
const status = run(command, args, env);
|
|
59
|
+
if (status !== 0) {
|
|
60
|
+
console.error(`[publish] ${label} failed (exit ${status}). Aborting publish.`);
|
|
61
|
+
}
|
|
62
|
+
return status;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const NPM_BIN = "npm";
|
|
66
|
+
|
|
67
|
+
function main() {
|
|
68
|
+
const dryRun = hasArg("--dry-run");
|
|
69
|
+
const tag = process.env.NPM_PUBLISH_TAG || "latest";
|
|
70
|
+
const otp = process.env.NPM_OTP || "";
|
|
71
|
+
const access = process.env.NPM_PUBLISH_ACCESS || "public";
|
|
72
|
+
const registry = getRegistryUrl();
|
|
73
|
+
const token = process.env.NPM_ACCESS_TOKEN || process.env.NODE_AUTH_TOKEN || "";
|
|
74
|
+
|
|
75
|
+
if (!dryRun && !token) {
|
|
76
|
+
console.error(
|
|
77
|
+
"[publish] Missing token. Set NPM_ACCESS_TOKEN (or NODE_AUTH_TOKEN) in environment. - use a 2FA Permissionless token",
|
|
78
|
+
);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.log(
|
|
83
|
+
`[publish] Running validation gates (${dryRun ? "dry-run" : "publish"})...`,
|
|
84
|
+
);
|
|
85
|
+
const prepushStatus = runCheck(
|
|
86
|
+
"pre-push checks",
|
|
87
|
+
NPM_BIN,
|
|
88
|
+
["run", "prepush:check"],
|
|
89
|
+
process.env,
|
|
90
|
+
);
|
|
91
|
+
if (prepushStatus !== 0) {
|
|
92
|
+
process.exit(prepushStatus);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const prepublishStatus = runCheck(
|
|
96
|
+
"prepublish checks",
|
|
97
|
+
NPM_BIN,
|
|
98
|
+
["run", "prepublishOnly"],
|
|
99
|
+
process.env,
|
|
100
|
+
);
|
|
101
|
+
if (prepublishStatus !== 0) {
|
|
102
|
+
process.exit(prepublishStatus);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let tempConfig = null;
|
|
106
|
+
try {
|
|
107
|
+
const env = { ...process.env };
|
|
108
|
+
if (!dryRun) {
|
|
109
|
+
tempConfig = createEphemeralNpmrc(registry, token);
|
|
110
|
+
env.NPM_CONFIG_USERCONFIG = tempConfig.npmrcPath;
|
|
111
|
+
env.NODE_AUTH_TOKEN = token;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const publishArgs = [
|
|
115
|
+
"publish",
|
|
116
|
+
"--registry",
|
|
117
|
+
registry,
|
|
118
|
+
"--access",
|
|
119
|
+
access,
|
|
120
|
+
"--tag",
|
|
121
|
+
tag,
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
if (dryRun) {
|
|
125
|
+
publishArgs.push("--dry-run");
|
|
126
|
+
}
|
|
127
|
+
if (otp) {
|
|
128
|
+
publishArgs.push("--otp", otp);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
console.log(
|
|
132
|
+
`[publish] npm ${publishArgs.join(" ")} (token via env/userconfig, redacted)`,
|
|
133
|
+
);
|
|
134
|
+
const status = run(NPM_BIN, publishArgs, env);
|
|
135
|
+
if (status === 0 && !dryRun) {
|
|
136
|
+
console.log(
|
|
137
|
+
"\n[publish] REMINDER: deprecate the legacy npm package to redirect users:\n" +
|
|
138
|
+
" npm deprecate codex-monitor@'*' \"Renamed to @virtengine/openfleet. Install: npm install -g @virtengine/openfleet\"\n" +
|
|
139
|
+
" # If a scoped legacy package exists:\n" +
|
|
140
|
+
" npm deprecate @virtengine/codex-monitor@'*' \"Renamed to @virtengine/openfleet. Install: npm install -g @virtengine/openfleet\"\n",
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
process.exit(status);
|
|
144
|
+
} finally {
|
|
145
|
+
if (tempConfig?.folder) {
|
|
146
|
+
rmSync(tempConfig.folder, { recursive: true, force: true });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
main();
|
package/repo-root.mjs
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the repo root for openfleet.
|
|
6
|
+
*
|
|
7
|
+
* Priority:
|
|
8
|
+
* 1. Explicit REPO_ROOT env var.
|
|
9
|
+
* 2. git rev-parse --show-toplevel (relative to cwd).
|
|
10
|
+
* 3. process.cwd().
|
|
11
|
+
*/
|
|
12
|
+
export function resolveRepoRoot(options = {}) {
|
|
13
|
+
const envRoot = process.env.REPO_ROOT;
|
|
14
|
+
if (envRoot) return resolve(envRoot);
|
|
15
|
+
|
|
16
|
+
const cwd = options.cwd || process.cwd();
|
|
17
|
+
try {
|
|
18
|
+
const gitRoot = execSync("git rev-parse --show-toplevel", {
|
|
19
|
+
encoding: "utf8",
|
|
20
|
+
cwd,
|
|
21
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
22
|
+
}).trim();
|
|
23
|
+
if (gitRoot) return gitRoot;
|
|
24
|
+
} catch {
|
|
25
|
+
// ignore - fall back to cwd
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return resolve(cwd);
|
|
29
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const DEFAULT_MUTEX_BACKOFF_STEP_MS = 30_000;
|
|
2
|
+
const DEFAULT_MUTEX_BACKOFF_MAX_MS = 90_000;
|
|
3
|
+
const DEFAULT_MIN_RESTART_INTERVAL_MS = 15_000;
|
|
4
|
+
const DEFAULT_QUICK_EXIT_THRESHOLD_MS = 20_000;
|
|
5
|
+
|
|
6
|
+
export class RestartController {
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
this.mutexBackoffStepMs =
|
|
9
|
+
options.mutexBackoffStepMs ?? DEFAULT_MUTEX_BACKOFF_STEP_MS;
|
|
10
|
+
this.mutexBackoffMaxMs =
|
|
11
|
+
options.mutexBackoffMaxMs ?? DEFAULT_MUTEX_BACKOFF_MAX_MS;
|
|
12
|
+
this.minRestartIntervalMs =
|
|
13
|
+
options.minRestartIntervalMs ?? DEFAULT_MIN_RESTART_INTERVAL_MS;
|
|
14
|
+
this.quickExitThresholdMs =
|
|
15
|
+
options.quickExitThresholdMs ?? DEFAULT_QUICK_EXIT_THRESHOLD_MS;
|
|
16
|
+
|
|
17
|
+
this.mutexHeldDetected = false;
|
|
18
|
+
this.mutexBackoffMs = 0;
|
|
19
|
+
this.lastProcessStartAt = 0;
|
|
20
|
+
this.consecutiveQuickExits = 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
noteLogLine(line) {
|
|
24
|
+
if (!line) return;
|
|
25
|
+
if (
|
|
26
|
+
line.includes("Another orchestrator instance is already running") ||
|
|
27
|
+
line.includes("mutex held")
|
|
28
|
+
) {
|
|
29
|
+
this.mutexHeldDetected = true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
noteProcessStarted(nowMs = Date.now()) {
|
|
34
|
+
this.mutexHeldDetected = false;
|
|
35
|
+
this.lastProcessStartAt = nowMs;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
getMinRestartDelay(nowMs = Date.now()) {
|
|
39
|
+
if (this.lastProcessStartAt <= 0) return 0;
|
|
40
|
+
const sinceLast = nowMs - this.lastProcessStartAt;
|
|
41
|
+
if (sinceLast < this.minRestartIntervalMs) {
|
|
42
|
+
return this.minRestartIntervalMs - sinceLast;
|
|
43
|
+
}
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
shouldSuppressRestart(reason) {
|
|
48
|
+
return reason === "file-change" && this.mutexBackoffMs > 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
resetBackoff() {
|
|
52
|
+
this.mutexBackoffMs = 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
getRestartDelay() {
|
|
56
|
+
return this.mutexBackoffMs;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
recordExit(runDurationMs, isMutexHeld) {
|
|
60
|
+
const hadBackoff = this.mutexBackoffMs > 0;
|
|
61
|
+
const isQuickExit = runDurationMs < this.quickExitThresholdMs;
|
|
62
|
+
|
|
63
|
+
if (isQuickExit) {
|
|
64
|
+
this.consecutiveQuickExits += 1;
|
|
65
|
+
} else {
|
|
66
|
+
this.consecutiveQuickExits = 0;
|
|
67
|
+
if (this.mutexBackoffMs > 0) {
|
|
68
|
+
this.mutexBackoffMs = 0;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (isMutexHeld) {
|
|
73
|
+
this.mutexHeldDetected = false;
|
|
74
|
+
this.mutexBackoffMs = Math.min(
|
|
75
|
+
this.mutexBackoffMs + this.mutexBackoffStepMs,
|
|
76
|
+
this.mutexBackoffMaxMs,
|
|
77
|
+
);
|
|
78
|
+
return {
|
|
79
|
+
isMutexHeld: true,
|
|
80
|
+
backoffMs: this.mutexBackoffMs,
|
|
81
|
+
consecutiveQuickExits: this.consecutiveQuickExits,
|
|
82
|
+
backoffReset: hadBackoff && !isQuickExit,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
isMutexHeld: false,
|
|
88
|
+
backoffMs: this.mutexBackoffMs,
|
|
89
|
+
consecutiveQuickExits: this.consecutiveQuickExits,
|
|
90
|
+
backoffReset: hadBackoff && !isQuickExit,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const RESTART_DEFAULTS = {
|
|
96
|
+
mutexBackoffStepMs: DEFAULT_MUTEX_BACKOFF_STEP_MS,
|
|
97
|
+
mutexBackoffMaxMs: DEFAULT_MUTEX_BACKOFF_MAX_MS,
|
|
98
|
+
minRestartIntervalMs: DEFAULT_MIN_RESTART_INTERVAL_MS,
|
|
99
|
+
quickExitThresholdMs: DEFAULT_QUICK_EXIT_THRESHOLD_MS,
|
|
100
|
+
};
|