@virtengine/openfleet 0.25.0 → 0.26.2
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 +38 -34
- package/agent-hook-bridge.mjs +1 -1
- package/agent-hooks.mjs +5 -5
- package/agent-prompts.mjs +4 -4
- package/cli.mjs +79 -17
- package/config-doctor.mjs +1 -1
- package/config.mjs +11 -11
- package/hook-profiles.mjs +17 -17
- package/kanban-adapter.mjs +6 -6
- package/monitor.mjs +3 -3
- package/package.json +6 -2
- package/postinstall.mjs +1 -1
- package/preflight.mjs +2 -2
- package/publish.mjs +95 -5
- package/setup.mjs +17 -17
- package/task-executor.mjs +3 -3
- package/telegram-bot.mjs +1 -1
- package/telegram-sentinel.mjs +1 -1
- package/ui/modules/settings-schema.js +10 -10
- package/ui-server.mjs +17 -17
- package/update-check.mjs +7 -7
- package/ve-orchestrator.ps1 +1 -1
- package/worktree-manager.mjs +2 -2
package/.env.example
CHANGED
|
@@ -109,7 +109,7 @@ TELEGRAM_MINIAPP_ENABLED=false
|
|
|
109
109
|
# Keep Telegram command availability even when openfleet is down.
|
|
110
110
|
# Sentinel can auto-restart monitor, detect crash loops, and run repair-agent.
|
|
111
111
|
# Auto-start sentinel whenever openfleet starts (default: disabled)
|
|
112
|
-
#
|
|
112
|
+
# OPENFLEET_SENTINEL_AUTO_START=true
|
|
113
113
|
# Auto-restart monitor when sentinel detects monitor down/crash (default: 1)
|
|
114
114
|
# SENTINEL_AUTO_RESTART_MONITOR=true
|
|
115
115
|
# Crash-loop threshold within rolling window (default: 3)
|
|
@@ -322,9 +322,9 @@ TELEGRAM_MINIAPP_ENABLED=false
|
|
|
322
322
|
# Auto-assign task creator/login when creating issues (default: true)
|
|
323
323
|
# GITHUB_AUTO_ASSIGN_CREATOR=true
|
|
324
324
|
# Codex task scoping label policy (only matching labels are picked by openfleet)
|
|
325
|
-
#
|
|
326
|
-
#
|
|
327
|
-
#
|
|
325
|
+
# OPENFLEET_TASK_LABEL=openfleet
|
|
326
|
+
# OPENFLEET_TASK_LABELS=openfleet,codex-mointor
|
|
327
|
+
# OPENFLEET_ENFORCE_TASK_LABEL=true
|
|
328
328
|
# Optional issue fetch cap per sync/poll cycle (default: 1000)
|
|
329
329
|
# GITHUB_ISSUES_LIST_LIMIT=1000
|
|
330
330
|
|
|
@@ -427,12 +427,12 @@ TELEGRAM_MINIAPP_ENABLED=false
|
|
|
427
427
|
# PID file: .cache/openfleet.pid
|
|
428
428
|
# Logs: logs/daemon.log
|
|
429
429
|
# Daemon crash supervision (monitor child):
|
|
430
|
-
#
|
|
431
|
-
#
|
|
430
|
+
# OPENFLEET_DAEMON_RESTART_DELAY_MS=5000
|
|
431
|
+
# OPENFLEET_DAEMON_MAX_RESTARTS=0 # 0 = unlimited
|
|
432
432
|
# Consider any crash within this window as an instant startup failure (default: 15000)
|
|
433
|
-
#
|
|
433
|
+
# OPENFLEET_DAEMON_INSTANT_CRASH_WINDOW_MS=15000
|
|
434
434
|
# Stop auto-restarts after this many instant failures in a row (default: 3)
|
|
435
|
-
#
|
|
435
|
+
# OPENFLEET_DAEMON_MAX_INSTANT_RESTARTS=3
|
|
436
436
|
|
|
437
437
|
# ─── Vibe-Kanban ──────────────────────────────────────────────────────────────
|
|
438
438
|
# Base URL for the Vibe-Kanban API (default: http://127.0.0.1:54089)
|
|
@@ -492,7 +492,7 @@ VK_RECOVERY_PORT=54089
|
|
|
492
492
|
# Target branch for PR checks/merge (default: origin/main)
|
|
493
493
|
# VK_TARGET_BRANCH=origin/main
|
|
494
494
|
# Default upstream/base branch for openfleet tasks (overrides VK_TARGET_BRANCH)
|
|
495
|
-
#
|
|
495
|
+
# OPENFLEET_TASK_UPSTREAM=origin/ve/openfleet-generic
|
|
496
496
|
|
|
497
497
|
# ─── Codex / AI Provider ─────────────────────────────────────────────────────
|
|
498
498
|
# The Codex SDK uses OpenAI-compatible configuration that has been setup in ~/.codex/config.toml -
|
|
@@ -544,39 +544,39 @@ VK_RECOVERY_PORT=54089
|
|
|
544
544
|
#
|
|
545
545
|
# Hook profile for setup/non-interactive runs:
|
|
546
546
|
# strict | balanced | lightweight | none
|
|
547
|
-
#
|
|
547
|
+
# OPENFLEET_HOOK_PROFILE=strict
|
|
548
548
|
#
|
|
549
549
|
# Which agents should receive generated hook files (comma-separated):
|
|
550
550
|
# codex,claude,copilot
|
|
551
|
-
#
|
|
551
|
+
# OPENFLEET_HOOK_TARGETS=codex,claude,copilot
|
|
552
552
|
#
|
|
553
553
|
# Set to false to skip hook scaffolding during setup.
|
|
554
|
-
#
|
|
554
|
+
# OPENFLEET_HOOKS_ENABLED=true
|
|
555
555
|
# Set to true to overwrite existing generated hook files.
|
|
556
|
-
#
|
|
556
|
+
# OPENFLEET_HOOKS_OVERWRITE=false
|
|
557
557
|
# Optional overrides for generated bridge command tokens.
|
|
558
558
|
# Defaults are portable across workstations:
|
|
559
559
|
# node scripts/openfleet/agent-hook-bridge.mjs
|
|
560
|
-
#
|
|
561
|
-
#
|
|
560
|
+
# OPENFLEET_HOOK_NODE_BIN=node
|
|
561
|
+
# OPENFLEET_HOOK_BRIDGE_PATH=scripts/openfleet/agent-hook-bridge.mjs
|
|
562
562
|
#
|
|
563
563
|
# Optional per-event command overrides (separate multiple commands with ';;').
|
|
564
564
|
# Use value 'none' to disable that event in generated .codex/hooks.json.
|
|
565
|
-
#
|
|
566
|
-
#
|
|
567
|
-
#
|
|
568
|
-
#
|
|
569
|
-
#
|
|
565
|
+
# OPENFLEET_HOOK_PREPUSH=go vet ./...;;go build ./...
|
|
566
|
+
# OPENFLEET_HOOK_PRECOMMIT=gofmt -l .
|
|
567
|
+
# OPENFLEET_HOOK_TASK_COMPLETE=echo \"task completed\"
|
|
568
|
+
# OPENFLEET_HOOK_SESSION_START=echo \"session start\"
|
|
569
|
+
# OPENFLEET_HOOK_SESSION_STOP=echo \"session stop\"
|
|
570
570
|
#
|
|
571
571
|
# Built-in hook behavior inside openfleet runtime:
|
|
572
572
|
# force (default), auto, off
|
|
573
|
-
#
|
|
574
|
-
#
|
|
575
|
-
#
|
|
576
|
-
#
|
|
573
|
+
# OPENFLEET_HOOKS_BUILTINS_MODE=force
|
|
574
|
+
# OPENFLEET_HOOKS_DISABLE_PREPUSH=false
|
|
575
|
+
# OPENFLEET_HOOKS_DISABLE_TASK_COMPLETE=false
|
|
576
|
+
# OPENFLEET_HOOKS_DISABLE_HEALTH_CHECK=false
|
|
577
577
|
|
|
578
578
|
# Force hooks to fire even for non-managed sessions (debug only):
|
|
579
|
-
#
|
|
579
|
+
# OPENFLEET_HOOKS_FORCE=false
|
|
580
580
|
|
|
581
581
|
# VE_MANAGED is auto-set by openfleet at startup. Agent hook bridge
|
|
582
582
|
# scripts check for this and exit silently if not present, preventing
|
|
@@ -735,11 +735,15 @@ COMPLEXITY_ROUTING_ENABLED=true
|
|
|
735
735
|
# COMPLEXITY_ROUTING_CODEX_LOW_REASONING=low
|
|
736
736
|
# COMPLEXITY_ROUTING_CODEX_MEDIUM_MODEL=gpt-5.2-codex
|
|
737
737
|
# COMPLEXITY_ROUTING_CODEX_MEDIUM_VARIANT=DEFAULT
|
|
738
|
+
# COMPLEXITY_ROUTING_CODEX_MEDIUM_REASONING=medium
|
|
738
739
|
# COMPLEXITY_ROUTING_CODEX_HIGH_MODEL=gpt-5.1-codex-max
|
|
739
740
|
# COMPLEXITY_ROUTING_CODEX_HIGH_VARIANT=GPT51_CODEX_MAX
|
|
740
|
-
#
|
|
741
|
-
#
|
|
742
|
-
#
|
|
741
|
+
# COMPLEXITY_ROUTING_CODEX_HIGH_REASONING=high
|
|
742
|
+
# COMPLEXITY_ROUTING_COPILOT_LOW_MODEL=claude-haiku-4.5
|
|
743
|
+
# COMPLEXITY_ROUTING_COPILOT_MEDIUM_MODEL=claude-sonnet-4.5
|
|
744
|
+
# COMPLEXITY_ROUTING_COPILOT_MEDIUM_REASONING=medium
|
|
745
|
+
# COMPLEXITY_ROUTING_COPILOT_HIGH_MODEL=claude-opus-4.6
|
|
746
|
+
# COMPLEXITY_ROUTING_COPILOT_HIGH_REASONING=high
|
|
743
747
|
|
|
744
748
|
# ─── Shared Cloud Workspaces ────────────────────────────────────────────────
|
|
745
749
|
# Registry file for shared workspace leasing (default: .cache/openfleet/shared-workspaces.json)
|
|
@@ -812,11 +816,11 @@ COPILOT_CLOUD_DISABLED=true
|
|
|
812
816
|
# .openfleet/agents/*.md
|
|
813
817
|
# Files in that folder are loaded automatically and are intended for per-project customization.
|
|
814
818
|
# You can also override any prompt path explicitly with env vars:
|
|
815
|
-
#
|
|
816
|
-
#
|
|
817
|
-
#
|
|
818
|
-
#
|
|
819
|
-
#
|
|
819
|
+
# OPENFLEET_PROMPT_PLANNER=.openfleet/agents/task-planner.md
|
|
820
|
+
# OPENFLEET_PROMPT_MONITOR_MONITOR=.openfleet/agents/monitor-monitor.md
|
|
821
|
+
# OPENFLEET_PROMPT_TASK_EXECUTOR=.openfleet/agents/task-executor.md
|
|
822
|
+
# OPENFLEET_PROMPT_REVIEWER=.openfleet/agents/reviewer.md
|
|
823
|
+
# OPENFLEET_PROMPT_SDK_CONFLICT_RESOLVER=.openfleet/agents/sdk-conflict-resolver.md
|
|
820
824
|
|
|
821
825
|
# ─── Dependabot / Bot PR Auto-Merge ───────────────────────────────────────────
|
|
822
826
|
# Auto-merge Dependabot (and other bot) PRs after all CI checks pass.
|
|
@@ -860,7 +864,7 @@ COPILOT_CLOUD_DISABLED=true
|
|
|
860
864
|
|
|
861
865
|
# ─── Advanced ─────────────────────────────────────────────────────────────────
|
|
862
866
|
# Override openfleet config directory (where .env and config live)
|
|
863
|
-
#
|
|
867
|
+
# OPENFLEET_DIR=/path/to/scripts/openfleet
|
|
864
868
|
# Max orchestrator restarts (0 = unlimited)
|
|
865
869
|
# MAX_RESTARTS=0
|
|
866
870
|
# Restart delay in milliseconds
|
package/agent-hook-bridge.mjs
CHANGED
|
@@ -166,7 +166,7 @@ async function run() {
|
|
|
166
166
|
// openfleet sets VE_MANAGED=1 in all spawned agent environments.
|
|
167
167
|
// If this env var is missing, we're running inside a standalone agent session
|
|
168
168
|
// that just happens to have the hook files in its config — exit silently.
|
|
169
|
-
if (!process.env.VE_MANAGED && !process.env.
|
|
169
|
+
if (!process.env.VE_MANAGED && !process.env.OPENFLEET_HOOKS_FORCE) {
|
|
170
170
|
process.exit(0);
|
|
171
171
|
}
|
|
172
172
|
|
package/agent-hooks.mjs
CHANGED
|
@@ -617,7 +617,7 @@ export async function executeBlockingHooks(event, context = {}) {
|
|
|
617
617
|
export function registerBuiltinHooks(options = {}) {
|
|
618
618
|
const modeRaw =
|
|
619
619
|
options.mode ??
|
|
620
|
-
process.env.
|
|
620
|
+
process.env.OPENFLEET_HOOKS_BUILTINS_MODE ??
|
|
621
621
|
process.env.VE_HOOK_BUILTINS_MODE ??
|
|
622
622
|
"force";
|
|
623
623
|
const mode = String(modeRaw).trim().toLowerCase();
|
|
@@ -633,12 +633,12 @@ export function registerBuiltinHooks(options = {}) {
|
|
|
633
633
|
(hook) => !hook.builtin,
|
|
634
634
|
);
|
|
635
635
|
const skipPrePush =
|
|
636
|
-
envFlag("
|
|
636
|
+
envFlag("OPENFLEET_HOOKS_DISABLE_PREPUSH", false) ||
|
|
637
637
|
envFlag("VE_HOOK_DISABLE_PREPUSH", false) ||
|
|
638
638
|
(mode === "auto" && hasCustomPrePush);
|
|
639
639
|
const skipTaskComplete =
|
|
640
|
-
envFlag("
|
|
641
|
-
envFlag("
|
|
640
|
+
envFlag("OPENFLEET_HOOKS_DISABLE_TASK_COMPLETE", false) ||
|
|
641
|
+
envFlag("OPENFLEET_HOOKS_DISABLE_VALIDATION", false) ||
|
|
642
642
|
envFlag("VE_HOOK_DISABLE_TASK_COMPLETE", false) ||
|
|
643
643
|
(mode === "auto" && hasCustomTaskComplete);
|
|
644
644
|
|
|
@@ -680,7 +680,7 @@ export function registerBuiltinHooks(options = {}) {
|
|
|
680
680
|
// ── SessionStart: worktree health check ──
|
|
681
681
|
// Verifies the worktree directory exists, is a valid git repo,
|
|
682
682
|
// and the expected branch is checked out. Retryable for transient git issues.
|
|
683
|
-
const skipHealthCheck = envFlag("
|
|
683
|
+
const skipHealthCheck = envFlag("OPENFLEET_HOOKS_DISABLE_HEALTH_CHECK", false);
|
|
684
684
|
if (!skipHealthCheck) {
|
|
685
685
|
const healthCmd = IS_WINDOWS
|
|
686
686
|
? 'powershell -NoProfile -Command "if (-not (Test-Path .git)) { if (-not (git rev-parse --git-dir 2>$null)) { Write-Error \'Not a git repository\'; exit 1 } }; git status --porcelain 2>&1 | Out-Null; if ($LASTEXITCODE -ne 0) { Write-Error \'git status failed\'; exit 1 }; Write-Host \'OK: worktree healthy\'"'
|
package/agent-prompts.mjs
CHANGED
|
@@ -119,7 +119,7 @@ export const AGENT_PROMPT_DEFINITIONS = Object.freeze(
|
|
|
119
119
|
PROMPT_DEFS.map((item) =>
|
|
120
120
|
Object.freeze({
|
|
121
121
|
...item,
|
|
122
|
-
envVar: `
|
|
122
|
+
envVar: `OPENFLEET_PROMPT_${toEnvSuffix(item.key)}`,
|
|
123
123
|
defaultRelativePath: `${PROMPT_WORKSPACE_DIR}/${item.filename}`,
|
|
124
124
|
}),
|
|
125
125
|
),
|
|
@@ -562,7 +562,7 @@ export function getAgentPromptDefinitions() {
|
|
|
562
562
|
|
|
563
563
|
export function getDefaultPromptWorkspace(repoRoot) {
|
|
564
564
|
const override = String(
|
|
565
|
-
process.env.
|
|
565
|
+
process.env.OPENFLEET_PROMPT_WORKSPACE || "",
|
|
566
566
|
).trim();
|
|
567
567
|
if (override) {
|
|
568
568
|
return isAbsolute(override)
|
|
@@ -607,13 +607,13 @@ export function ensureAgentPromptWorkspace(repoRoot) {
|
|
|
607
607
|
mkdirSync(workspaceDir, { recursive: true });
|
|
608
608
|
} catch (err) {
|
|
609
609
|
const fallbackRoot = resolve(
|
|
610
|
-
process.env.
|
|
610
|
+
process.env.OPENFLEET_HOME ||
|
|
611
611
|
process.env.HOME ||
|
|
612
612
|
process.env.USERPROFILE ||
|
|
613
613
|
homedir(),
|
|
614
614
|
);
|
|
615
615
|
const fallbackDir = resolve(fallbackRoot, PROMPT_WORKSPACE_DIR);
|
|
616
|
-
process.env.
|
|
616
|
+
process.env.OPENFLEET_PROMPT_WORKSPACE = fallbackDir;
|
|
617
617
|
workspaceDir = fallbackDir;
|
|
618
618
|
mkdirSync(workspaceDir, { recursive: true });
|
|
619
619
|
console.warn(
|
package/cli.mjs
CHANGED
|
@@ -140,9 +140,9 @@ function showHelp() {
|
|
|
140
140
|
5. Built-in defaults
|
|
141
141
|
|
|
142
142
|
Auto-update environment variables:
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
143
|
+
OPENFLEET_SKIP_UPDATE_CHECK=1 Disable startup version check
|
|
144
|
+
OPENFLEET_SKIP_AUTO_UPDATE=1 Disable background polling
|
|
145
|
+
OPENFLEET_UPDATE_INTERVAL_MS=N Override poll interval (default: 600000)
|
|
146
146
|
|
|
147
147
|
See .env.example for all environment variables.
|
|
148
148
|
|
|
@@ -199,23 +199,23 @@ const SENTINEL_SCRIPT_PATH = fileURLToPath(
|
|
|
199
199
|
new URL("./telegram-sentinel.mjs", import.meta.url),
|
|
200
200
|
);
|
|
201
201
|
const IS_DAEMON_CHILD =
|
|
202
|
-
args.includes("--daemon-child") || process.env.
|
|
202
|
+
args.includes("--daemon-child") || process.env.OPENFLEET_DAEMON === "1";
|
|
203
203
|
const DAEMON_RESTART_DELAY_MS = Math.max(
|
|
204
204
|
1000,
|
|
205
|
-
Number(process.env.
|
|
205
|
+
Number(process.env.OPENFLEET_DAEMON_RESTART_DELAY_MS || 5000) || 5000,
|
|
206
206
|
);
|
|
207
207
|
const DAEMON_MAX_RESTARTS = Math.max(
|
|
208
208
|
0,
|
|
209
|
-
Number(process.env.
|
|
209
|
+
Number(process.env.OPENFLEET_DAEMON_MAX_RESTARTS || 0) || 0,
|
|
210
210
|
);
|
|
211
211
|
const DAEMON_INSTANT_CRASH_WINDOW_MS = Math.max(
|
|
212
212
|
1000,
|
|
213
|
-
Number(process.env.
|
|
213
|
+
Number(process.env.OPENFLEET_DAEMON_INSTANT_CRASH_WINDOW_MS || 15000) ||
|
|
214
214
|
15000,
|
|
215
215
|
);
|
|
216
216
|
const DAEMON_MAX_INSTANT_RESTARTS = Math.max(
|
|
217
217
|
1,
|
|
218
|
-
Number(process.env.
|
|
218
|
+
Number(process.env.OPENFLEET_DAEMON_MAX_INSTANT_RESTARTS || 3) || 3,
|
|
219
219
|
);
|
|
220
220
|
let daemonRestartCount = 0;
|
|
221
221
|
const daemonCrashTracker = createDaemonCrashTracker({
|
|
@@ -283,7 +283,7 @@ async function ensureSentinelRunning(options = {}) {
|
|
|
283
283
|
windowsHide: process.platform === "win32",
|
|
284
284
|
env: {
|
|
285
285
|
...process.env,
|
|
286
|
-
|
|
286
|
+
OPENFLEET_SENTINEL_COMPANION: "1",
|
|
287
287
|
},
|
|
288
288
|
cwd: process.cwd(),
|
|
289
289
|
});
|
|
@@ -380,7 +380,7 @@ function startDaemon() {
|
|
|
380
380
|
detached: true,
|
|
381
381
|
stdio: "ignore",
|
|
382
382
|
windowsHide: process.platform === "win32",
|
|
383
|
-
env: { ...process.env,
|
|
383
|
+
env: { ...process.env, OPENFLEET_DAEMON: "1" },
|
|
384
384
|
cwd: process.cwd(),
|
|
385
385
|
},
|
|
386
386
|
);
|
|
@@ -510,7 +510,7 @@ async function main() {
|
|
|
510
510
|
// Write PID file if running as daemon child
|
|
511
511
|
if (
|
|
512
512
|
args.includes("--daemon-child") ||
|
|
513
|
-
process.env.
|
|
513
|
+
process.env.OPENFLEET_DAEMON === "1"
|
|
514
514
|
) {
|
|
515
515
|
writePidFile(process.pid);
|
|
516
516
|
// Redirect console to log file on daemon child
|
|
@@ -556,15 +556,15 @@ async function main() {
|
|
|
556
556
|
|
|
557
557
|
const sentinelRequested =
|
|
558
558
|
args.includes("--sentinel") ||
|
|
559
|
-
parseBoolEnv(process.env.
|
|
559
|
+
parseBoolEnv(process.env.OPENFLEET_SENTINEL_AUTO_START, false);
|
|
560
560
|
if (sentinelRequested) {
|
|
561
561
|
const sentinel = await ensureSentinelRunning({ quiet: false });
|
|
562
562
|
if (!sentinel.ok) {
|
|
563
563
|
const mode = args.includes("--sentinel")
|
|
564
564
|
? "requested by --sentinel"
|
|
565
|
-
: "requested by
|
|
565
|
+
: "requested by OPENFLEET_SENTINEL_AUTO_START";
|
|
566
566
|
const strictSentinel = parseBoolEnv(
|
|
567
|
-
process.env.
|
|
567
|
+
process.env.OPENFLEET_SENTINEL_STRICT,
|
|
568
568
|
false,
|
|
569
569
|
);
|
|
570
570
|
const prefix = strictSentinel ? "✖" : "⚠";
|
|
@@ -649,7 +649,7 @@ async function main() {
|
|
|
649
649
|
|
|
650
650
|
// Propagate --no-auto-update to env for monitor.mjs to pick up
|
|
651
651
|
if (args.includes("--no-auto-update")) {
|
|
652
|
-
process.env.
|
|
652
|
+
process.env.OPENFLEET_SKIP_AUTO_UPDATE = "1";
|
|
653
653
|
}
|
|
654
654
|
|
|
655
655
|
// Mark all child processes as openfleet managed.
|
|
@@ -661,7 +661,7 @@ async function main() {
|
|
|
661
661
|
if (args.includes("--setup") || args.includes("setup")) {
|
|
662
662
|
const configDirArg = getArgValue("--config-dir");
|
|
663
663
|
if (configDirArg) {
|
|
664
|
-
process.env.
|
|
664
|
+
process.env.OPENFLEET_DIR = configDirArg;
|
|
665
665
|
}
|
|
666
666
|
const { runSetup } = await import("./setup.mjs");
|
|
667
667
|
await runSetup();
|
|
@@ -682,13 +682,75 @@ async function main() {
|
|
|
682
682
|
console.log("\n 🚀 First run detected — launching setup wizard...\n");
|
|
683
683
|
const configDirArg = getArgValue("--config-dir");
|
|
684
684
|
if (configDirArg) {
|
|
685
|
-
process.env.
|
|
685
|
+
process.env.OPENFLEET_DIR = configDirArg;
|
|
686
686
|
}
|
|
687
687
|
const { runSetup } = await import("./setup.mjs");
|
|
688
688
|
await runSetup();
|
|
689
689
|
console.log("\n Setup complete! Starting openfleet...\n");
|
|
690
690
|
}
|
|
691
691
|
|
|
692
|
+
// ── Handle --echo-logs: tail the active monitor's log instead of spawning a new instance ──
|
|
693
|
+
if (args.includes("--echo-logs")) {
|
|
694
|
+
// Search for the monitor PID file in common cache locations
|
|
695
|
+
const candidatePidFiles = [
|
|
696
|
+
process.env.OPENFLEET_DIR
|
|
697
|
+
? resolve(process.env.OPENFLEET_DIR, ".cache", "openfleet.pid")
|
|
698
|
+
: null,
|
|
699
|
+
resolve(__dirname, "..", ".cache", "openfleet.pid"),
|
|
700
|
+
resolve(process.cwd(), ".cache", "openfleet.pid"),
|
|
701
|
+
].filter(Boolean);
|
|
702
|
+
|
|
703
|
+
let activePidFile = null;
|
|
704
|
+
for (const f of candidatePidFiles) {
|
|
705
|
+
if (existsSync(f)) {
|
|
706
|
+
activePidFile = f;
|
|
707
|
+
break;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
if (activePidFile) {
|
|
712
|
+
try {
|
|
713
|
+
const pidData = JSON.parse(readFileSync(activePidFile, "utf8"));
|
|
714
|
+
const monitorPid = Number(pidData.pid);
|
|
715
|
+
const monitorPath = (pidData.argv || [])[1] || "";
|
|
716
|
+
|
|
717
|
+
let isAlive = false;
|
|
718
|
+
try {
|
|
719
|
+
process.kill(monitorPid, 0);
|
|
720
|
+
isAlive = true;
|
|
721
|
+
} catch {}
|
|
722
|
+
|
|
723
|
+
if (isAlive && monitorPath) {
|
|
724
|
+
const logDir = resolve(dirname(monitorPath), "logs");
|
|
725
|
+
const daemonLog = resolve(logDir, "daemon.log");
|
|
726
|
+
const monitorLog = resolve(logDir, "monitor.log");
|
|
727
|
+
const logFile = existsSync(daemonLog) ? daemonLog : monitorLog;
|
|
728
|
+
|
|
729
|
+
if (existsSync(logFile)) {
|
|
730
|
+
console.log(
|
|
731
|
+
`\n Tailing logs for active openfleet (PID ${monitorPid}):\n ${logFile}\n`,
|
|
732
|
+
);
|
|
733
|
+
await new Promise((res) => {
|
|
734
|
+
const tail = spawn("tail", ["-f", "-n", "200", logFile], {
|
|
735
|
+
stdio: "inherit",
|
|
736
|
+
});
|
|
737
|
+
tail.on("exit", res);
|
|
738
|
+
process.on("SIGINT", () => {
|
|
739
|
+
tail.kill();
|
|
740
|
+
res();
|
|
741
|
+
});
|
|
742
|
+
});
|
|
743
|
+
process.exit(0);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
} catch {
|
|
747
|
+
// best-effort — fall through to normal start
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// No active monitor found — start normally; echoLogs will be picked up by config.mjs
|
|
752
|
+
}
|
|
753
|
+
|
|
692
754
|
// Fork monitor as a child process — enables self-restart on source changes.
|
|
693
755
|
// When monitor exits with code 75, cli re-forks with a fresh ESM module cache.
|
|
694
756
|
await runMonitor();
|
package/config-doctor.mjs
CHANGED
|
@@ -82,7 +82,7 @@ function isWslInteropRuntime() {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
function resolveConfigDir(repoRoot) {
|
|
85
|
-
const explicit = process.env.
|
|
85
|
+
const explicit = process.env.OPENFLEET_DIR;
|
|
86
86
|
if (explicit) return resolve(explicit);
|
|
87
87
|
|
|
88
88
|
const repoPath = resolve(repoRoot || process.cwd());
|
package/config.mjs
CHANGED
|
@@ -726,7 +726,7 @@ export function loadConfig(argv = process.argv, options = {}) {
|
|
|
726
726
|
// Determine config directory (where openfleet stores its config)
|
|
727
727
|
const configDir =
|
|
728
728
|
cli["config-dir"] ||
|
|
729
|
-
process.env.
|
|
729
|
+
process.env.OPENFLEET_DIR ||
|
|
730
730
|
resolveConfigDir(repoRootForConfig);
|
|
731
731
|
|
|
732
732
|
const configFile = loadConfigFile(configDir);
|
|
@@ -740,8 +740,8 @@ export function loadConfig(argv = process.argv, options = {}) {
|
|
|
740
740
|
const repoSelection =
|
|
741
741
|
cli["repo-name"] ||
|
|
742
742
|
cli.repository ||
|
|
743
|
-
process.env.
|
|
744
|
-
process.env.
|
|
743
|
+
process.env.OPENFLEET_REPO ||
|
|
744
|
+
process.env.OPENFLEET_REPO_NAME ||
|
|
745
745
|
process.env.REPO_NAME ||
|
|
746
746
|
configData.defaultRepository ||
|
|
747
747
|
configData.defaultRepo ||
|
|
@@ -774,8 +774,8 @@ export function loadConfig(argv = process.argv, options = {}) {
|
|
|
774
774
|
(profiles.default ? "default" : "");
|
|
775
775
|
const profileName =
|
|
776
776
|
cli.profile ||
|
|
777
|
-
process.env.
|
|
778
|
-
process.env.
|
|
777
|
+
process.env.OPENFLEET_PROFILE ||
|
|
778
|
+
process.env.OPENFLEET_ENV_PROFILE ||
|
|
779
779
|
selectedRepository?.profile ||
|
|
780
780
|
selectedRepository?.envProfile ||
|
|
781
781
|
defaultProfile ||
|
|
@@ -838,7 +838,7 @@ export function loadConfig(argv = process.argv, options = {}) {
|
|
|
838
838
|
const mode =
|
|
839
839
|
(
|
|
840
840
|
cli.mode ||
|
|
841
|
-
process.env.
|
|
841
|
+
process.env.OPENFLEET_MODE ||
|
|
842
842
|
configData.mode ||
|
|
843
843
|
selectedRepository?.mode ||
|
|
844
844
|
""
|
|
@@ -943,20 +943,20 @@ export function loadConfig(argv = process.argv, options = {}) {
|
|
|
943
943
|
const interactiveShellEnabled =
|
|
944
944
|
flags.has("shell") ||
|
|
945
945
|
flags.has("interactive") ||
|
|
946
|
-
isEnvEnabled(process.env.
|
|
947
|
-
isEnvEnabled(process.env.
|
|
946
|
+
isEnvEnabled(process.env.OPENFLEET_SHELL, false) ||
|
|
947
|
+
isEnvEnabled(process.env.OPENFLEET_INTERACTIVE, false) ||
|
|
948
948
|
configData.interactiveShellEnabled === true ||
|
|
949
949
|
configData.shellEnabled === true;
|
|
950
950
|
const preflightEnabled = flags.has("no-preflight")
|
|
951
951
|
? false
|
|
952
952
|
: configData.preflightEnabled !== undefined
|
|
953
953
|
? configData.preflightEnabled
|
|
954
|
-
: isEnvEnabled(process.env.
|
|
954
|
+
: isEnvEnabled(process.env.OPENFLEET_PREFLIGHT_DISABLED, false)
|
|
955
955
|
? false
|
|
956
956
|
: true;
|
|
957
957
|
const preflightRetryMs = Number(
|
|
958
958
|
cli["preflight-retry"] ||
|
|
959
|
-
process.env.
|
|
959
|
+
process.env.OPENFLEET_PREFLIGHT_RETRY_MS ||
|
|
960
960
|
configData.preflightRetryMs ||
|
|
961
961
|
"300000",
|
|
962
962
|
);
|
|
@@ -1656,7 +1656,7 @@ function detectProjectName(configDir, repoRoot) {
|
|
|
1656
1656
|
}
|
|
1657
1657
|
|
|
1658
1658
|
function findOrchestratorScript(configDir, repoRoot) {
|
|
1659
|
-
const shellModeEnv = String(process.env.
|
|
1659
|
+
const shellModeEnv = String(process.env.OPENFLEET_SHELL_MODE || "")
|
|
1660
1660
|
.trim()
|
|
1661
1661
|
.toLowerCase();
|
|
1662
1662
|
const shellModeRequested = ["1", "true", "yes", "on"].includes(shellModeEnv);
|
package/hook-profiles.mjs
CHANGED
|
@@ -11,13 +11,13 @@ const LEGACY_BRIDGE_SNIPPET = "scripts/openfleet/agent-hook-bridge.mjs";
|
|
|
11
11
|
const DEFAULT_BRIDGE_SCRIPT_PATH = resolve(__dirname, "agent-hook-bridge.mjs");
|
|
12
12
|
|
|
13
13
|
function getHookNodeBinary() {
|
|
14
|
-
const configured = String(process.env.
|
|
14
|
+
const configured = String(process.env.OPENFLEET_HOOK_NODE_BIN || "").trim();
|
|
15
15
|
return configured || "node";
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
function getHookBridgeScriptPath() {
|
|
19
19
|
const configured = String(
|
|
20
|
-
process.env.
|
|
20
|
+
process.env.OPENFLEET_HOOK_BRIDGE_PATH || "",
|
|
21
21
|
).trim();
|
|
22
22
|
return configured || DEFAULT_BRIDGE_SCRIPT_PATH;
|
|
23
23
|
}
|
|
@@ -242,23 +242,23 @@ export function normalizeHookTargets(value) {
|
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
export function buildHookScaffoldOptionsFromEnv(env = process.env) {
|
|
245
|
-
const profile = normalizeProfile(env.
|
|
245
|
+
const profile = normalizeProfile(env.OPENFLEET_HOOK_PROFILE);
|
|
246
246
|
return {
|
|
247
|
-
enabled: parseBoolean(env.
|
|
247
|
+
enabled: parseBoolean(env.OPENFLEET_HOOKS_ENABLED, true),
|
|
248
248
|
profile,
|
|
249
|
-
targets: normalizeHookTargets(env.
|
|
250
|
-
overwriteExisting: parseBoolean(env.
|
|
249
|
+
targets: normalizeHookTargets(env.OPENFLEET_HOOK_TARGETS),
|
|
250
|
+
overwriteExisting: parseBoolean(env.OPENFLEET_HOOKS_OVERWRITE, false),
|
|
251
251
|
commands: {
|
|
252
252
|
SessionStart: normalizeOverrideCommands(
|
|
253
|
-
env.
|
|
253
|
+
env.OPENFLEET_HOOK_SESSION_START,
|
|
254
254
|
),
|
|
255
255
|
SessionStop: normalizeOverrideCommands(
|
|
256
|
-
env.
|
|
256
|
+
env.OPENFLEET_HOOK_SESSION_STOP,
|
|
257
257
|
),
|
|
258
|
-
PrePush: normalizeOverrideCommands(env.
|
|
259
|
-
PreCommit: normalizeOverrideCommands(env.
|
|
258
|
+
PrePush: normalizeOverrideCommands(env.OPENFLEET_HOOK_PREPUSH),
|
|
259
|
+
PreCommit: normalizeOverrideCommands(env.OPENFLEET_HOOK_PRECOMMIT),
|
|
260
260
|
TaskComplete: normalizeOverrideCommands(
|
|
261
|
-
env.
|
|
261
|
+
env.OPENFLEET_HOOK_TASK_COMPLETE,
|
|
262
262
|
),
|
|
263
263
|
},
|
|
264
264
|
};
|
|
@@ -545,10 +545,10 @@ function buildDisableEnv(hookConfig) {
|
|
|
545
545
|
const hasTaskComplete = Array.isArray(hookConfig.hooks?.TaskComplete);
|
|
546
546
|
|
|
547
547
|
return {
|
|
548
|
-
|
|
548
|
+
OPENFLEET_HOOKS_BUILTINS_MODE:
|
|
549
549
|
hasPrePush || hasTaskComplete ? "auto" : "off",
|
|
550
|
-
|
|
551
|
-
|
|
550
|
+
OPENFLEET_HOOKS_DISABLE_PREPUSH: hasPrePush ? "0" : "1",
|
|
551
|
+
OPENFLEET_HOOKS_DISABLE_TASK_COMPLETE: hasTaskComplete ? "0" : "1",
|
|
552
552
|
};
|
|
553
553
|
}
|
|
554
554
|
|
|
@@ -571,9 +571,9 @@ export function scaffoldAgentHookFiles(repoRoot, options = {}) {
|
|
|
571
571
|
|
|
572
572
|
if (!enabled) {
|
|
573
573
|
result.env = {
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
574
|
+
OPENFLEET_HOOKS_BUILTINS_MODE: "off",
|
|
575
|
+
OPENFLEET_HOOKS_DISABLE_PREPUSH: "1",
|
|
576
|
+
OPENFLEET_HOOKS_DISABLE_TASK_COMPLETE: "1",
|
|
577
577
|
};
|
|
578
578
|
return result;
|
|
579
579
|
}
|
package/kanban-adapter.mjs
CHANGED
|
@@ -862,13 +862,13 @@ class GitHubIssuesAdapter {
|
|
|
862
862
|
};
|
|
863
863
|
|
|
864
864
|
this._canonicalTaskLabel =
|
|
865
|
-
process.env.
|
|
865
|
+
process.env.OPENFLEET_TASK_LABEL || "openfleet";
|
|
866
866
|
this._taskScopeLabels = normalizeLabels(
|
|
867
|
-
process.env.
|
|
867
|
+
process.env.OPENFLEET_TASK_LABELS ||
|
|
868
868
|
`${this._canonicalTaskLabel},codex-mointor`,
|
|
869
869
|
);
|
|
870
870
|
this._enforceTaskLabel = parseBooleanEnv(
|
|
871
|
-
process.env.
|
|
871
|
+
process.env.OPENFLEET_ENFORCE_TASK_LABEL,
|
|
872
872
|
true,
|
|
873
873
|
);
|
|
874
874
|
|
|
@@ -3010,17 +3010,17 @@ class JiraAdapter {
|
|
|
3010
3010
|
process.env.JIRA_SUBTASK_PARENT_KEY || "",
|
|
3011
3011
|
).trim();
|
|
3012
3012
|
this._canonicalTaskLabel = String(
|
|
3013
|
-
process.env.
|
|
3013
|
+
process.env.OPENFLEET_TASK_LABEL || "openfleet",
|
|
3014
3014
|
)
|
|
3015
3015
|
.trim()
|
|
3016
3016
|
.toLowerCase();
|
|
3017
3017
|
this._taskScopeLabels = normalizeLabels(
|
|
3018
3018
|
process.env.JIRA_TASK_LABELS ||
|
|
3019
|
-
process.env.
|
|
3019
|
+
process.env.OPENFLEET_TASK_LABELS ||
|
|
3020
3020
|
`${this._canonicalTaskLabel},openfleet`,
|
|
3021
3021
|
).map((label) => this._sanitizeJiraLabel(label));
|
|
3022
3022
|
this._enforceTaskLabel = parseBooleanEnv(
|
|
3023
|
-
process.env.JIRA_ENFORCE_TASK_LABEL ?? process.env.
|
|
3023
|
+
process.env.JIRA_ENFORCE_TASK_LABEL ?? process.env.OPENFLEET_ENFORCE_TASK_LABEL,
|
|
3024
3024
|
true,
|
|
3025
3025
|
);
|
|
3026
3026
|
this._codexLabels = {
|
package/monitor.mjs
CHANGED
|
@@ -6109,9 +6109,9 @@ async function actOnAssessment(ctx, decision) {
|
|
|
6109
6109
|
// Use config-driven branch routing instead of hardcoded defaults
|
|
6110
6110
|
const DEFAULT_TARGET_BRANCH =
|
|
6111
6111
|
branchRouting?.defaultBranch || process.env.VK_TARGET_BRANCH || "origin/main";
|
|
6112
|
-
const
|
|
6112
|
+
const DEFAULT_OPENFLEET_UPSTREAM =
|
|
6113
6113
|
branchRouting?.scopeMap?.["openfleet"] ||
|
|
6114
|
-
process.env.
|
|
6114
|
+
process.env.OPENFLEET_TASK_UPSTREAM ||
|
|
6115
6115
|
"origin/ve/openfleet-generic";
|
|
6116
6116
|
|
|
6117
6117
|
/**
|
|
@@ -6303,7 +6303,7 @@ function resolveUpstreamFromTask(task) {
|
|
|
6303
6303
|
text.includes("@virtengine/openfleet") ||
|
|
6304
6304
|
text.includes("scripts/openfleet")
|
|
6305
6305
|
) {
|
|
6306
|
-
return
|
|
6306
|
+
return DEFAULT_OPENFLEET_UPSTREAM;
|
|
6307
6307
|
}
|
|
6308
6308
|
|
|
6309
6309
|
return null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@virtengine/openfleet",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.2",
|
|
4
4
|
"description": "AI-powered orchestrator supervisor — manages AI agent executors with failover, auto-restarts on failure, analyzes crashes with Codex SDK, creates PRs via Vibe-Kanban API, and sends Telegram notifications. Supports N executors with weighted distribution, multi-repo projects, and auto-setup.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache 2.0",
|
|
@@ -92,7 +92,11 @@
|
|
|
92
92
|
"prepush:check": "npm run syntax:check && npm test",
|
|
93
93
|
"prepublishOnly": "node prepublish-check.mjs",
|
|
94
94
|
"publish:env": "node publish.mjs",
|
|
95
|
-
"publish:dry-run": "node publish.mjs --dry-run"
|
|
95
|
+
"publish:dry-run": "node publish.mjs --dry-run",
|
|
96
|
+
"publish:minor": "node publish.mjs --bump minor",
|
|
97
|
+
"publish:major": "node publish.mjs --bump major",
|
|
98
|
+
"publish:minor:dry": "node publish.mjs --bump minor --dry-run",
|
|
99
|
+
"publish:major:dry": "node publish.mjs --bump major --dry-run"
|
|
96
100
|
},
|
|
97
101
|
"files": [
|
|
98
102
|
".env.example",
|
package/postinstall.mjs
CHANGED
package/preflight.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { resolve } from "node:path";
|
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
|
|
5
5
|
const isWindows = process.platform === "win32";
|
|
6
|
-
const MIN_FREE_GB = Number(process.env.
|
|
6
|
+
const MIN_FREE_GB = Number(process.env.OPENFLEET_MIN_FREE_GB || "10");
|
|
7
7
|
const MIN_FREE_BYTES = MIN_FREE_GB * 1024 * 1024 * 1024;
|
|
8
8
|
|
|
9
9
|
function runCommand(command, args, options = {}) {
|
|
@@ -167,7 +167,7 @@ function isShellModeRequested() {
|
|
|
167
167
|
.toLowerCase();
|
|
168
168
|
if (script.endsWith(".sh")) return true;
|
|
169
169
|
if (script.endsWith(".ps1")) return false;
|
|
170
|
-
if (parseEnvBool(process.env.
|
|
170
|
+
if (parseEnvBool(process.env.OPENFLEET_SHELL_MODE, false)) return true;
|
|
171
171
|
return !isWindows;
|
|
172
172
|
}
|
|
173
173
|
|
package/publish.mjs
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
//
|
|
3
|
+
// Usage:
|
|
4
|
+
// node publish.mjs # publish at current version (prompts login if no token)
|
|
5
|
+
// node publish.mjs --bump minor # 0.25.0 → 0.25.1 (patch digit)
|
|
6
|
+
// node publish.mjs --bump major # 0.25.0 → 0.26.0 (minor digit)
|
|
7
|
+
// node publish.mjs --bump minor --dry-run
|
|
8
|
+
//
|
|
9
|
+
// To set an automation token (Linux/macOS bash — NOT setx/PowerShell):
|
|
10
|
+
// export NPM_ACCESS_TOKEN=npm_xxxx && node publish.mjs --bump minor
|
|
11
|
+
// # or inline:
|
|
12
|
+
// NPM_ACCESS_TOKEN=npm_xxxx npm run publish:minor
|
|
13
|
+
//
|
|
14
|
+
// Terminology matches the openfleet convention (not semver):
|
|
15
|
+
// minor = last digit (semver patch)
|
|
16
|
+
// major = middle digit (semver minor)
|
|
2
17
|
|
|
3
18
|
import { spawnSync } from "node:child_process";
|
|
4
|
-
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
19
|
+
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
5
20
|
import { tmpdir } from "node:os";
|
|
6
21
|
import { join, resolve } from "node:path";
|
|
7
22
|
import { fileURLToPath } from "node:url";
|
|
@@ -64,8 +79,82 @@ function runCheck(label, command, args, env) {
|
|
|
64
79
|
|
|
65
80
|
const NPM_BIN = "npm";
|
|
66
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Bump the version in package.json and sync package-lock.json.
|
|
84
|
+
* type: "minor" → patch digit (0.25.0 → 0.25.1)
|
|
85
|
+
* "major" → middle digit (0.25.0 → 0.26.0)
|
|
86
|
+
* Returns the new version string.
|
|
87
|
+
*/
|
|
88
|
+
function bumpVersion(type, dryRun) {
|
|
89
|
+
const pkgPath = resolve(SCRIPT_DIR, "package.json");
|
|
90
|
+
const lockPath = resolve(SCRIPT_DIR, "package-lock.json");
|
|
91
|
+
|
|
92
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
93
|
+
const [maj, mid, pat] = pkg.version.split(".").map(Number);
|
|
94
|
+
|
|
95
|
+
let newVersion;
|
|
96
|
+
if (type === "minor") {
|
|
97
|
+
// minor = patch digit (last number)
|
|
98
|
+
newVersion = `${maj}.${mid}.${pat + 1}`;
|
|
99
|
+
} else if (type === "major") {
|
|
100
|
+
// major = middle digit, reset patch
|
|
101
|
+
newVersion = `${maj}.${mid + 1}.0`;
|
|
102
|
+
} else {
|
|
103
|
+
console.error(
|
|
104
|
+
`[publish] Unknown bump type "${type}". Use "minor" (0.25.0→0.25.1) or "major" (0.25.0→0.26.0).`,
|
|
105
|
+
);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
console.log(`[publish] Bumping version: ${pkg.version} → ${newVersion}`);
|
|
110
|
+
|
|
111
|
+
if (!dryRun) {
|
|
112
|
+
pkg.version = newVersion;
|
|
113
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
114
|
+
|
|
115
|
+
// Update package-lock.json in-place
|
|
116
|
+
try {
|
|
117
|
+
const lock = JSON.parse(readFileSync(lockPath, "utf8"));
|
|
118
|
+
lock.version = newVersion;
|
|
119
|
+
if (lock.packages?.[""] != null) {
|
|
120
|
+
lock.packages[""].version = newVersion;
|
|
121
|
+
}
|
|
122
|
+
writeFileSync(lockPath, JSON.stringify(lock, null, 2) + "\n", "utf8");
|
|
123
|
+
console.log(
|
|
124
|
+
`[publish] package.json + package-lock.json updated to ${newVersion}`,
|
|
125
|
+
);
|
|
126
|
+
} catch {
|
|
127
|
+
console.warn(
|
|
128
|
+
"[publish] Could not update package-lock.json — regenerating...",
|
|
129
|
+
);
|
|
130
|
+
spawnSync(NPM_BIN, ["install", "--package-lock-only", "--ignore-scripts"], {
|
|
131
|
+
stdio: "inherit",
|
|
132
|
+
cwd: SCRIPT_DIR,
|
|
133
|
+
shell: true,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
} else {
|
|
137
|
+
console.log(
|
|
138
|
+
`[publish] dry-run: would write version ${newVersion} to package.json & package-lock.json`,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return newVersion;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function getBumpArg() {
|
|
146
|
+
const idx = process.argv.indexOf("--bump");
|
|
147
|
+
if (idx !== -1 && process.argv[idx + 1]) return process.argv[idx + 1];
|
|
148
|
+
const flag = process.argv.find((a) => a.startsWith("--bump="));
|
|
149
|
+
if (flag) return flag.slice(7);
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
|
|
67
153
|
function main() {
|
|
154
|
+
const bumpType = getBumpArg();
|
|
68
155
|
const dryRun = hasArg("--dry-run");
|
|
156
|
+
if (bumpType) bumpVersion(bumpType, dryRun);
|
|
157
|
+
|
|
69
158
|
const tag = process.env.NPM_PUBLISH_TAG || "latest";
|
|
70
159
|
const otp = process.env.NPM_OTP || "";
|
|
71
160
|
const access = process.env.NPM_PUBLISH_ACCESS || "public";
|
|
@@ -73,10 +162,11 @@ function main() {
|
|
|
73
162
|
const token = process.env.NPM_ACCESS_TOKEN || process.env.NODE_AUTH_TOKEN || "";
|
|
74
163
|
|
|
75
164
|
if (!dryRun && !token) {
|
|
76
|
-
console.
|
|
77
|
-
"[publish]
|
|
165
|
+
console.log(
|
|
166
|
+
"[publish] No NPM_ACCESS_TOKEN found — npm will prompt for login.",
|
|
78
167
|
);
|
|
79
|
-
|
|
168
|
+
} else if (!dryRun && token) {
|
|
169
|
+
console.log("[publish] Token found — using NPM_ACCESS_TOKEN for auth.");
|
|
80
170
|
}
|
|
81
171
|
|
|
82
172
|
console.log(
|
|
@@ -105,7 +195,7 @@ function main() {
|
|
|
105
195
|
let tempConfig = null;
|
|
106
196
|
try {
|
|
107
197
|
const env = { ...process.env };
|
|
108
|
-
if (!dryRun) {
|
|
198
|
+
if (!dryRun && token) {
|
|
109
199
|
tempConfig = createEphemeralNpmrc(registry, token);
|
|
110
200
|
env.NPM_CONFIG_USERCONFIG = tempConfig.npmrcPath;
|
|
111
201
|
env.NODE_AUTH_TOKEN = token;
|
package/setup.mjs
CHANGED
|
@@ -97,7 +97,7 @@ function isPathInside(parent, child) {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
function resolveConfigDir(repoRoot) {
|
|
100
|
-
const explicit = process.env.
|
|
100
|
+
const explicit = process.env.OPENFLEET_DIR;
|
|
101
101
|
if (explicit) return resolve(explicit);
|
|
102
102
|
const repoPath = resolve(repoRoot || process.cwd());
|
|
103
103
|
const packageDir = resolve(__dirname);
|
|
@@ -2488,7 +2488,7 @@ async function main() {
|
|
|
2488
2488
|
|
|
2489
2489
|
const canonicalLabel = "openfleet";
|
|
2490
2490
|
const existingScopeLabels = String(
|
|
2491
|
-
process.env.
|
|
2491
|
+
process.env.OPENFLEET_TASK_LABELS || "",
|
|
2492
2492
|
)
|
|
2493
2493
|
.split(",")
|
|
2494
2494
|
.map((entry) => entry.trim().toLowerCase())
|
|
@@ -2499,9 +2499,9 @@ async function main() {
|
|
|
2499
2499
|
if (!existingScopeLabels.includes("codex-mointor")) {
|
|
2500
2500
|
existingScopeLabels.push("codex-mointor");
|
|
2501
2501
|
}
|
|
2502
|
-
env.
|
|
2503
|
-
env.
|
|
2504
|
-
env.
|
|
2502
|
+
env.OPENFLEET_TASK_LABEL = canonicalLabel;
|
|
2503
|
+
env.OPENFLEET_TASK_LABELS = existingScopeLabels.join(",");
|
|
2504
|
+
env.OPENFLEET_ENFORCE_TASK_LABEL = "true";
|
|
2505
2505
|
|
|
2506
2506
|
if (githubTaskMode === "kanban") {
|
|
2507
2507
|
env.GITHUB_PROJECT_OWNER =
|
|
@@ -2546,7 +2546,7 @@ async function main() {
|
|
|
2546
2546
|
projectTitle: env.GITHUB_PROJECT_TITLE || "OpenFleet",
|
|
2547
2547
|
projectOwner: env.GITHUB_PROJECT_OWNER || env.GITHUB_REPO_OWNER || "",
|
|
2548
2548
|
projectNumber: env.GITHUB_PROJECT_NUMBER || "",
|
|
2549
|
-
taskLabel: env.
|
|
2549
|
+
taskLabel: env.OPENFLEET_TASK_LABEL || "openfleet",
|
|
2550
2550
|
},
|
|
2551
2551
|
};
|
|
2552
2552
|
info(
|
|
@@ -2789,7 +2789,7 @@ async function main() {
|
|
|
2789
2789
|
const canonicalLabel = "openfleet";
|
|
2790
2790
|
const jiraScopeLabels = String(
|
|
2791
2791
|
process.env.JIRA_TASK_LABELS ||
|
|
2792
|
-
process.env.
|
|
2792
|
+
process.env.OPENFLEET_TASK_LABELS ||
|
|
2793
2793
|
"",
|
|
2794
2794
|
)
|
|
2795
2795
|
.split(",")
|
|
@@ -2801,10 +2801,10 @@ async function main() {
|
|
|
2801
2801
|
if (!jiraScopeLabels.includes("codex-mointor")) {
|
|
2802
2802
|
jiraScopeLabels.push("codex-mointor");
|
|
2803
2803
|
}
|
|
2804
|
-
env.
|
|
2805
|
-
env.
|
|
2806
|
-
env.
|
|
2807
|
-
env.JIRA_TASK_LABELS = env.
|
|
2804
|
+
env.OPENFLEET_TASK_LABEL = canonicalLabel;
|
|
2805
|
+
env.OPENFLEET_TASK_LABELS = jiraScopeLabels.join(",");
|
|
2806
|
+
env.OPENFLEET_ENFORCE_TASK_LABEL = "true";
|
|
2807
|
+
env.JIRA_TASK_LABELS = env.OPENFLEET_TASK_LABELS;
|
|
2808
2808
|
env.JIRA_ENFORCE_TASK_LABEL = "true";
|
|
2809
2809
|
|
|
2810
2810
|
if (hasJiraCreds) {
|
|
@@ -3244,9 +3244,9 @@ async function main() {
|
|
|
3244
3244
|
const profileMap = ["strict", "balanced", "lightweight", "none"];
|
|
3245
3245
|
let profile = "balanced";
|
|
3246
3246
|
let targets = ["codex", "claude", "copilot"];
|
|
3247
|
-
let prePushRaw = process.env.
|
|
3248
|
-
let preCommitRaw = process.env.
|
|
3249
|
-
let taskCompleteRaw = process.env.
|
|
3247
|
+
let prePushRaw = process.env.OPENFLEET_HOOK_PREPUSH || "";
|
|
3248
|
+
let preCommitRaw = process.env.OPENFLEET_HOOK_PRECOMMIT || "";
|
|
3249
|
+
let taskCompleteRaw = process.env.OPENFLEET_HOOK_TASK_COMPLETE || "";
|
|
3250
3250
|
let overwriteHooks = false;
|
|
3251
3251
|
|
|
3252
3252
|
if (isAdvancedSetup) {
|
|
@@ -3294,15 +3294,15 @@ async function main() {
|
|
|
3294
3294
|
|
|
3295
3295
|
prePushRaw = await prompt.ask(
|
|
3296
3296
|
"Pre-push command override",
|
|
3297
|
-
process.env.
|
|
3297
|
+
process.env.OPENFLEET_HOOK_PREPUSH || "",
|
|
3298
3298
|
);
|
|
3299
3299
|
preCommitRaw = await prompt.ask(
|
|
3300
3300
|
"Pre-commit command override",
|
|
3301
|
-
process.env.
|
|
3301
|
+
process.env.OPENFLEET_HOOK_PRECOMMIT || "",
|
|
3302
3302
|
);
|
|
3303
3303
|
taskCompleteRaw = await prompt.ask(
|
|
3304
3304
|
"Task-complete command override",
|
|
3305
|
-
process.env.
|
|
3305
|
+
process.env.OPENFLEET_HOOK_TASK_COMPLETE || "",
|
|
3306
3306
|
);
|
|
3307
3307
|
|
|
3308
3308
|
overwriteHooks = await prompt.confirm(
|
package/task-executor.mjs
CHANGED
|
@@ -97,7 +97,7 @@ const NO_COMMIT_MAX_COOLDOWN_MS = 2 * 60 * 60 * 1000;
|
|
|
97
97
|
const CLAIM_CONFLICT_COMMENT_COOLDOWN_MS = 30 * 60 * 1000; // 30 minutes
|
|
98
98
|
const CODEX_TASK_LABELS = (() => {
|
|
99
99
|
const raw = String(
|
|
100
|
-
process.env.
|
|
100
|
+
process.env.OPENFLEET_TASK_LABELS || "openfleet,codex-mointor",
|
|
101
101
|
);
|
|
102
102
|
const labels = raw
|
|
103
103
|
.split(",")
|
|
@@ -1010,11 +1010,11 @@ class TaskExecutor {
|
|
|
1010
1010
|
this._claimConflictNotifiedAt = new Map();
|
|
1011
1011
|
this._instanceIdExplicit =
|
|
1012
1012
|
String(process.env.VE_INSTANCE_ID || "").trim() !== "" ||
|
|
1013
|
-
String(process.env.
|
|
1013
|
+
String(process.env.OPENFLEET_INSTANCE_ID || "").trim() !== "";
|
|
1014
1014
|
this._instanceId =
|
|
1015
1015
|
String(
|
|
1016
1016
|
process.env.VE_INSTANCE_ID ||
|
|
1017
|
-
process.env.
|
|
1017
|
+
process.env.OPENFLEET_INSTANCE_ID ||
|
|
1018
1018
|
`${os.hostname() || "host"}-${process.pid}`,
|
|
1019
1019
|
).trim() || `executor-${process.pid}`;
|
|
1020
1020
|
this.taskClaimOwnerStaleTtlMs = Math.max(
|
package/telegram-bot.mjs
CHANGED
|
@@ -2614,7 +2614,7 @@ async function clearWebAppMenuButton() {
|
|
|
2614
2614
|
}
|
|
2615
2615
|
}
|
|
2616
2616
|
|
|
2617
|
-
const MENU_BUTTON_REFRESH_MS =
|
|
2617
|
+
const MENU_BUTTON_REFRESH_MS = 60 * 1000; // 1 minute — re-sync after tunnel URL rotates
|
|
2618
2618
|
|
|
2619
2619
|
async function refreshMenuButton() {
|
|
2620
2620
|
const { uiUrl: currentUiUrl, webAppUrl: currentUrl } = syncUiUrlsFromServer();
|
package/telegram-sentinel.mjs
CHANGED
|
@@ -104,12 +104,12 @@ export const SETTINGS_SCHEMA = [
|
|
|
104
104
|
// ── Kanban / Tasks ─────────────────────────────────────────
|
|
105
105
|
{ key: "KANBAN_BACKEND", label: "Kanban Backend", category: "kanban", type: "select", defaultVal: "internal", options: ["internal", "vk", "github", "jira"], description: "Task management backend. 'internal' uses built-in store, 'github' syncs with GitHub Issues/Projects." },
|
|
106
106
|
{ key: "KANBAN_SYNC_POLICY", label: "Sync Policy", category: "kanban", type: "select", defaultVal: "internal-primary", options: ["internal-primary", "bidirectional"], description: "How tasks sync between internal store and external backend." },
|
|
107
|
-
{ key: "
|
|
108
|
-
{ key: "
|
|
107
|
+
{ key: "OPENFLEET_TASK_LABEL", label: "Task Label", category: "kanban", type: "string", defaultVal: "openfleet", description: "GitHub label used to scope which issues are managed by Codex Monitor." },
|
|
108
|
+
{ key: "OPENFLEET_ENFORCE_TASK_LABEL", label: "Enforce Task Label", category: "kanban", type: "boolean", defaultVal: true, description: "Only pick up issues that have the task label. Prevents processing unrelated issues." },
|
|
109
109
|
{ key: "STALE_TASK_AGE_HOURS", label: "Stale Task Age", category: "kanban", type: "number", defaultVal: 3, min: 1, max: 168, unit: "hours", description: "Hours before an in-progress task with no activity is considered stale and eligible for recovery." },
|
|
110
110
|
{ key: "TASK_PLANNER_MODE", label: "Task Planner Mode", category: "kanban", type: "select", defaultVal: "kanban", options: ["kanban", "codex-sdk", "disabled"], description: "How the autonomous task planner operates. 'disabled' turns off automatic task generation." },
|
|
111
111
|
{ key: "TASK_PLANNER_DEDUP_HOURS", label: "Planner Dedup Window", category: "kanban", type: "number", defaultVal: 6, min: 1, max: 72, unit: "hours", description: "Hours to look back for duplicate task detection.", advanced: true },
|
|
112
|
-
{ key: "
|
|
112
|
+
{ key: "OPENFLEET_PROMPT_PLANNER", label: "Planner Prompt Path", category: "advanced", type: "string", description: "Override the task planner prompt file path.", advanced: true },
|
|
113
113
|
|
|
114
114
|
// ── GitHub / Git ─────────────────────────────────────────
|
|
115
115
|
{ key: "GITHUB_TOKEN", label: "GitHub Token", category: "github", type: "secret", sensitive: true, description: "Personal access token or fine-grained token for GitHub API. Required for GitHub kanban backend." },
|
|
@@ -157,7 +157,7 @@ export const SETTINGS_SCHEMA = [
|
|
|
157
157
|
{ key: "CONTAINER_CPU_LIMIT", label: "CPU Limit", category: "security", type: "string", description: "Container CPU limit (e.g., '2', '1.5'). Leave empty for no limit.", validate: "^\\d+\\.?\\d*$" },
|
|
158
158
|
|
|
159
159
|
// ── Sentinel / Reliability ─────────────────────────────────
|
|
160
|
-
{ key: "
|
|
160
|
+
{ key: "OPENFLEET_SENTINEL_AUTO_START", label: "Auto-Start Sentinel", category: "sentinel", type: "boolean", defaultVal: false, description: "Automatically start the sentinel watchdog on boot." },
|
|
161
161
|
{ key: "SENTINEL_AUTO_RESTART_MONITOR", label: "Auto-Restart on Crash", category: "sentinel", type: "boolean", defaultVal: true, description: "Automatically restart the monitor process if it crashes." },
|
|
162
162
|
{ key: "SENTINEL_CRASH_LOOP_THRESHOLD", label: "Crash Loop Threshold", category: "sentinel", type: "number", defaultVal: 3, min: 2, max: 20, description: "Number of crashes within the window before declaring a crash loop." },
|
|
163
163
|
{ key: "SENTINEL_CRASH_LOOP_WINDOW_MIN", label: "Crash Loop Window", category: "sentinel", type: "number", defaultVal: 10, min: 2, max: 60, unit: "min", description: "Rolling time window for crash loop detection." },
|
|
@@ -165,11 +165,11 @@ export const SETTINGS_SCHEMA = [
|
|
|
165
165
|
{ key: "SENTINEL_REPAIR_TIMEOUT_MIN", label: "Repair Timeout", category: "sentinel", type: "number", defaultVal: 20, min: 5, max: 120, unit: "min", description: "Maximum time the repair agent can run." },
|
|
166
166
|
|
|
167
167
|
// ── Agent Hooks ────────────────────────────────────────────
|
|
168
|
-
{ key: "
|
|
169
|
-
{ key: "
|
|
170
|
-
{ key: "
|
|
171
|
-
{ key: "
|
|
172
|
-
{ key: "
|
|
168
|
+
{ key: "OPENFLEET_HOOK_PROFILE", label: "Hook Profile", category: "hooks", type: "select", defaultVal: "strict", options: ["strict", "balanced", "lightweight", "none"], description: "Pre-configured hook intensity. 'strict' runs all checks, 'none' disables hooks." },
|
|
169
|
+
{ key: "OPENFLEET_HOOK_TARGETS", label: "Hook Targets", category: "hooks", type: "string", defaultVal: "codex,claude,copilot", description: "Comma-separated list of agent SDKs to install hooks for.", validate: "^[a-z,]+$" },
|
|
170
|
+
{ key: "OPENFLEET_HOOKS_ENABLED", label: "Enable Hooks", category: "hooks", type: "boolean", defaultVal: true, description: "Enable agent lifecycle hook scaffolding." },
|
|
171
|
+
{ key: "OPENFLEET_HOOKS_OVERWRITE", label: "Overwrite Existing", category: "hooks", type: "boolean", defaultVal: false, description: "Overwrite existing hook files when installing. Use with caution." },
|
|
172
|
+
{ key: "OPENFLEET_HOOKS_BUILTINS_MODE", label: "Built-ins Mode", category: "hooks", type: "select", defaultVal: "force", options: ["force", "auto", "off"], description: "How built-in hooks are managed. 'force' always installs, 'auto' only if missing." },
|
|
173
173
|
|
|
174
174
|
// ── Logging / Monitoring ────────────────────────────────────
|
|
175
175
|
{ key: "AGENT_WORK_LOGGING_ENABLED", label: "Work Logging", category: "logging", type: "boolean", defaultVal: true, description: "Enable structured agent work logging with transcripts." },
|
|
@@ -213,7 +213,7 @@ export function getGroupedSettings(includeAdvanced = false) {
|
|
|
213
213
|
*/
|
|
214
214
|
export function validateSetting(def, value) {
|
|
215
215
|
if (value === "" || value == null) return { valid: true };
|
|
216
|
-
if (def.key === "
|
|
216
|
+
if (def.key === "OPENFLEET_HOOK_TARGETS") {
|
|
217
217
|
const targets = String(value || "")
|
|
218
218
|
.split(",")
|
|
219
219
|
.map((entry) => entry.trim().toLowerCase())
|
package/ui-server.mjs
CHANGED
|
@@ -75,8 +75,8 @@ let _configSchema = null;
|
|
|
75
75
|
let _configValidator = null;
|
|
76
76
|
|
|
77
77
|
function resolveConfigPath() {
|
|
78
|
-
return process.env.
|
|
79
|
-
? resolve(process.env.
|
|
78
|
+
return process.env.OPENFLEET_CONFIG_PATH
|
|
79
|
+
? resolve(process.env.OPENFLEET_CONFIG_PATH)
|
|
80
80
|
: resolve(__dirname, "openfleet.config.json");
|
|
81
81
|
}
|
|
82
82
|
|
|
@@ -165,7 +165,7 @@ function coerceSettingValue(def, value, propSchema) {
|
|
|
165
165
|
.split(",")
|
|
166
166
|
.map((part) => part.trim())
|
|
167
167
|
.filter(Boolean);
|
|
168
|
-
if (def?.key === "
|
|
168
|
+
if (def?.key === "OPENFLEET_HOOK_TARGETS") {
|
|
169
169
|
parts = parts.map((part) => part.toLowerCase());
|
|
170
170
|
if (parts.includes("all")) {
|
|
171
171
|
const allowed = Array.isArray(propSchema?.items?.enum)
|
|
@@ -213,7 +213,7 @@ function getSchemaProperty(schema, pathParts) {
|
|
|
213
213
|
|
|
214
214
|
const ROOT_SKIP_ENV_KEYS = new Set([]);
|
|
215
215
|
const ROOT_OVERRIDE_MAP = {
|
|
216
|
-
|
|
216
|
+
OPENFLEET_MODE: "mode",
|
|
217
217
|
TASK_PLANNER_MODE: "plannerMode",
|
|
218
218
|
EXECUTOR_DISTRIBUTION: "distribution",
|
|
219
219
|
};
|
|
@@ -274,8 +274,8 @@ function mapEnvKeyToConfigPath(key, schema) {
|
|
|
274
274
|
const sub = toCamelCaseFromEnv(rest);
|
|
275
275
|
if (schema.properties.failover.properties[sub]) return buildConfigPath(["failover", sub]);
|
|
276
276
|
}
|
|
277
|
-
if (envKey.startsWith("
|
|
278
|
-
const rest = envKey.slice("
|
|
277
|
+
if (envKey.startsWith("OPENFLEET_PROMPT_") && schema.properties.agentPrompts?.properties) {
|
|
278
|
+
const rest = envKey.slice("OPENFLEET_PROMPT_".length);
|
|
279
279
|
const sub = toCamelCaseFromEnv(rest);
|
|
280
280
|
if (schema.properties.agentPrompts.properties[sub]) {
|
|
281
281
|
return buildConfigPath(["agentPrompts", sub]);
|
|
@@ -293,10 +293,10 @@ function mapEnvKeyToConfigPath(key, schema) {
|
|
|
293
293
|
}
|
|
294
294
|
}
|
|
295
295
|
const hookProfileMap = {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
296
|
+
OPENFLEET_HOOK_PROFILE: ["hookProfiles", "profile"],
|
|
297
|
+
OPENFLEET_HOOK_TARGETS: ["hookProfiles", "targets"],
|
|
298
|
+
OPENFLEET_HOOKS_ENABLED: ["hookProfiles", "enabled"],
|
|
299
|
+
OPENFLEET_HOOKS_OVERWRITE: ["hookProfiles", "overwriteExisting"],
|
|
300
300
|
};
|
|
301
301
|
if (hookProfileMap[envKey]) {
|
|
302
302
|
const pathParts = hookProfileMap[envKey];
|
|
@@ -470,10 +470,10 @@ const SETTINGS_KNOWN_KEYS = [
|
|
|
470
470
|
"CODEX_MODEL_PROFILE_M_PROVIDER", "CODEX_MODEL_PROFILE_M_MODEL", "CODEX_MODEL_PROFILE_M_BASE_URL", "CODEX_MODEL_PROFILE_M_API_KEY",
|
|
471
471
|
"CODEX_SUBAGENT_MODEL", "ANTHROPIC_API_KEY", "CLAUDE_MODEL",
|
|
472
472
|
"COPILOT_MODEL", "COPILOT_CLI_TOKEN",
|
|
473
|
-
"KANBAN_BACKEND", "KANBAN_SYNC_POLICY", "
|
|
474
|
-
"
|
|
473
|
+
"KANBAN_BACKEND", "KANBAN_SYNC_POLICY", "OPENFLEET_TASK_LABEL",
|
|
474
|
+
"OPENFLEET_ENFORCE_TASK_LABEL", "STALE_TASK_AGE_HOURS",
|
|
475
475
|
"TASK_PLANNER_MODE", "TASK_PLANNER_DEDUP_HOURS",
|
|
476
|
-
"
|
|
476
|
+
"OPENFLEET_PROMPT_PLANNER",
|
|
477
477
|
"GITHUB_TOKEN", "GITHUB_REPOSITORY", "GITHUB_PROJECT_MODE",
|
|
478
478
|
"GITHUB_PROJECT_NUMBER", "GITHUB_DEFAULT_ASSIGNEE", "GITHUB_AUTO_ASSIGN_CREATOR",
|
|
479
479
|
"GITHUB_PROJECT_WEBHOOK_PATH", "GITHUB_PROJECT_WEBHOOK_SECRET", "GITHUB_PROJECT_WEBHOOK_REQUIRE_SIGNATURE",
|
|
@@ -488,12 +488,12 @@ const SETTINGS_KNOWN_KEYS = [
|
|
|
488
488
|
"CODEX_SANDBOX", "CODEX_FEATURES_BWRAP", "CODEX_SANDBOX_PERMISSIONS", "CODEX_SANDBOX_WRITABLE_ROOTS",
|
|
489
489
|
"CONTAINER_ENABLED", "CONTAINER_RUNTIME", "CONTAINER_IMAGE",
|
|
490
490
|
"CONTAINER_TIMEOUT_MS", "MAX_CONCURRENT_CONTAINERS", "CONTAINER_MEMORY_LIMIT", "CONTAINER_CPU_LIMIT",
|
|
491
|
-
"
|
|
491
|
+
"OPENFLEET_SENTINEL_AUTO_START", "SENTINEL_AUTO_RESTART_MONITOR",
|
|
492
492
|
"SENTINEL_CRASH_LOOP_THRESHOLD", "SENTINEL_CRASH_LOOP_WINDOW_MIN",
|
|
493
493
|
"SENTINEL_REPAIR_AGENT_ENABLED", "SENTINEL_REPAIR_TIMEOUT_MIN",
|
|
494
|
-
"
|
|
495
|
-
"
|
|
496
|
-
"
|
|
494
|
+
"OPENFLEET_HOOK_PROFILE", "OPENFLEET_HOOK_TARGETS",
|
|
495
|
+
"OPENFLEET_HOOKS_ENABLED", "OPENFLEET_HOOKS_OVERWRITE",
|
|
496
|
+
"OPENFLEET_HOOKS_BUILTINS_MODE",
|
|
497
497
|
"AGENT_WORK_LOGGING_ENABLED", "AGENT_WORK_ANALYZER_ENABLED",
|
|
498
498
|
"AGENT_SESSION_LOG_RETENTION", "AGENT_ERROR_LOOP_THRESHOLD",
|
|
499
499
|
"AGENT_STUCK_THRESHOLD_MS", "LOG_MAX_SIZE_MB",
|
package/update-check.mjs
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* auto-installs updates and restarts the process. Zero user interaction.
|
|
9
9
|
*
|
|
10
10
|
* Respects:
|
|
11
|
-
* -
|
|
12
|
-
* -
|
|
13
|
-
* -
|
|
11
|
+
* - OPENFLEET_SKIP_UPDATE_CHECK=1 — disable startup check
|
|
12
|
+
* - OPENFLEET_SKIP_AUTO_UPDATE=1 — disable polling auto-update
|
|
13
|
+
* - OPENFLEET_UPDATE_INTERVAL_MS — override poll interval (default 10 min)
|
|
14
14
|
* - Caches the last check timestamp so we don't query npm too aggressively
|
|
15
15
|
*/
|
|
16
16
|
|
|
@@ -124,7 +124,7 @@ async function fetchLatestVersion() {
|
|
|
124
124
|
* Called on startup — must never throw or delay the main process.
|
|
125
125
|
*/
|
|
126
126
|
export async function checkForUpdate(currentVersion) {
|
|
127
|
-
if (process.env.
|
|
127
|
+
if (process.env.OPENFLEET_SKIP_UPDATE_CHECK) return;
|
|
128
128
|
|
|
129
129
|
try {
|
|
130
130
|
// Rate-limit: at most once per hour
|
|
@@ -240,13 +240,13 @@ let cleanupHandlersRegistered = false;
|
|
|
240
240
|
* @param {number} [opts.parentPid] - Parent process PID to monitor (default: process.ppid)
|
|
241
241
|
*/
|
|
242
242
|
export function startAutoUpdateLoop(opts = {}) {
|
|
243
|
-
if (process.env.
|
|
244
|
-
console.log("[auto-update] Disabled via
|
|
243
|
+
if (process.env.OPENFLEET_SKIP_AUTO_UPDATE === "1") {
|
|
244
|
+
console.log("[auto-update] Disabled via OPENFLEET_SKIP_AUTO_UPDATE=1");
|
|
245
245
|
return;
|
|
246
246
|
}
|
|
247
247
|
|
|
248
248
|
const intervalMs =
|
|
249
|
-
Number(process.env.
|
|
249
|
+
Number(process.env.OPENFLEET_UPDATE_INTERVAL_MS) ||
|
|
250
250
|
opts.intervalMs ||
|
|
251
251
|
AUTO_UPDATE_INTERVAL_MS;
|
|
252
252
|
const onRestart = opts.onRestart || (() => process.exit(0));
|
package/ve-orchestrator.ps1
CHANGED
|
@@ -969,7 +969,7 @@ function Initialize-CISweepConfig {
|
|
|
969
969
|
$script:CopilotCloudDisableOnRateLimit = Get-EnvBool -Name "COPILOT_CLOUD_DISABLE_ON_RATE_LIMIT" -Default $true
|
|
970
970
|
$envCopilotLocalResolution = Get-EnvFallback -Name "COPILOT_LOCAL_RESOLUTION"
|
|
971
971
|
$script:CopilotLocalResolution = if ($envCopilotLocalResolution) { $envCopilotLocalResolution } else { "agent" }
|
|
972
|
-
$script:CodexMonitorTaskUpstream = Get-EnvString -Name "
|
|
972
|
+
$script:CodexMonitorTaskUpstream = Get-EnvString -Name "OPENFLEET_TASK_UPSTREAM" -Default "origin/ve/openfleet-generic"
|
|
973
973
|
|
|
974
974
|
# Branch routing scope map (v0.8) — maps conventional commit scopes to upstream branches
|
|
975
975
|
$script:BranchRoutingScopeMap = @{}
|
package/worktree-manager.mjs
CHANGED
|
@@ -177,7 +177,7 @@ function detectGitTopLevel(candidatePath) {
|
|
|
177
177
|
* Resolve the best repository root for singleton initialization.
|
|
178
178
|
* Priority:
|
|
179
179
|
* 1) explicit repoRoot arg
|
|
180
|
-
* 2) VE_REPO_ROOT /
|
|
180
|
+
* 2) VE_REPO_ROOT / OPENFLEET_REPO_ROOT env
|
|
181
181
|
* 3) current working directory's git top-level
|
|
182
182
|
* 4) module-relative git top-level (useful for local dev)
|
|
183
183
|
* 5) process.cwd() fallback
|
|
@@ -189,7 +189,7 @@ function resolveDefaultRepoRoot(repoRoot) {
|
|
|
189
189
|
if (repoRoot) return resolve(repoRoot);
|
|
190
190
|
|
|
191
191
|
const envRoot =
|
|
192
|
-
process.env.VE_REPO_ROOT || process.env.
|
|
192
|
+
process.env.VE_REPO_ROOT || process.env.OPENFLEET_REPO_ROOT || "";
|
|
193
193
|
const fromEnv = detectGitTopLevel(envRoot) || (envRoot ? resolve(envRoot) : null);
|
|
194
194
|
if (fromEnv) return fromEnv;
|
|
195
195
|
|