Remove unused root Claude guide
This commit is contained in:
@@ -25,9 +25,8 @@ import {
|
||||
import {
|
||||
computeVerificationSnapshotId,
|
||||
formatVerificationResponse,
|
||||
resolveVerificationResponsesDir,
|
||||
runVerificationRequestDirect,
|
||||
VERIFICATION_PROFILES,
|
||||
waitForVerificationResponse,
|
||||
} from './verification.js';
|
||||
import { resolveIpcDirectories } from './ipc-paths.js';
|
||||
import { buildSendMessageIpcPayload } from './ipc-message.js';
|
||||
@@ -38,8 +37,6 @@ const MESSAGES_DIR = path.join(HOST_IPC_DIR, 'messages');
|
||||
const TASKS_DIR = path.join(HOST_IPC_DIR, 'tasks');
|
||||
const HOST_EVIDENCE_RESPONSES_DIR =
|
||||
resolveHostEvidenceResponsesDir(HOST_IPC_DIR);
|
||||
const VERIFICATION_RESPONSES_DIR =
|
||||
resolveVerificationResponsesDir(HOST_IPC_DIR);
|
||||
const REPO_ROOT = process.env.EJCLAW_WORK_DIR || process.cwd();
|
||||
|
||||
// Context from environment variables (set by the agent runner)
|
||||
@@ -453,20 +450,12 @@ server.tool(
|
||||
.toString(36)
|
||||
.slice(2, 8)}`;
|
||||
|
||||
writeIpcFile(TASKS_DIR, {
|
||||
type: 'verification_request',
|
||||
requestId,
|
||||
profile: args.profile,
|
||||
expected_snapshot_id: snapshotId,
|
||||
groupFolder,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await waitForVerificationResponse(
|
||||
VERIFICATION_RESPONSES_DIR,
|
||||
const response = await runVerificationRequestDirect(REPO_ROOT, {
|
||||
requestId,
|
||||
);
|
||||
profile: args.profile,
|
||||
expectedSnapshotId: snapshotId,
|
||||
});
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import fs from 'fs';
|
||||
import { execFile } from 'child_process';
|
||||
import path from 'path';
|
||||
import { pathToFileURL } from 'url';
|
||||
export {
|
||||
computeVerificationSnapshotId,
|
||||
resolveVerificationResponsesDir,
|
||||
} from '../../../shared/verification-snapshot.js';
|
||||
|
||||
export const VERIFICATION_PROFILES = [
|
||||
@@ -13,6 +13,45 @@ export const VERIFICATION_PROFILES = [
|
||||
|
||||
export type VerificationProfile = (typeof VERIFICATION_PROFILES)[number];
|
||||
|
||||
type VerificationHelperEnvKey =
|
||||
| 'PATH'
|
||||
| 'HOME'
|
||||
| 'USER'
|
||||
| 'LOGNAME'
|
||||
| 'SHELL'
|
||||
| 'LANG'
|
||||
| 'LC_ALL'
|
||||
| 'LC_CTYPE'
|
||||
| 'NODE_PATH'
|
||||
| 'NODE_OPTIONS'
|
||||
| 'TERM'
|
||||
| 'COLORTERM'
|
||||
| 'FORCE_COLOR';
|
||||
|
||||
const VERIFICATION_HELPER_ENV_KEYS: readonly VerificationHelperEnvKey[] = [
|
||||
'PATH',
|
||||
'HOME',
|
||||
'USER',
|
||||
'LOGNAME',
|
||||
'SHELL',
|
||||
'LANG',
|
||||
'LC_ALL',
|
||||
'LC_CTYPE',
|
||||
'NODE_PATH',
|
||||
'NODE_OPTIONS',
|
||||
'TERM',
|
||||
'COLORTERM',
|
||||
'FORCE_COLOR',
|
||||
];
|
||||
const HELPER_TIMEOUT_MS = 20 * 60 * 1000;
|
||||
const HELPER_MAX_BUFFER = 20 * 1024 * 1024;
|
||||
|
||||
export interface VerificationRequest {
|
||||
requestId: string;
|
||||
profile: VerificationProfile;
|
||||
expectedSnapshotId?: string;
|
||||
}
|
||||
|
||||
export interface VerificationResponse {
|
||||
requestId: string;
|
||||
ok: boolean;
|
||||
@@ -36,33 +75,87 @@ export function isVerificationProfile(
|
||||
);
|
||||
}
|
||||
|
||||
export async function waitForVerificationResponse(
|
||||
responseDir: string,
|
||||
requestId: string,
|
||||
options?: {
|
||||
timeoutMs?: number;
|
||||
pollMs?: number;
|
||||
},
|
||||
): Promise<VerificationResponse> {
|
||||
const timeoutMs = options?.timeoutMs ?? 20 * 60 * 1000;
|
||||
const pollMs = options?.pollMs ?? 100;
|
||||
const responsePath = path.join(responseDir, `${requestId}.json`);
|
||||
const deadline = Date.now() + timeoutMs;
|
||||
function buildVerificationHelperEnv(): NodeJS.ProcessEnv {
|
||||
const env: NodeJS.ProcessEnv = {
|
||||
TZ: 'Asia/Seoul',
|
||||
CI: '1',
|
||||
};
|
||||
|
||||
while (Date.now() <= deadline) {
|
||||
if (fs.existsSync(responsePath)) {
|
||||
const response = JSON.parse(
|
||||
fs.readFileSync(responsePath, 'utf-8'),
|
||||
) as VerificationResponse;
|
||||
fs.unlinkSync(responsePath);
|
||||
return response;
|
||||
for (const key of VERIFICATION_HELPER_ENV_KEYS) {
|
||||
const value = process.env[key];
|
||||
if (value) {
|
||||
env[key] = value;
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, pollMs));
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Timed out waiting for verification response: ${requestId}`,
|
||||
return env;
|
||||
}
|
||||
|
||||
function execFileCapture(
|
||||
file: string,
|
||||
args: string[],
|
||||
options?: {
|
||||
cwd?: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
},
|
||||
): Promise<{ stdout: string; stderr: string }> {
|
||||
return new Promise((resolve, reject) => {
|
||||
execFile(
|
||||
file,
|
||||
args,
|
||||
{
|
||||
encoding: 'utf8',
|
||||
timeout: HELPER_TIMEOUT_MS,
|
||||
maxBuffer: HELPER_MAX_BUFFER,
|
||||
cwd: options?.cwd,
|
||||
env: options?.env,
|
||||
},
|
||||
(error, stdout, stderr) => {
|
||||
if (error) {
|
||||
reject(
|
||||
Object.assign(error, {
|
||||
stdout,
|
||||
stderr,
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve({ stdout, stderr });
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export async function runVerificationRequestDirect(
|
||||
repoRoot: string,
|
||||
request: VerificationRequest,
|
||||
): Promise<VerificationResponse> {
|
||||
const helperPath = path.join(
|
||||
repoRoot,
|
||||
'shared',
|
||||
'verification-request-runner.js',
|
||||
);
|
||||
const helperEnv = buildVerificationHelperEnv();
|
||||
const { stdout, stderr } = await execFileCapture(
|
||||
'bun',
|
||||
[helperPath, repoRoot, JSON.stringify(request)],
|
||||
{
|
||||
cwd: repoRoot,
|
||||
env: helperEnv,
|
||||
},
|
||||
);
|
||||
|
||||
try {
|
||||
return JSON.parse(stdout) as VerificationResponse;
|
||||
} catch (error) {
|
||||
const detail = stderr.trim() || stdout.trim();
|
||||
throw new Error(
|
||||
`Failed to parse verification response from ${pathToFileURL(helperPath).href}: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}${detail ? `\n${detail}` : ''}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function formatVerificationResponse(
|
||||
|
||||
@@ -7,8 +7,6 @@ import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
computeVerificationSnapshotId,
|
||||
formatVerificationResponse,
|
||||
resolveVerificationResponsesDir,
|
||||
waitForVerificationResponse,
|
||||
} from '../src/verification.js';
|
||||
|
||||
describe('runner verification helpers', () => {
|
||||
@@ -26,39 +24,6 @@ describe('runner verification helpers', () => {
|
||||
expect(second).toBe(first);
|
||||
});
|
||||
|
||||
it('reads and removes the verification response file', async () => {
|
||||
const ipcDir = fs.mkdtempSync(
|
||||
path.join(os.tmpdir(), 'ejclaw-verification-ipc-'),
|
||||
);
|
||||
const responseDir = resolveVerificationResponsesDir(ipcDir);
|
||||
fs.mkdirSync(responseDir, { recursive: true });
|
||||
|
||||
const responsePath = path.join(responseDir, 'req-1.json');
|
||||
fs.writeFileSync(
|
||||
responsePath,
|
||||
JSON.stringify({
|
||||
requestId: 'req-1',
|
||||
ok: true,
|
||||
profile: 'typecheck',
|
||||
command: 'npm run typecheck',
|
||||
stdout: '',
|
||||
stderr: '',
|
||||
exitCode: 0,
|
||||
snapshotId: 'fs:abc123',
|
||||
runtimeVersion: 'host:bun@test',
|
||||
}),
|
||||
);
|
||||
|
||||
const response = await waitForVerificationResponse(responseDir, 'req-1', {
|
||||
timeoutMs: 100,
|
||||
pollMs: 10,
|
||||
});
|
||||
|
||||
expect(response.ok).toBe(true);
|
||||
expect(response.snapshotId).toBe('fs:abc123');
|
||||
expect(fs.existsSync(responsePath)).toBe(false);
|
||||
});
|
||||
|
||||
it('formats the response into a compact MCP-friendly text block', () => {
|
||||
const text = formatVerificationResponse({
|
||||
requestId: 'req-2',
|
||||
|
||||
Reference in New Issue
Block a user