@ttfw/envoi 1.0.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/README.md +238 -0
- package/dist/commands/app.d.ts +2 -0
- package/dist/commands/app.d.ts.map +1 -0
- package/dist/commands/app.js +31 -0
- package/dist/commands/app.js.map +1 -0
- package/dist/commands/autonomy.d.ts +6 -0
- package/dist/commands/autonomy.d.ts.map +1 -0
- package/dist/commands/autonomy.js +89 -0
- package/dist/commands/autonomy.js.map +1 -0
- package/dist/commands/builder.d.ts +13 -0
- package/dist/commands/builder.d.ts.map +1 -0
- package/dist/commands/builder.js +142 -0
- package/dist/commands/builder.js.map +1 -0
- package/dist/commands/idea.d.ts +12 -0
- package/dist/commands/idea.d.ts.map +1 -0
- package/dist/commands/idea.js +79 -0
- package/dist/commands/idea.js.map +1 -0
- package/dist/commands/init.d.ts +18 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +423 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/mode.d.ts +13 -0
- package/dist/commands/mode.d.ts.map +1 -0
- package/dist/commands/mode.js +96 -0
- package/dist/commands/mode.js.map +1 -0
- package/dist/commands/onboard.d.ts +37 -0
- package/dist/commands/onboard.d.ts.map +1 -0
- package/dist/commands/onboard.js +743 -0
- package/dist/commands/onboard.js.map +1 -0
- package/dist/commands/pr-note.d.ts +8 -0
- package/dist/commands/pr-note.d.ts.map +1 -0
- package/dist/commands/pr-note.js +27 -0
- package/dist/commands/pr-note.js.map +1 -0
- package/dist/commands/undo.d.ts +7 -0
- package/dist/commands/undo.d.ts.map +1 -0
- package/dist/commands/undo.js +59 -0
- package/dist/commands/undo.js.map +1 -0
- package/dist/commands/update.d.ts +24 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +248 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/constants/report_codes.d.ts +29 -0
- package/dist/constants/report_codes.d.ts.map +1 -0
- package/dist/constants/report_codes.js +69 -0
- package/dist/constants/report_codes.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +675 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/autonomy.d.ts +16 -0
- package/dist/lib/autonomy.d.ts.map +1 -0
- package/dist/lib/autonomy.js +38 -0
- package/dist/lib/autonomy.js.map +1 -0
- package/dist/lib/blocked.d.ts +87 -0
- package/dist/lib/blocked.d.ts.map +1 -0
- package/dist/lib/blocked.js +134 -0
- package/dist/lib/blocked.js.map +1 -0
- package/dist/lib/branding.d.ts +13 -0
- package/dist/lib/branding.d.ts.map +1 -0
- package/dist/lib/branding.js +19 -0
- package/dist/lib/branding.js.map +1 -0
- package/dist/lib/claude.d.ts +42 -0
- package/dist/lib/claude.d.ts.map +1 -0
- package/dist/lib/claude.js +291 -0
- package/dist/lib/claude.js.map +1 -0
- package/dist/lib/config.d.ts +71 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +410 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/diff.d.ts +150 -0
- package/dist/lib/diff.d.ts.map +1 -0
- package/dist/lib/diff.js +257 -0
- package/dist/lib/diff.js.map +1 -0
- package/dist/lib/doctor.d.ts +67 -0
- package/dist/lib/doctor.d.ts.map +1 -0
- package/dist/lib/doctor.js +211 -0
- package/dist/lib/doctor.js.map +1 -0
- package/dist/lib/fingerprint.d.ts +27 -0
- package/dist/lib/fingerprint.d.ts.map +1 -0
- package/dist/lib/fingerprint.js +116 -0
- package/dist/lib/fingerprint.js.map +1 -0
- package/dist/lib/fs.d.ts +93 -0
- package/dist/lib/fs.d.ts.map +1 -0
- package/dist/lib/fs.js +179 -0
- package/dist/lib/fs.js.map +1 -0
- package/dist/lib/git.d.ts +177 -0
- package/dist/lib/git.d.ts.map +1 -0
- package/dist/lib/git.js +355 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/git_branching.d.ts +84 -0
- package/dist/lib/git_branching.d.ts.map +1 -0
- package/dist/lib/git_branching.js +327 -0
- package/dist/lib/git_branching.js.map +1 -0
- package/dist/lib/gitignore.d.ts +26 -0
- package/dist/lib/gitignore.d.ts.map +1 -0
- package/dist/lib/gitignore.js +119 -0
- package/dist/lib/gitignore.js.map +1 -0
- package/dist/lib/guardrails.d.ts +232 -0
- package/dist/lib/guardrails.d.ts.map +1 -0
- package/dist/lib/guardrails.js +323 -0
- package/dist/lib/guardrails.js.map +1 -0
- package/dist/lib/history.d.ts +110 -0
- package/dist/lib/history.d.ts.map +1 -0
- package/dist/lib/history.js +236 -0
- package/dist/lib/history.js.map +1 -0
- package/dist/lib/index.d.ts +29 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +29 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/json-extract.d.ts +42 -0
- package/dist/lib/json-extract.d.ts.map +1 -0
- package/dist/lib/json-extract.js +201 -0
- package/dist/lib/json-extract.js.map +1 -0
- package/dist/lib/judge.d.ts +237 -0
- package/dist/lib/judge.d.ts.map +1 -0
- package/dist/lib/judge.js +501 -0
- package/dist/lib/judge.js.map +1 -0
- package/dist/lib/lock.d.ts +79 -0
- package/dist/lib/lock.d.ts.map +1 -0
- package/dist/lib/lock.js +254 -0
- package/dist/lib/lock.js.map +1 -0
- package/dist/lib/migration.d.ts +9 -0
- package/dist/lib/migration.d.ts.map +1 -0
- package/dist/lib/migration.js +74 -0
- package/dist/lib/migration.js.map +1 -0
- package/dist/lib/paths.d.ts +18 -0
- package/dist/lib/paths.d.ts.map +1 -0
- package/dist/lib/paths.js +27 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/preflight.d.ts +33 -0
- package/dist/lib/preflight.d.ts.map +1 -0
- package/dist/lib/preflight.js +177 -0
- package/dist/lib/preflight.js.map +1 -0
- package/dist/lib/prompt_budget.d.ts +18 -0
- package/dist/lib/prompt_budget.d.ts.map +1 -0
- package/dist/lib/prompt_budget.js +36 -0
- package/dist/lib/prompt_budget.js.map +1 -0
- package/dist/lib/report.d.ts +102 -0
- package/dist/lib/report.d.ts.map +1 -0
- package/dist/lib/report.js +347 -0
- package/dist/lib/report.js.map +1 -0
- package/dist/lib/reviewer-flow.d.ts +80 -0
- package/dist/lib/reviewer-flow.d.ts.map +1 -0
- package/dist/lib/reviewer-flow.js +138 -0
- package/dist/lib/reviewer-flow.js.map +1 -0
- package/dist/lib/reviewer.d.ts +53 -0
- package/dist/lib/reviewer.d.ts.map +1 -0
- package/dist/lib/reviewer.js +199 -0
- package/dist/lib/reviewer.js.map +1 -0
- package/dist/lib/risk.d.ts +127 -0
- package/dist/lib/risk.d.ts.map +1 -0
- package/dist/lib/risk.js +192 -0
- package/dist/lib/risk.js.map +1 -0
- package/dist/lib/rollback.d.ts +143 -0
- package/dist/lib/rollback.d.ts.map +1 -0
- package/dist/lib/rollback.js +244 -0
- package/dist/lib/rollback.js.map +1 -0
- package/dist/lib/schema.d.ts +47 -0
- package/dist/lib/schema.d.ts.map +1 -0
- package/dist/lib/schema.js +91 -0
- package/dist/lib/schema.js.map +1 -0
- package/dist/lib/scope.d.ts +89 -0
- package/dist/lib/scope.d.ts.map +1 -0
- package/dist/lib/scope.js +135 -0
- package/dist/lib/scope.js.map +1 -0
- package/dist/lib/self_update.d.ts +13 -0
- package/dist/lib/self_update.d.ts.map +1 -0
- package/dist/lib/self_update.js +172 -0
- package/dist/lib/self_update.js.map +1 -0
- package/dist/lib/state.d.ts +143 -0
- package/dist/lib/state.d.ts.map +1 -0
- package/dist/lib/state.js +258 -0
- package/dist/lib/state.js.map +1 -0
- package/dist/lib/tick.d.ts +310 -0
- package/dist/lib/tick.d.ts.map +1 -0
- package/dist/lib/tick.js +424 -0
- package/dist/lib/tick.js.map +1 -0
- package/dist/lib/transport.d.ts +145 -0
- package/dist/lib/transport.d.ts.map +1 -0
- package/dist/lib/transport.js +237 -0
- package/dist/lib/transport.js.map +1 -0
- package/dist/lib/verdict_labels.d.ts +5 -0
- package/dist/lib/verdict_labels.d.ts.map +1 -0
- package/dist/lib/verdict_labels.js +25 -0
- package/dist/lib/verdict_labels.js.map +1 -0
- package/dist/lib/verify-safety.d.ts +63 -0
- package/dist/lib/verify-safety.d.ts.map +1 -0
- package/dist/lib/verify-safety.js +123 -0
- package/dist/lib/verify-safety.js.map +1 -0
- package/dist/lib/verify.d.ts +139 -0
- package/dist/lib/verify.d.ts.map +1 -0
- package/dist/lib/verify.js +311 -0
- package/dist/lib/verify.js.map +1 -0
- package/dist/lib/workspace_state.d.ts +79 -0
- package/dist/lib/workspace_state.d.ts.map +1 -0
- package/dist/lib/workspace_state.js +283 -0
- package/dist/lib/workspace_state.js.map +1 -0
- package/dist/runner/builder.d.ts +58 -0
- package/dist/runner/builder.d.ts.map +1 -0
- package/dist/runner/builder.js +775 -0
- package/dist/runner/builder.js.map +1 -0
- package/dist/runner/builder_parse.d.ts +37 -0
- package/dist/runner/builder_parse.d.ts.map +1 -0
- package/dist/runner/builder_parse.js +76 -0
- package/dist/runner/builder_parse.js.map +1 -0
- package/dist/runner/index.d.ts +9 -0
- package/dist/runner/index.d.ts.map +1 -0
- package/dist/runner/index.js +7 -0
- package/dist/runner/index.js.map +1 -0
- package/dist/runner/loop.d.ts +51 -0
- package/dist/runner/loop.d.ts.map +1 -0
- package/dist/runner/loop.js +221 -0
- package/dist/runner/loop.js.map +1 -0
- package/dist/runner/orchestrator.d.ts +67 -0
- package/dist/runner/orchestrator.d.ts.map +1 -0
- package/dist/runner/orchestrator.js +376 -0
- package/dist/runner/orchestrator.js.map +1 -0
- package/dist/runner/tick.d.ts +10 -0
- package/dist/runner/tick.d.ts.map +1 -0
- package/dist/runner/tick.js +1639 -0
- package/dist/runner/tick.js.map +1 -0
- package/dist/types/blocked.d.ts +52 -0
- package/dist/types/blocked.d.ts.map +1 -0
- package/dist/types/blocked.js +8 -0
- package/dist/types/blocked.js.map +1 -0
- package/dist/types/builder.d.ts +25 -0
- package/dist/types/builder.d.ts.map +1 -0
- package/dist/types/builder.js +7 -0
- package/dist/types/builder.js.map +1 -0
- package/dist/types/claude.d.ts +86 -0
- package/dist/types/claude.d.ts.map +1 -0
- package/dist/types/claude.js +48 -0
- package/dist/types/claude.js.map +1 -0
- package/dist/types/config.d.ts +384 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +7 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/index.d.ts +18 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/lock.d.ts +21 -0
- package/dist/types/lock.d.ts.map +1 -0
- package/dist/types/lock.js +8 -0
- package/dist/types/lock.js.map +1 -0
- package/dist/types/preflight.d.ts +49 -0
- package/dist/types/preflight.d.ts.map +1 -0
- package/dist/types/preflight.js +8 -0
- package/dist/types/preflight.js.map +1 -0
- package/dist/types/report.d.ts +161 -0
- package/dist/types/report.d.ts.map +1 -0
- package/dist/types/report.js +8 -0
- package/dist/types/report.js.map +1 -0
- package/dist/types/reviewer.d.ts +66 -0
- package/dist/types/reviewer.d.ts.map +1 -0
- package/dist/types/reviewer.js +5 -0
- package/dist/types/reviewer.js.map +1 -0
- package/dist/types/state.d.ts +124 -0
- package/dist/types/state.d.ts.map +1 -0
- package/dist/types/state.js +20 -0
- package/dist/types/state.js.map +1 -0
- package/dist/types/task.d.ts +117 -0
- package/dist/types/task.d.ts.map +1 -0
- package/dist/types/task.js +7 -0
- package/dist/types/task.js.map +1 -0
- package/dist/types/workspace_state.d.ts +125 -0
- package/dist/types/workspace_state.d.ts.map +1 -0
- package/dist/types/workspace_state.js +10 -0
- package/dist/types/workspace_state.js.map +1 -0
- package/envoi.config.json +191 -0
- package/package.json +52 -0
- package/relais/prompts/.gitkeep +0 -0
- package/relais/prompts/builder.system.txt +13 -0
- package/relais/prompts/builder.user.txt +15 -0
- package/relais/prompts/orchestrator.system.txt +37 -0
- package/relais/prompts/orchestrator.user.txt +34 -0
- package/relais/prompts/reviewer.system.txt +33 -0
- package/relais/prompts/reviewer.user.txt +35 -0
- package/relais/schemas/.gitkeep +0 -0
- package/relais/schemas/builder_result.schema.json +29 -0
- package/relais/schemas/report.schema.json +195 -0
- package/relais/schemas/reviewer_result.schema.json +70 -0
- package/relais/schemas/task.schema.json +155 -0
package/dist/lib/lock.js
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lock mechanism for preventing concurrent envoi runs.
|
|
3
|
+
*
|
|
4
|
+
* Implements crash-safe lock acquisition with boot_id tracking to enable
|
|
5
|
+
* safe reclaim of stale locks after crashes or reboots.
|
|
6
|
+
*/
|
|
7
|
+
import { execSync } from 'node:child_process';
|
|
8
|
+
import { hostname } from 'node:os';
|
|
9
|
+
import { unlink } from 'node:fs/promises';
|
|
10
|
+
import { atomicWriteJson, atomicReadJson, AtomicFsError } from './fs.js';
|
|
11
|
+
/**
|
|
12
|
+
* Error thrown when a lock is already held by another process.
|
|
13
|
+
*/
|
|
14
|
+
export class LockHeldError extends Error {
|
|
15
|
+
lockPath;
|
|
16
|
+
holder;
|
|
17
|
+
constructor(message, lockPath, holder) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.lockPath = lockPath;
|
|
20
|
+
this.holder = holder;
|
|
21
|
+
this.name = 'LockHeldError';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Error thrown when a lock file is corrupt or malformed.
|
|
26
|
+
*/
|
|
27
|
+
export class LockCorruptError extends Error {
|
|
28
|
+
lockPath;
|
|
29
|
+
constructor(message, lockPath) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.lockPath = lockPath;
|
|
32
|
+
this.name = 'LockCorruptError';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Cached boot ID to avoid repeated system calls */
|
|
36
|
+
let cachedBootId = null;
|
|
37
|
+
/**
|
|
38
|
+
* Gets a unique identifier for the current boot session.
|
|
39
|
+
*
|
|
40
|
+
* On Linux, reads /proc/sys/kernel/random/boot_id.
|
|
41
|
+
* On macOS, uses system uptime + hostname as a fingerprint since macOS
|
|
42
|
+
* doesn't have boot_id but uptime.boot_time is stable per boot.
|
|
43
|
+
*
|
|
44
|
+
* The result is cached for the lifetime of the process.
|
|
45
|
+
*
|
|
46
|
+
* @returns A string uniquely identifying the current boot session
|
|
47
|
+
*/
|
|
48
|
+
export function getBootId() {
|
|
49
|
+
if (cachedBootId !== null) {
|
|
50
|
+
return cachedBootId;
|
|
51
|
+
}
|
|
52
|
+
const platform = process.platform;
|
|
53
|
+
if (platform === 'linux') {
|
|
54
|
+
try {
|
|
55
|
+
// Linux provides a unique boot ID
|
|
56
|
+
const { readFileSync } = require('node:fs');
|
|
57
|
+
const bootId = readFileSync('/proc/sys/kernel/random/boot_id', 'utf-8').trim();
|
|
58
|
+
cachedBootId = bootId;
|
|
59
|
+
return bootId;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Fall through to fallback
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (platform === 'darwin') {
|
|
66
|
+
try {
|
|
67
|
+
// macOS: use boot time from sysctl
|
|
68
|
+
const output = execSync('sysctl -n kern.boottime', { encoding: 'utf-8' });
|
|
69
|
+
// Output format: { sec = 1234567890, usec = 123456 } ...
|
|
70
|
+
const match = output.match(/sec\s*=\s*(\d+)/);
|
|
71
|
+
if (match) {
|
|
72
|
+
const bootTime = match[1];
|
|
73
|
+
cachedBootId = `${hostname()}-${bootTime}`;
|
|
74
|
+
return cachedBootId;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Fall through to fallback
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Fallback: use process start time as a rough approximation
|
|
82
|
+
// This is less reliable but better than nothing
|
|
83
|
+
const startTime = Date.now() - (process.uptime() * 1000);
|
|
84
|
+
cachedBootId = `${hostname()}-${Math.floor(startTime / 1000)}`;
|
|
85
|
+
return cachedBootId;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Checks if a process with the given PID is currently running.
|
|
89
|
+
*
|
|
90
|
+
* Uses process.kill(pid, 0) which checks for process existence
|
|
91
|
+
* without actually sending a signal.
|
|
92
|
+
*
|
|
93
|
+
* @param pid - The process ID to check
|
|
94
|
+
* @returns true if the process is running, false otherwise
|
|
95
|
+
*/
|
|
96
|
+
export function isPidRunning(pid) {
|
|
97
|
+
try {
|
|
98
|
+
// Signal 0 doesn't send anything, just checks if process exists
|
|
99
|
+
process.kill(pid, 0);
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
// ESRCH = no such process, EPERM = exists but no permission
|
|
104
|
+
if (error instanceof Error && 'code' in error) {
|
|
105
|
+
const code = error.code;
|
|
106
|
+
if (code === 'EPERM') {
|
|
107
|
+
// Process exists but we don't have permission to signal it
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Determines if a lock is stale and can be reclaimed.
|
|
116
|
+
*
|
|
117
|
+
* A lock is considered stale if:
|
|
118
|
+
* - The holding process is no longer running, OR
|
|
119
|
+
* - The boot_id differs from the current boot (system has rebooted)
|
|
120
|
+
*
|
|
121
|
+
* @param lock - The lock information to check
|
|
122
|
+
* @returns true if the lock is stale and can be reclaimed
|
|
123
|
+
*/
|
|
124
|
+
export function isLockStale(lock) {
|
|
125
|
+
// If boot_id differs, the system has rebooted - lock is definitely stale
|
|
126
|
+
if (lock.boot_id !== getBootId()) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
// Same boot session - check if the process is still running
|
|
130
|
+
return !isPidRunning(lock.pid);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Validates that a parsed value is a valid LockInfo object.
|
|
134
|
+
*
|
|
135
|
+
* @param lockPath - Path to the lock file (for error messages)
|
|
136
|
+
* @param value - The parsed JSON value to validate
|
|
137
|
+
* @throws {LockCorruptError} If the value is not a valid LockInfo
|
|
138
|
+
*/
|
|
139
|
+
function validateLockShape(lockPath, value) {
|
|
140
|
+
const remediation = `Delete the lock file at ${lockPath} and retry.`;
|
|
141
|
+
// Must be an object (not null, not array)
|
|
142
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
143
|
+
throw new LockCorruptError(`Lock file has invalid structure (expected object, got ${value === null ? 'null' : Array.isArray(value) ? 'array' : typeof value}). ${remediation}`, lockPath);
|
|
144
|
+
}
|
|
145
|
+
const obj = value;
|
|
146
|
+
// pid must be integer > 0
|
|
147
|
+
if (typeof obj.pid !== 'number' || !Number.isInteger(obj.pid) || obj.pid <= 0) {
|
|
148
|
+
throw new LockCorruptError(`Lock file has invalid pid (expected integer > 0, got ${JSON.stringify(obj.pid)}). ${remediation}`, lockPath);
|
|
149
|
+
}
|
|
150
|
+
// started_at must be non-empty string
|
|
151
|
+
if (typeof obj.started_at !== 'string' || obj.started_at === '') {
|
|
152
|
+
throw new LockCorruptError(`Lock file has invalid started_at (expected non-empty string, got ${JSON.stringify(obj.started_at)}). ${remediation}`, lockPath);
|
|
153
|
+
}
|
|
154
|
+
// boot_id must be non-empty string
|
|
155
|
+
if (typeof obj.boot_id !== 'string' || obj.boot_id === '') {
|
|
156
|
+
throw new LockCorruptError(`Lock file has invalid boot_id (expected non-empty string, got ${JSON.stringify(obj.boot_id)}). ${remediation}`, lockPath);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Acquires a lock by creating a lock file atomically.
|
|
161
|
+
*
|
|
162
|
+
* If a lock already exists:
|
|
163
|
+
* - If stale (process dead or different boot), reclaims it with a warning
|
|
164
|
+
* - If held by an active process, throws LockHeldError
|
|
165
|
+
*
|
|
166
|
+
* @param lockPath - Path to the lock file
|
|
167
|
+
* @returns The lock information that was written
|
|
168
|
+
* @throws {LockHeldError} If the lock is held by another active process
|
|
169
|
+
* @throws {LockCorruptError} If the lock file is corrupt or malformed
|
|
170
|
+
* @throws {AtomicFsError} If the lock file cannot be written
|
|
171
|
+
*/
|
|
172
|
+
export async function acquireLock(lockPath) {
|
|
173
|
+
// Check if lock file exists
|
|
174
|
+
let existingLock = null;
|
|
175
|
+
try {
|
|
176
|
+
const rawValue = await atomicReadJson(lockPath);
|
|
177
|
+
validateLockShape(lockPath, rawValue);
|
|
178
|
+
existingLock = rawValue;
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
// Re-throw LockCorruptError as-is
|
|
182
|
+
if (error instanceof LockCorruptError) {
|
|
183
|
+
throw error;
|
|
184
|
+
}
|
|
185
|
+
if (error instanceof AtomicFsError) {
|
|
186
|
+
const cause = error.cause;
|
|
187
|
+
// File doesn't exist - no lock, proceed
|
|
188
|
+
if (cause && 'code' in cause && cause.code === 'ENOENT') {
|
|
189
|
+
// No lock file exists
|
|
190
|
+
}
|
|
191
|
+
else if (cause instanceof SyntaxError) {
|
|
192
|
+
// JSON parse error - corrupt lock file
|
|
193
|
+
const remediation = `Delete the lock file at ${lockPath} and retry.`;
|
|
194
|
+
throw new LockCorruptError(`Lock file contains invalid JSON. ${remediation}`, lockPath);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
// Real error (I/O issue, permission denied, etc.)
|
|
198
|
+
throw error;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
// Unknown error, rethrow
|
|
203
|
+
throw error;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (existingLock) {
|
|
207
|
+
if (isLockStale(existingLock)) {
|
|
208
|
+
// Lock is stale - log warning and reclaim
|
|
209
|
+
console.warn(`Reclaiming stale lock from PID ${existingLock.pid} (started_at: ${existingLock.started_at}, boot_id: ${existingLock.boot_id})`);
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
// Lock is held by an active process
|
|
213
|
+
throw new LockHeldError(`Lock is held by PID ${existingLock.pid} (started_at: ${existingLock.started_at})`, lockPath, existingLock);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// Create new lock
|
|
217
|
+
const lockInfo = {
|
|
218
|
+
pid: process.pid,
|
|
219
|
+
started_at: new Date().toISOString(),
|
|
220
|
+
boot_id: getBootId(),
|
|
221
|
+
};
|
|
222
|
+
await atomicWriteJson(lockPath, lockInfo);
|
|
223
|
+
return lockInfo;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Releases a lock by deleting the lock file.
|
|
227
|
+
*
|
|
228
|
+
* Only releases the lock if it belongs to the current process.
|
|
229
|
+
* Silently succeeds if the lock doesn't exist or belongs to another process.
|
|
230
|
+
*
|
|
231
|
+
* @param lockPath - Path to the lock file
|
|
232
|
+
*/
|
|
233
|
+
export async function releaseLock(lockPath) {
|
|
234
|
+
try {
|
|
235
|
+
// Read current lock to verify ownership
|
|
236
|
+
const lock = await atomicReadJson(lockPath);
|
|
237
|
+
// Only delete if we own the lock
|
|
238
|
+
if (lock.pid === process.pid && lock.boot_id === getBootId()) {
|
|
239
|
+
await unlink(lockPath);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
// Lock doesn't exist or can't be read - nothing to release
|
|
244
|
+
if (error instanceof AtomicFsError) {
|
|
245
|
+
const cause = error.cause;
|
|
246
|
+
if (cause && 'code' in cause && cause.code === 'ENOENT') {
|
|
247
|
+
// File doesn't exist - already released
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
// For other errors, also silently succeed - lock cleanup shouldn't fail the process
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/lib/lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { EnvoiConfig } from '../types/config.js';
|
|
2
|
+
interface MigrationResult {
|
|
3
|
+
config: EnvoiConfig;
|
|
4
|
+
configPath: string;
|
|
5
|
+
migrated: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function migrateLegacyLayoutIfNeeded(configPath: string, config: EnvoiConfig): Promise<MigrationResult>;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=migration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration.d.ts","sourceRoot":"","sources":["../../src/lib/migration.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAStD,UAAU,eAAe;IACvB,MAAM,EAAE,WAAW,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAkDD,wBAAsB,2BAA2B,CAC/C,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,eAAe,CAAC,CA0B1B"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { access, rename } from 'node:fs/promises';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { atomicWriteJson } from './fs.js';
|
|
4
|
+
import { CONFIG_FILE_NAME, LEGACY_WORKSPACE_DIR_NAME, PRODUCT_NAME, WORKSPACE_DIR_NAME, isLegacyConfigPath, } from './branding.js';
|
|
5
|
+
async function exists(path) {
|
|
6
|
+
try {
|
|
7
|
+
await access(path);
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function rewriteString(value, oldWorkspace, newWorkspace) {
|
|
15
|
+
if (value === oldWorkspace)
|
|
16
|
+
return newWorkspace;
|
|
17
|
+
if (value.startsWith(`${oldWorkspace}/`)) {
|
|
18
|
+
return `${newWorkspace}/${value.slice(oldWorkspace.length + 1)}`;
|
|
19
|
+
}
|
|
20
|
+
if (value.startsWith('relais/')) {
|
|
21
|
+
return `envoi/${value.slice('relais/'.length)}`;
|
|
22
|
+
}
|
|
23
|
+
return value.replace(/\brelais\//g, 'envoi/');
|
|
24
|
+
}
|
|
25
|
+
function rewriteValue(value, oldWorkspace, newWorkspace) {
|
|
26
|
+
if (typeof value === 'string') {
|
|
27
|
+
return rewriteString(value, oldWorkspace, newWorkspace);
|
|
28
|
+
}
|
|
29
|
+
if (Array.isArray(value)) {
|
|
30
|
+
return value.map((entry) => rewriteValue(entry, oldWorkspace, newWorkspace));
|
|
31
|
+
}
|
|
32
|
+
if (value && typeof value === 'object') {
|
|
33
|
+
const output = {};
|
|
34
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
35
|
+
output[key] = rewriteValue(entry, oldWorkspace, newWorkspace);
|
|
36
|
+
}
|
|
37
|
+
return output;
|
|
38
|
+
}
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
function buildMigratedConfig(config) {
|
|
42
|
+
const oldWorkspace = config.workspace_dir || LEGACY_WORKSPACE_DIR_NAME;
|
|
43
|
+
const newWorkspace = oldWorkspace === LEGACY_WORKSPACE_DIR_NAME ? WORKSPACE_DIR_NAME : oldWorkspace;
|
|
44
|
+
const rewritten = rewriteValue(config, oldWorkspace, newWorkspace);
|
|
45
|
+
return {
|
|
46
|
+
...rewritten,
|
|
47
|
+
product_name: PRODUCT_NAME.toLowerCase(),
|
|
48
|
+
workspace_dir: newWorkspace,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export async function migrateLegacyLayoutIfNeeded(configPath, config) {
|
|
52
|
+
if (!isLegacyConfigPath(configPath)) {
|
|
53
|
+
return { config, configPath, migrated: false };
|
|
54
|
+
}
|
|
55
|
+
const repoRoot = dirname(configPath);
|
|
56
|
+
const targetConfigPath = join(repoRoot, CONFIG_FILE_NAME);
|
|
57
|
+
const migratedConfig = buildMigratedConfig(config);
|
|
58
|
+
if (config.workspace_dir === LEGACY_WORKSPACE_DIR_NAME) {
|
|
59
|
+
const legacyWorkspacePath = join(repoRoot, LEGACY_WORKSPACE_DIR_NAME);
|
|
60
|
+
const newWorkspacePath = join(repoRoot, WORKSPACE_DIR_NAME);
|
|
61
|
+
if ((await exists(legacyWorkspacePath)) && !(await exists(newWorkspacePath))) {
|
|
62
|
+
await rename(legacyWorkspacePath, newWorkspacePath);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (!(await exists(targetConfigPath))) {
|
|
66
|
+
await atomicWriteJson(targetConfigPath, migratedConfig);
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
config: migratedConfig,
|
|
70
|
+
configPath: targetConfigPath,
|
|
71
|
+
migrated: true,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=migration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration.js","sourceRoot":"","sources":["../../src/lib/migration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE1C,OAAO,EACL,gBAAgB,EAChB,yBAAyB,EACzB,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AAQvB,KAAK,UAAU,MAAM,CAAC,IAAY;IAChC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,KAAa,EAAE,YAAoB,EAAE,YAAoB;IAC9E,IAAI,KAAK,KAAK,YAAY;QAAE,OAAO,YAAY,CAAC;IAChD,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,YAAY,GAAG,CAAC,EAAE,CAAC;QACzC,OAAO,GAAG,YAAY,IAAI,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;IACnE,CAAC;IACD,IAAI,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAChC,OAAO,SAAS,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;IAClD,CAAC;IACD,OAAO,KAAK,CAAC,OAAO,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,YAAY,CAAC,KAAc,EAAE,YAAoB,EAAE,YAAoB;IAC9E,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;IAC1D,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC;IAC/E,CAAC;IACD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACjD,MAAM,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,KAAK,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;QAChE,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,mBAAmB,CAAC,MAAmB;IAC9C,MAAM,YAAY,GAAG,MAAM,CAAC,aAAa,IAAI,yBAAyB,CAAC;IACvE,MAAM,YAAY,GAAG,YAAY,KAAK,yBAAyB,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,YAAY,CAAC;IACpG,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,YAAY,CAAgB,CAAC;IAClF,OAAO;QACL,GAAG,SAAS;QACZ,YAAY,EAAE,YAAY,CAAC,WAAW,EAAE;QACxC,aAAa,EAAE,YAAY;KAC5B,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,UAAkB,EAClB,MAAmB;IAEnB,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,EAAE,CAAC;QACpC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IACjD,CAAC;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACrC,MAAM,gBAAgB,GAAG,IAAI,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC;IAC1D,MAAM,cAAc,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAEnD,IAAI,MAAM,CAAC,aAAa,KAAK,yBAAyB,EAAE,CAAC;QACvD,MAAM,mBAAmB,GAAG,IAAI,CAAC,QAAQ,EAAE,yBAAyB,CAAC,CAAC;QACtE,MAAM,gBAAgB,GAAG,IAAI,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;QAC5D,IAAI,CAAC,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC,EAAE,CAAC;YAC7E,MAAM,MAAM,CAAC,mBAAmB,EAAE,gBAAgB,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;IAED,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC,EAAE,CAAC;QACtC,MAAM,eAAe,CAAC,gBAAgB,EAAE,cAAc,CAAC,CAAC;IAC1D,CAAC;IAED,OAAO;QACL,MAAM,EAAE,cAAc;QACtB,UAAU,EAAE,gBAAgB;QAC5B,QAAQ,EAAE,IAAI;KACf,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path helpers for workspace-relative files.
|
|
3
|
+
*
|
|
4
|
+
* Config paths may be expressed either:
|
|
5
|
+
* - relative to `workspace_dir` (e.g. "prompts/orchestrator.user.txt"), OR
|
|
6
|
+
* - already prefixed with `workspace_dir` (e.g. "envoi/prompts/orchestrator.user.txt")
|
|
7
|
+
*
|
|
8
|
+
* We support both forms for backwards compatibility.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Resolves a config path into a filesystem path.
|
|
12
|
+
*
|
|
13
|
+
* If `p` is absolute, returns it as-is.
|
|
14
|
+
* If `p` already starts with `${workspaceDir}/`, returns it as-is.
|
|
15
|
+
* Otherwise returns `join(workspaceDir, p)`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveInWorkspace(workspaceDir: string, p: string): string;
|
|
18
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/lib/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAO1E"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path helpers for workspace-relative files.
|
|
3
|
+
*
|
|
4
|
+
* Config paths may be expressed either:
|
|
5
|
+
* - relative to `workspace_dir` (e.g. "prompts/orchestrator.user.txt"), OR
|
|
6
|
+
* - already prefixed with `workspace_dir` (e.g. "envoi/prompts/orchestrator.user.txt")
|
|
7
|
+
*
|
|
8
|
+
* We support both forms for backwards compatibility.
|
|
9
|
+
*/
|
|
10
|
+
import { isAbsolute, join } from 'node:path';
|
|
11
|
+
/**
|
|
12
|
+
* Resolves a config path into a filesystem path.
|
|
13
|
+
*
|
|
14
|
+
* If `p` is absolute, returns it as-is.
|
|
15
|
+
* If `p` already starts with `${workspaceDir}/`, returns it as-is.
|
|
16
|
+
* Otherwise returns `join(workspaceDir, p)`.
|
|
17
|
+
*/
|
|
18
|
+
export function resolveInWorkspace(workspaceDir, p) {
|
|
19
|
+
if (isAbsolute(p))
|
|
20
|
+
return p;
|
|
21
|
+
const normalized = p.replace(/^[.][/]/, '');
|
|
22
|
+
if (normalized === workspaceDir || normalized.startsWith(`${workspaceDir}/`)) {
|
|
23
|
+
return normalized;
|
|
24
|
+
}
|
|
25
|
+
return join(workspaceDir, normalized);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/lib/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE7C;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,YAAoB,EAAE,CAAS;IAChE,IAAI,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IAC5B,MAAM,UAAU,GAAG,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAC5C,IAAI,UAAU,KAAK,YAAY,IAAI,UAAU,CAAC,UAAU,CAAC,GAAG,YAAY,GAAG,CAAC,EAAE,CAAC;QAC7E,OAAO,UAAU,CAAC;IACpB,CAAC;IACD,OAAO,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preflight checks for determining if a tick can safely start.
|
|
3
|
+
*
|
|
4
|
+
* Runs a series of checks and returns a PreflightResult indicating
|
|
5
|
+
* whether execution can proceed or is blocked.
|
|
6
|
+
*/
|
|
7
|
+
import type { EnvoiConfig } from '../types/config.js';
|
|
8
|
+
import type { PreflightResult } from '../types/preflight.js';
|
|
9
|
+
/**
|
|
10
|
+
* Runs all preflight checks to determine if a tick can safely start.
|
|
11
|
+
*
|
|
12
|
+
* Checks are run in order, returning immediately on BLOCKED:
|
|
13
|
+
* 1. Git repo exists (if config.runner.require_git)
|
|
14
|
+
* 2. Worktree is clean (no uncommitted changes)
|
|
15
|
+
* 3. Cleanup .tmp files under config.workspace_dir
|
|
16
|
+
* 4. Check history size (vs config.history.max_mb)
|
|
17
|
+
* 5. Budget check (placeholder - returns warning for now)
|
|
18
|
+
*
|
|
19
|
+
* @param config - The Envoi configuration
|
|
20
|
+
* @returns PreflightResult indicating success or blocked state
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const result = await runPreflight(config);
|
|
25
|
+
* if (!result.ok) {
|
|
26
|
+
* console.error(`Blocked: ${result.blocked_code}: ${result.blocked_reason}`);
|
|
27
|
+
* process.exit(1);
|
|
28
|
+
* }
|
|
29
|
+
* console.log(`Preflight passed, base commit: ${result.base_commit}`);
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function runPreflight(config: EnvoiConfig): Promise<PreflightResult>;
|
|
33
|
+
//# sourceMappingURL=preflight.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preflight.d.ts","sourceRoot":"","sources":["../../src/lib/preflight.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAe,MAAM,uBAAuB,CAAC;AAuE1E;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC,CA2HhF"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preflight checks for determining if a tick can safely start.
|
|
3
|
+
*
|
|
4
|
+
* Runs a series of checks and returns a PreflightResult indicating
|
|
5
|
+
* whether execution can proceed or is blocked.
|
|
6
|
+
*/
|
|
7
|
+
import { stat, readdir } from 'node:fs/promises';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import { cleanupTmpFiles, isGlobPatternSafe } from './fs.js';
|
|
10
|
+
import { isGitRepo, isWorktreeCleanExcluding, getHeadCommit } from './git.js';
|
|
11
|
+
import { readWorkspaceState } from './workspace_state.js';
|
|
12
|
+
import { resolveInWorkspace } from './paths.js';
|
|
13
|
+
/**
|
|
14
|
+
* Creates a blocked PreflightResult with the given code and reason.
|
|
15
|
+
*/
|
|
16
|
+
function blocked(code, reason, warnings = []) {
|
|
17
|
+
return {
|
|
18
|
+
ok: false,
|
|
19
|
+
blocked_code: code,
|
|
20
|
+
blocked_reason: reason,
|
|
21
|
+
warnings,
|
|
22
|
+
base_commit: null,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Creates a successful PreflightResult.
|
|
27
|
+
*/
|
|
28
|
+
function success(baseCommit, warnings = []) {
|
|
29
|
+
return {
|
|
30
|
+
ok: true,
|
|
31
|
+
blocked_code: null,
|
|
32
|
+
blocked_reason: null,
|
|
33
|
+
warnings,
|
|
34
|
+
base_commit: baseCommit,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Calculates the total size of a directory in bytes.
|
|
39
|
+
*
|
|
40
|
+
* @param dirPath - Path to the directory
|
|
41
|
+
* @returns Total size in bytes, or 0 if directory doesn't exist
|
|
42
|
+
*/
|
|
43
|
+
async function getDirectorySize(dirPath) {
|
|
44
|
+
let totalSize = 0;
|
|
45
|
+
try {
|
|
46
|
+
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
47
|
+
for (const entry of entries) {
|
|
48
|
+
const entryPath = join(dirPath, entry.name);
|
|
49
|
+
if (entry.isFile()) {
|
|
50
|
+
try {
|
|
51
|
+
const stats = await stat(entryPath);
|
|
52
|
+
totalSize += stats.size;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Skip files we can't stat
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else if (entry.isDirectory()) {
|
|
59
|
+
// Recursively calculate subdirectory size
|
|
60
|
+
totalSize += await getDirectorySize(entryPath);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Directory doesn't exist or can't be read
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
return totalSize;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Runs all preflight checks to determine if a tick can safely start.
|
|
72
|
+
*
|
|
73
|
+
* Checks are run in order, returning immediately on BLOCKED:
|
|
74
|
+
* 1. Git repo exists (if config.runner.require_git)
|
|
75
|
+
* 2. Worktree is clean (no uncommitted changes)
|
|
76
|
+
* 3. Cleanup .tmp files under config.workspace_dir
|
|
77
|
+
* 4. Check history size (vs config.history.max_mb)
|
|
78
|
+
* 5. Budget check (placeholder - returns warning for now)
|
|
79
|
+
*
|
|
80
|
+
* @param config - The Envoi configuration
|
|
81
|
+
* @returns PreflightResult indicating success or blocked state
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* const result = await runPreflight(config);
|
|
86
|
+
* if (!result.ok) {
|
|
87
|
+
* console.error(`Blocked: ${result.blocked_code}: ${result.blocked_reason}`);
|
|
88
|
+
* process.exit(1);
|
|
89
|
+
* }
|
|
90
|
+
* console.log(`Preflight passed, base commit: ${result.base_commit}`);
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export async function runPreflight(config) {
|
|
94
|
+
const warnings = [];
|
|
95
|
+
let baseCommit = null;
|
|
96
|
+
// 1. Check if git repo exists (if required)
|
|
97
|
+
if (config.runner.require_git) {
|
|
98
|
+
if (!isGitRepo()) {
|
|
99
|
+
return blocked('BLOCKED_MISSING_CONFIG', 'Not inside a git repository (require_git is enabled)');
|
|
100
|
+
}
|
|
101
|
+
// 2. Check if worktree is clean (excluding runner-owned files)
|
|
102
|
+
const worktreeStatus = isWorktreeCleanExcluding(config.runner.runner_owned_globs);
|
|
103
|
+
if (!worktreeStatus.clean) {
|
|
104
|
+
return blocked('BLOCKED_DIRTY_WORKTREE', `Git worktree has uncommitted changes: ${worktreeStatus.dirtyFiles.join(', ')}`);
|
|
105
|
+
}
|
|
106
|
+
// Get base commit for tracking changes
|
|
107
|
+
try {
|
|
108
|
+
baseCommit = getHeadCommit();
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
return blocked('BLOCKED_MISSING_CONFIG', `Failed to get HEAD commit: ${error instanceof Error ? error.message : String(error)}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// 3. Validate delete_tmp_glob pattern if configured
|
|
115
|
+
const deleteGlob = config.runner.crash_cleanup?.delete_tmp_glob;
|
|
116
|
+
if (deleteGlob && deleteGlob.trim() !== '') {
|
|
117
|
+
const globSafety = isGlobPatternSafe(deleteGlob);
|
|
118
|
+
if (!globSafety.safe) {
|
|
119
|
+
return blocked('BLOCKED_CRASH_RECOVERY_REQUIRED', `Unsafe delete_tmp_glob pattern: ${globSafety.reason}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// 4. Cleanup .tmp files (crash artifacts)
|
|
123
|
+
try {
|
|
124
|
+
const deleted = await cleanupTmpFiles(config.workspace_dir);
|
|
125
|
+
if (deleted.length > 0) {
|
|
126
|
+
warnings.push(`Cleaned up ${deleted.length} stale .tmp file(s): ${deleted.join(', ')}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
// If cleanup fails, it might indicate a crash recovery issue
|
|
131
|
+
// For now, just add a warning - the directory might not exist yet
|
|
132
|
+
warnings.push(`Could not cleanup tmp files in ${config.workspace_dir}: ${error instanceof Error ? error.message : String(error)}`);
|
|
133
|
+
}
|
|
134
|
+
// 4. Check history size vs cap
|
|
135
|
+
if (config.history.enabled) {
|
|
136
|
+
const historyPath = resolveInWorkspace(config.workspace_dir, config.history.dir);
|
|
137
|
+
const historySizeBytes = await getDirectorySize(historyPath);
|
|
138
|
+
const historySizeMb = historySizeBytes / (1024 * 1024);
|
|
139
|
+
const maxMb = config.history.max_mb;
|
|
140
|
+
if (historySizeMb >= maxMb) {
|
|
141
|
+
return blocked('BLOCKED_HISTORY_CAP_CLEANUP_REQUIRED', `History directory (${historySizeMb.toFixed(2)} MB) exceeds cap (${maxMb} MB). Manual cleanup required.`);
|
|
142
|
+
}
|
|
143
|
+
// Warn if approaching limit (>80%)
|
|
144
|
+
const warnThreshold = maxMb * 0.8;
|
|
145
|
+
if (historySizeMb >= warnThreshold) {
|
|
146
|
+
warnings.push(`History size (${historySizeMb.toFixed(2)} MB) is approaching cap (${maxMb} MB)`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// 5. Budget hard-cap check
|
|
150
|
+
// Read workspace state and check if any budget exceeds its per-milestone cap
|
|
151
|
+
try {
|
|
152
|
+
const state = await readWorkspaceState(config.workspace_dir);
|
|
153
|
+
const caps = config.budgets.per_milestone;
|
|
154
|
+
if (state.budgets.ticks >= caps.max_ticks) {
|
|
155
|
+
return blocked('BLOCKED_BUDGET_CAP', `Tick budget exceeded: ${state.budgets.ticks} >= ${caps.max_ticks}`);
|
|
156
|
+
}
|
|
157
|
+
if (state.budgets.orchestrator_calls >= caps.max_orchestrator_calls) {
|
|
158
|
+
return blocked('BLOCKED_BUDGET_CAP', `Orchestrator call budget exceeded: ${state.budgets.orchestrator_calls} >= ${caps.max_orchestrator_calls}`);
|
|
159
|
+
}
|
|
160
|
+
if (state.budgets.builder_calls >= caps.max_builder_calls) {
|
|
161
|
+
return blocked('BLOCKED_BUDGET_CAP', `Builder call budget exceeded: ${state.budgets.builder_calls} >= ${caps.max_builder_calls}`);
|
|
162
|
+
}
|
|
163
|
+
if (state.budgets.verify_runs >= caps.max_verify_runs) {
|
|
164
|
+
return blocked('BLOCKED_BUDGET_CAP', `Verify run budget exceeded: ${state.budgets.verify_runs} >= ${caps.max_verify_runs}`);
|
|
165
|
+
}
|
|
166
|
+
// Add warning if approaching any limit
|
|
167
|
+
if (state.budget_warning) {
|
|
168
|
+
warnings.push('Budget warning: approaching limit on one or more budget categories');
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
// If we can't read state, add warning but don't block
|
|
173
|
+
warnings.push(`Could not read workspace state for budget check: ${error instanceof Error ? error.message : String(error)}`);
|
|
174
|
+
}
|
|
175
|
+
return success(baseCommit, warnings);
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=preflight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preflight.js","sourceRoot":"","sources":["../../src/lib/preflight.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt budgeting helpers to keep context payloads bounded and deterministic.
|
|
3
|
+
*/
|
|
4
|
+
export interface TruncatePromptSectionResult {
|
|
5
|
+
text: string;
|
|
6
|
+
truncated: boolean;
|
|
7
|
+
originalChars: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Truncates a prompt section to a max size with a deterministic marker.
|
|
11
|
+
*
|
|
12
|
+
* Strategy:
|
|
13
|
+
* - keep the beginning (usually most structural context)
|
|
14
|
+
* - keep the tail (usually latest status/error detail)
|
|
15
|
+
* - insert a deterministic marker between both
|
|
16
|
+
*/
|
|
17
|
+
export declare function truncatePromptSection(sectionName: string, value: string, maxChars: number): TruncatePromptSectionResult;
|
|
18
|
+
//# sourceMappingURL=prompt_budget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt_budget.d.ts","sourceRoot":"","sources":["../../src/lib/prompt_budget.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GACf,2BAA2B,CA0B7B"}
|