fix: salvage EJClaw runtime hardening

- add dist freshness guard to deploy flow
- preserve message source provenance for IPC injection
- harden Codex usage fallback and workspace package manager detection
- document owner branch return protocol
This commit is contained in:
ejclaw
2026-04-24 15:02:09 +09:00
parent a36c8a5f07
commit e036521054
19 changed files with 1059 additions and 51 deletions

View File

@@ -18,6 +18,7 @@ export interface WorkspaceDependencyInstallResult {
commandText?: string;
}
const COREPACK_PROJECT_SPEC_DISABLED = '0';
const INSTALL_STATE_FILENAME = '.ejclaw-install-state.json';
const NODE_MODULES_NOISE_ENTRIES = new Set([
'.cache',
@@ -117,6 +118,52 @@ function buildCorepackCommand(
};
}
function findNearestAncestorPackageManager(
repoDir: string,
): WorkspacePackageManager | null {
let currentDir = path.dirname(path.resolve(repoDir));
while (true) {
const packageManager = detectPackageManagerFromField(
readPackageJsonMetadata(currentDir)?.packageManager,
);
if (packageManager) {
return packageManager;
}
const parentDir = path.dirname(currentDir);
if (parentDir === currentDir) {
return null;
}
currentDir = parentDir;
}
}
export function buildWorkspaceCommandEnvironment(
repoDir: string,
packageManager: WorkspacePackageManager,
baseEnv: NodeJS.ProcessEnv = process.env,
): NodeJS.ProcessEnv {
if (packageManager !== 'pnpm') {
return baseEnv;
}
const localPackageManager = readPackageJsonMetadata(repoDir)?.packageManager;
if (typeof localPackageManager === 'string' && localPackageManager.trim()) {
return baseEnv;
}
const ancestorPackageManager = findNearestAncestorPackageManager(repoDir);
if (!ancestorPackageManager || ancestorPackageManager === packageManager) {
return baseEnv;
}
return {
...baseEnv,
COREPACK_ENABLE_PROJECT_SPEC: COREPACK_PROJECT_SPEC_DISABLED,
};
}
function computeInstallFingerprint(repoDir: string): string | null {
const packageJsonPath = path.join(repoDir, 'package.json');
if (!fs.existsSync(packageJsonPath)) {
@@ -359,6 +406,7 @@ export function ensureWorkspaceDependenciesInstalled(
try {
execFileSync(command.file, command.args, {
cwd: repoDir,
env: buildWorkspaceCommandEnvironment(repoDir, command.packageManager),
encoding: 'utf-8',
stdio: ['ignore', 'pipe', 'pipe'],
});
@@ -420,6 +468,7 @@ export function detectPnpmStorePath(workspaceDir: string): string | null {
try {
const storePath = execFileSync(file, args, {
cwd: workspaceDir,
env: buildWorkspaceCommandEnvironment(workspaceDir, 'pnpm'),
encoding: 'utf-8',
stdio: ['ignore', 'pipe', 'pipe'],
timeout: 5000,