@fitlab-ai/agent-infra 0.11.13 → 0.11.15
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/dist/bin/internal-cli.js +2 -7
- package/dist/lib/agent-clients/adapter.js +0 -3
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +5 -9
- package/dist/lib/agent-clients/adapters/codex-lifecycle/cli.js +1 -5
- package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +1 -5
- package/dist/lib/agent-clients/adapters/codex-orchestration.js +1 -3
- package/dist/lib/agent-clients/adapters/codex.js +0 -2
- package/dist/lib/internal/cli-route-inventory.js +0 -1
- package/dist/lib/internal/task-finalization.js +7 -5
- package/dist/lib/internal/task-lifecycle.js +3 -1
- package/dist/lib/internal/task-operation-registry.js +1 -3
- package/dist/lib/internal/task-validate.js +14 -124
- package/dist/lib/platform/pr-change-report.js +10 -3
- package/dist/lib/platform/release-notes.js +2 -2
- package/dist/lib/process-data/index.js +15 -4
- package/dist/lib/sandbox/commands/control-status.js +0 -1
- package/dist/lib/sandbox/commands/create.js +5 -4
- package/dist/lib/sandbox/config.js +12 -0
- package/dist/lib/sandbox/control/audit.js +1 -2
- package/dist/lib/sandbox/control/client.js +12 -47
- package/dist/lib/sandbox/control/executor.js +6 -10
- package/dist/lib/sandbox/control/host-recovery.js +89 -27
- package/dist/lib/sandbox/control/lifecycle.js +22 -37
- package/dist/lib/sandbox/control/protocol.js +8 -36
- package/dist/lib/sandbox/control/server.js +2 -21
- package/dist/lib/sandbox/environment.js +7 -8
- package/dist/lib/sandbox/mounts.js +71 -59
- package/dist/lib/sandbox/recovery.js +2 -3
- package/dist/lib/sandbox/removal.js +19 -4
- package/dist/lib/sandbox/workspace-identity.js +30 -15
- package/dist/lib/sandbox/workspace-view.js +41 -4
- package/dist/lib/task/control-authority.js +39 -17
- package/dist/lib/task/control-recovery.js +1 -2
- package/dist/lib/task/finalization.js +13 -2
- package/dist/lib/task/lifecycle.js +122 -35
- package/dist/lib/task/short-id.js +2 -2
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapter.ts +0 -27
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +6 -12
- package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +1 -5
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +0 -7
- package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +1 -5
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +1 -4
- package/lib/agent-clients/adapters/codex-orchestration.ts +1 -3
- package/lib/agent-clients/adapters/codex.ts +0 -2
- package/lib/internal/cli-route-inventory.ts +0 -1
- package/lib/internal/task-finalization.ts +8 -6
- package/lib/internal/task-lifecycle.ts +3 -1
- package/lib/internal/task-operation-registry.ts +1 -3
- package/lib/internal/task-validate.ts +13 -127
- package/lib/platform/pr-change-report.ts +9 -3
- package/lib/platform/release-notes.ts +2 -2
- package/lib/process-data/index.ts +15 -4
- package/lib/sandbox/commands/control-status.ts +0 -1
- package/lib/sandbox/commands/create.ts +5 -4
- package/lib/sandbox/config.ts +13 -0
- package/lib/sandbox/control/audit.ts +1 -2
- package/lib/sandbox/control/client.ts +13 -61
- package/lib/sandbox/control/executor.ts +7 -13
- package/lib/sandbox/control/host-recovery.ts +94 -21
- package/lib/sandbox/control/lifecycle.ts +22 -37
- package/lib/sandbox/control/protocol.ts +9 -45
- package/lib/sandbox/control/server.ts +2 -17
- package/lib/sandbox/environment.ts +7 -8
- package/lib/sandbox/mounts.ts +92 -75
- package/lib/sandbox/recovery.ts +2 -3
- package/lib/sandbox/removal.ts +16 -4
- package/lib/sandbox/workspace-identity.ts +36 -13
- package/lib/sandbox/workspace-view.ts +40 -5
- package/lib/task/control-authority.ts +40 -26
- package/lib/task/control-recovery.ts +2 -3
- package/lib/task/finalization.ts +15 -2
- package/lib/task/lifecycle.ts +135 -33
- package/lib/task/short-id.ts +5 -2
- package/package.json +1 -1
- package/templates/.agents/rules/lifecycle-orchestration.en.md +3 -4
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +3 -4
- package/templates/.agents/rules/task-short-id.en.md +18 -12
- package/templates/.agents/rules/task-short-id.zh-CN.md +12 -7
- package/templates/.agents/skills/complete-task/SKILL.en.md +10 -2
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +10 -2
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +12 -2
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +12 -2
- package/templates/.agents/skills/create-release-note/SKILL.en.md +3 -1
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +3 -1
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -5
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +13 -2
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +13 -2
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/watch-pr/SKILL.en.md +19 -2
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +20 -3
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +14 -0
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +15 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-client.js +0 -130
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +0 -127
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-operation.js +0 -131
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-registration.js +0 -275
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.js +0 -106
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +0 -345
- package/dist/lib/runtime/agent-runtime.js +0 -33
- package/dist/lib/sandbox/control/agent-operation.js +0 -34
- package/dist/lib/task/runtime-paths.js +0 -10
- package/lib/agent-clients/adapters/codex-lifecycle/controller-client.ts +0 -158
- package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +0 -163
- package/lib/agent-clients/adapters/codex-lifecycle/controller-operation.ts +0 -151
- package/lib/agent-clients/adapters/codex-lifecycle/controller-registration.ts +0 -352
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.ts +0 -109
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +0 -437
- package/lib/runtime/agent-runtime.ts +0 -41
- package/lib/sandbox/control/agent-operation.ts +0 -49
- package/lib/task/runtime-paths.ts +0 -12
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import { closeCodexControllerRegistration, CodexControllerRegistrationError, openCodexControllerRegistration, readCodexControllerRegistration, resolveCodexControllerBinding } from './controller-registration.js';
|
|
2
|
-
import { computeLifecycleBuildIdentity } from './build-identity.js';
|
|
3
|
-
function validProcess(value) {
|
|
4
|
-
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
5
|
-
return false;
|
|
6
|
-
const process = value;
|
|
7
|
-
return Object.keys(process).sort().join(',') === 'pid,startTime'
|
|
8
|
-
&& Number.isSafeInteger(process.pid) && process.pid > 0
|
|
9
|
-
&& Number.isSafeInteger(process.startTime) && process.startTime >= 0;
|
|
10
|
-
}
|
|
11
|
-
function validProof(value) {
|
|
12
|
-
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
13
|
-
return false;
|
|
14
|
-
const proof = value;
|
|
15
|
-
return Object.keys(proof).sort().join(',') === 'controllerProcess,leaseId,leaseSecret,version'
|
|
16
|
-
&& proof.version === 1
|
|
17
|
-
&& typeof proof.leaseId === 'string' && /^[a-f0-9]{64}$/u.test(proof.leaseId)
|
|
18
|
-
&& typeof proof.leaseSecret === 'string' && /^[a-f0-9]{64}$/u.test(proof.leaseSecret)
|
|
19
|
-
&& validProcess(proof.controllerProcess);
|
|
20
|
-
}
|
|
21
|
-
function validate(params) {
|
|
22
|
-
const { operation, payload, manifest } = params;
|
|
23
|
-
const expectedKeys = operation === 'controller.open' ? ['controllerProcess'] : ['proof'];
|
|
24
|
-
if (!['controller.open', 'controller.close', 'controller.verify'].includes(operation)
|
|
25
|
-
|| Object.keys(payload).sort().join(',') !== expectedKeys.sort().join(',')
|
|
26
|
-
|| (operation === 'controller.open'
|
|
27
|
-
? !validProcess(payload.controllerProcess)
|
|
28
|
-
: !validProof(payload.proof))) {
|
|
29
|
-
throw new Error('SANDBOX_CONTROL_REQUEST_INVALID: controller operation payload is invalid');
|
|
30
|
-
}
|
|
31
|
-
if (manifest.mode !== 'task-bound' || !manifest.taskId) {
|
|
32
|
-
throw new Error('SANDBOX_CONTROL_BRANCH_ONLY: controller operations require a task-bound sandbox');
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
async function execute(params) {
|
|
36
|
-
const { operation, payload, manifest, manifestPath } = params;
|
|
37
|
-
try {
|
|
38
|
-
const result = operation === 'controller.open'
|
|
39
|
-
? openCodexControllerRegistration({
|
|
40
|
-
manifest,
|
|
41
|
-
manifestPath,
|
|
42
|
-
controllerProcess: payload.controllerProcess,
|
|
43
|
-
buildIdentity: computeLifecycleBuildIdentity(manifest.repoRoot)
|
|
44
|
-
})
|
|
45
|
-
: operation === 'controller.close'
|
|
46
|
-
? closeCodexControllerRegistration({
|
|
47
|
-
manifest,
|
|
48
|
-
manifestPath,
|
|
49
|
-
proof: payload.proof
|
|
50
|
-
})
|
|
51
|
-
: (() => {
|
|
52
|
-
const binding = resolveCodexControllerBinding({
|
|
53
|
-
manifest,
|
|
54
|
-
manifestPath,
|
|
55
|
-
proof: payload.proof,
|
|
56
|
-
buildIdentity: computeLifecycleBuildIdentity(manifest.repoRoot)
|
|
57
|
-
});
|
|
58
|
-
return {
|
|
59
|
-
version: 1,
|
|
60
|
-
status: 'verified',
|
|
61
|
-
changed: false,
|
|
62
|
-
lease: null,
|
|
63
|
-
binding: {
|
|
64
|
-
taskId: manifest.taskId,
|
|
65
|
-
controlGeneration: binding.controlGeneration,
|
|
66
|
-
controllerInstanceDigest: binding.instanceDigest
|
|
67
|
-
},
|
|
68
|
-
error: null
|
|
69
|
-
};
|
|
70
|
-
})();
|
|
71
|
-
return { exitCode: 0, stdout: `${JSON.stringify(result)}\n`, stderr: '' };
|
|
72
|
-
}
|
|
73
|
-
catch (error) {
|
|
74
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
75
|
-
const code = error instanceof CodexControllerRegistrationError
|
|
76
|
-
? error.code
|
|
77
|
-
: /^([A-Z][A-Z0-9_]+)/u.exec(message)?.[1] ?? 'CODEX_SANDBOX_CONTROLLER_FAILED';
|
|
78
|
-
return {
|
|
79
|
-
exitCode: 1,
|
|
80
|
-
stdout: `${JSON.stringify({
|
|
81
|
-
version: 1,
|
|
82
|
-
status: 'failed',
|
|
83
|
-
changed: false,
|
|
84
|
-
lease: null,
|
|
85
|
-
error: { code, message, retryable: false }
|
|
86
|
-
})}\n`,
|
|
87
|
-
stderr: ''
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
function recover(params) {
|
|
92
|
-
if (params.stdout === null)
|
|
93
|
-
return { consistent: false };
|
|
94
|
-
let output;
|
|
95
|
-
try {
|
|
96
|
-
const parsed = JSON.parse(params.stdout);
|
|
97
|
-
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
98
|
-
return { consistent: false };
|
|
99
|
-
output = parsed;
|
|
100
|
-
}
|
|
101
|
-
catch {
|
|
102
|
-
return { consistent: false };
|
|
103
|
-
}
|
|
104
|
-
if (output.error !== null || typeof output.status !== 'string')
|
|
105
|
-
return { consistent: false };
|
|
106
|
-
try {
|
|
107
|
-
const registration = readCodexControllerRegistration(params.manifestPath);
|
|
108
|
-
if (params.operation === 'controller.open') {
|
|
109
|
-
const lease = output.lease;
|
|
110
|
-
return { consistent: output.status === 'opened' && output.changed === true
|
|
111
|
-
&& lease?.controlGeneration === registration.controlGeneration
|
|
112
|
-
&& lease?.controllerInstanceDigest === registration.controllerInstanceDigest
|
|
113
|
-
&& registration.controlGeneration === params.manifest.generation };
|
|
114
|
-
}
|
|
115
|
-
if (params.operation === 'controller.verify') {
|
|
116
|
-
const binding = output.binding;
|
|
117
|
-
return { consistent: output.status === 'verified' && output.changed === false
|
|
118
|
-
&& binding?.controlGeneration === registration.controlGeneration
|
|
119
|
-
&& binding?.controllerInstanceDigest === registration.controllerInstanceDigest };
|
|
120
|
-
}
|
|
121
|
-
return { consistent: false };
|
|
122
|
-
}
|
|
123
|
-
catch (error) {
|
|
124
|
-
return { consistent: params.operation === 'controller.close'
|
|
125
|
-
&& error.code === 'CODEX_SANDBOX_CONTROLLER_REGISTRATION_MISSING'
|
|
126
|
-
&& output.status === 'closed' && typeof output.changed === 'boolean' };
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
const codexControllerOperation = Object.freeze({ validate, execute, recover });
|
|
130
|
-
export { codexControllerOperation };
|
|
131
|
-
//# sourceMappingURL=controller-operation.js.map
|
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
import crypto from 'node:crypto';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { parseLinuxProcessStat } from '../../../server/process-state.js';
|
|
5
|
-
import { commandForEngine, runProbe } from '../../../sandbox/shell.js';
|
|
6
|
-
const CONTROLLER_TTL_MS = 4 * 60 * 60 * 1_000;
|
|
7
|
-
const HEX_256 = /^[a-f0-9]{64}$/u;
|
|
8
|
-
const REGISTRATION_KEYS = [
|
|
9
|
-
'buildIdentity', 'containerId', 'controlGeneration', 'controllerInstanceDigest',
|
|
10
|
-
'controllerProcess', 'expiresAt', 'issuedAt', 'leaseId', 'leaseSecretHash',
|
|
11
|
-
'taskId', 'version'
|
|
12
|
-
].sort().join(',');
|
|
13
|
-
export class CodexControllerRegistrationError extends Error {
|
|
14
|
-
code;
|
|
15
|
-
constructor(code, message) {
|
|
16
|
-
super(`${code}: ${message}`);
|
|
17
|
-
this.name = code;
|
|
18
|
-
this.code = code;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
function fail(code, message) {
|
|
22
|
-
throw new CodexControllerRegistrationError(code, message);
|
|
23
|
-
}
|
|
24
|
-
function exactKeys(value, keys) {
|
|
25
|
-
return Object.keys(value).sort().join(',') === [...keys].sort().join(',');
|
|
26
|
-
}
|
|
27
|
-
function validProcess(value) {
|
|
28
|
-
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
29
|
-
return false;
|
|
30
|
-
const processValue = value;
|
|
31
|
-
return exactKeys(processValue, ['pid', 'startTime'])
|
|
32
|
-
&& Number.isSafeInteger(processValue.pid) && processValue.pid > 0
|
|
33
|
-
&& Number.isSafeInteger(processValue.startTime) && processValue.startTime >= 0;
|
|
34
|
-
}
|
|
35
|
-
function validBuild(value) {
|
|
36
|
-
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
37
|
-
return false;
|
|
38
|
-
const build = value;
|
|
39
|
-
return exactKeys(build, [
|
|
40
|
-
'internalExecutableBuildHash', 'lifecycleContractHash', 'packageVersion', 'protocolVersion'
|
|
41
|
-
])
|
|
42
|
-
&& build.protocolVersion === 3
|
|
43
|
-
&& typeof build.packageVersion === 'string' && build.packageVersion.length > 0
|
|
44
|
-
&& typeof build.internalExecutableBuildHash === 'string' && HEX_256.test(build.internalExecutableBuildHash)
|
|
45
|
-
&& typeof build.lifecycleContractHash === 'string' && HEX_256.test(build.lifecycleContractHash);
|
|
46
|
-
}
|
|
47
|
-
function parseRegistration(raw) {
|
|
48
|
-
let value;
|
|
49
|
-
try {
|
|
50
|
-
value = JSON.parse(raw);
|
|
51
|
-
}
|
|
52
|
-
catch {
|
|
53
|
-
fail('CODEX_SANDBOX_CONTROLLER_REGISTRATION_INVALID', 'controller registration is malformed');
|
|
54
|
-
}
|
|
55
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
56
|
-
fail('CODEX_SANDBOX_CONTROLLER_REGISTRATION_INVALID', 'controller registration is invalid');
|
|
57
|
-
}
|
|
58
|
-
const registration = value;
|
|
59
|
-
if (Object.keys(registration).sort().join(',') !== REGISTRATION_KEYS
|
|
60
|
-
|| registration.version !== 1
|
|
61
|
-
|| typeof registration.taskId !== 'string'
|
|
62
|
-
|| typeof registration.controlGeneration !== 'string'
|
|
63
|
-
|| typeof registration.containerId !== 'string'
|
|
64
|
-
|| typeof registration.leaseId !== 'string' || !HEX_256.test(registration.leaseId)
|
|
65
|
-
|| typeof registration.leaseSecretHash !== 'string' || !HEX_256.test(registration.leaseSecretHash)
|
|
66
|
-
|| typeof registration.controllerInstanceDigest !== 'string' || !HEX_256.test(registration.controllerInstanceDigest)
|
|
67
|
-
|| !validProcess(registration.controllerProcess)
|
|
68
|
-
|| !validBuild(registration.buildIdentity)
|
|
69
|
-
|| !Number.isSafeInteger(registration.issuedAt)
|
|
70
|
-
|| !Number.isSafeInteger(registration.expiresAt)
|
|
71
|
-
|| registration.expiresAt <= registration.issuedAt) {
|
|
72
|
-
fail('CODEX_SANDBOX_CONTROLLER_REGISTRATION_INVALID', 'controller registration schema is invalid');
|
|
73
|
-
}
|
|
74
|
-
return registration;
|
|
75
|
-
}
|
|
76
|
-
function registrationPath(manifestPath) {
|
|
77
|
-
return path.join(path.dirname(path.resolve(manifestPath)), 'codex-controller.json');
|
|
78
|
-
}
|
|
79
|
-
function readRaw(file) {
|
|
80
|
-
try {
|
|
81
|
-
const stat = fs.lstatSync(file);
|
|
82
|
-
if (!stat.isFile() || stat.isSymbolicLink()
|
|
83
|
-
|| (process.platform !== 'win32' && (stat.mode & 0o777) !== 0o600)) {
|
|
84
|
-
fail('CODEX_SANDBOX_CONTROLLER_REGISTRATION_INVALID', 'controller registration file is unsafe');
|
|
85
|
-
}
|
|
86
|
-
return { raw: fs.readFileSync(file, 'utf8'), stat };
|
|
87
|
-
}
|
|
88
|
-
catch (error) {
|
|
89
|
-
if (error.code === 'ENOENT')
|
|
90
|
-
return null;
|
|
91
|
-
throw error;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
function sameFile(left, right) {
|
|
95
|
-
return left.dev === right.dev && left.ino === right.ino && left.mode === right.mode
|
|
96
|
-
&& left.size === right.size && left.mtimeMs === right.mtimeMs;
|
|
97
|
-
}
|
|
98
|
-
function secretHash(secret) {
|
|
99
|
-
return crypto.createHash('sha256')
|
|
100
|
-
.update('agent-infra/codex-controller-lease/v1\0')
|
|
101
|
-
.update(secret)
|
|
102
|
-
.digest('hex');
|
|
103
|
-
}
|
|
104
|
-
function safeEqual(left, right) {
|
|
105
|
-
if (!HEX_256.test(left) || !HEX_256.test(right))
|
|
106
|
-
return false;
|
|
107
|
-
return crypto.timingSafeEqual(Buffer.from(left, 'hex'), Buffer.from(right, 'hex'));
|
|
108
|
-
}
|
|
109
|
-
function defaultProbe(manifest, identity) {
|
|
110
|
-
const command = commandForEngine(manifest.engine, 'docker', [
|
|
111
|
-
'exec', manifest.containerIdentity.id, 'cat', `/proc/${identity.pid}/stat`
|
|
112
|
-
]);
|
|
113
|
-
const result = runProbe(command.cmd, command.args, { timeout: 2_000 });
|
|
114
|
-
if (result.status !== 0) {
|
|
115
|
-
const stderr = typeof result.stderr === 'string' ? result.stderr : result.stderr?.toString('utf8') ?? '';
|
|
116
|
-
return /no such (?:file|container)|not found|does not exist/iu.test(stderr) ? 'dead' : 'unknown';
|
|
117
|
-
}
|
|
118
|
-
const stdout = typeof result.stdout === 'string' ? result.stdout : result.stdout?.toString('utf8') ?? '';
|
|
119
|
-
const stat = parseLinuxProcessStat(stdout);
|
|
120
|
-
if (!stat)
|
|
121
|
-
return 'unknown';
|
|
122
|
-
if (stat.state === 'Z' || stat.startTime !== identity.startTime)
|
|
123
|
-
return 'dead';
|
|
124
|
-
return 'alive';
|
|
125
|
-
}
|
|
126
|
-
function assertRegistrationBinding(registration, manifest, buildIdentity) {
|
|
127
|
-
if (registration.taskId !== manifest.taskId
|
|
128
|
-
|| registration.controlGeneration !== manifest.generation
|
|
129
|
-
|| registration.containerId !== manifest.containerIdentity.id) {
|
|
130
|
-
fail('CODEX_SANDBOX_CONTROLLER_REGISTRATION_INVALID', 'controller registration binding is invalid');
|
|
131
|
-
}
|
|
132
|
-
if (registration.buildIdentity.protocolVersion !== buildIdentity.protocolVersion) {
|
|
133
|
-
fail('CODEX_LIFECYCLE_PROTOCOL_MISMATCH', 'Codex lifecycle protocol version does not match');
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
function atomicWrite(file, value) {
|
|
137
|
-
const temporary = path.join(path.dirname(file), `.${path.basename(file)}.${process.pid}.${crypto.randomUUID()}.tmp`);
|
|
138
|
-
try {
|
|
139
|
-
fs.writeFileSync(temporary, `${JSON.stringify(value)}\n`, { mode: 0o600, flag: 'wx' });
|
|
140
|
-
fs.chmodSync(temporary, 0o600);
|
|
141
|
-
fs.renameSync(temporary, file);
|
|
142
|
-
}
|
|
143
|
-
finally {
|
|
144
|
-
fs.rmSync(temporary, { force: true });
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
export function readCodexControllerRegistration(manifestPath) {
|
|
148
|
-
const existing = readRaw(registrationPath(manifestPath));
|
|
149
|
-
if (!existing)
|
|
150
|
-
fail('CODEX_SANDBOX_CONTROLLER_REGISTRATION_MISSING', 'controller registration is missing');
|
|
151
|
-
return parseRegistration(existing.raw);
|
|
152
|
-
}
|
|
153
|
-
export function openCodexControllerRegistration(params, options = {}) {
|
|
154
|
-
if (params.manifest.mode !== 'task-bound' || !params.manifest.taskId) {
|
|
155
|
-
fail('SANDBOX_CONTROL_BRANCH_ONLY', 'branch-only sandboxes cannot register a Codex controller');
|
|
156
|
-
}
|
|
157
|
-
if (!validProcess(params.controllerProcess)) {
|
|
158
|
-
fail('CODEX_SANDBOX_CONTROLLER_PROCESS_INACTIVE', 'requested controller process is invalid');
|
|
159
|
-
}
|
|
160
|
-
const file = registrationPath(params.manifestPath);
|
|
161
|
-
const now = (options.now ?? Date.now)();
|
|
162
|
-
const initial = readRaw(file);
|
|
163
|
-
if (initial) {
|
|
164
|
-
const existing = parseRegistration(initial.raw);
|
|
165
|
-
assertRegistrationBinding(existing, params.manifest, params.buildIdentity);
|
|
166
|
-
if (existing.expiresAt > now) {
|
|
167
|
-
const oldState = (options.probeProcess ?? ((identity) => defaultProbe(params.manifest, identity)))(existing.controllerProcess);
|
|
168
|
-
if (oldState === 'alive')
|
|
169
|
-
fail('CODEX_SANDBOX_CONTROLLER_BUSY', 'an active controller registration already exists');
|
|
170
|
-
if (oldState === 'unknown')
|
|
171
|
-
fail('CODEX_SANDBOX_CONTROLLER_PROCESS_UNKNOWN', 'existing controller process state is unknown');
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
const requestedState = (options.probeProcess ?? ((identity) => defaultProbe(params.manifest, identity)))(params.controllerProcess);
|
|
175
|
-
if (requestedState === 'dead')
|
|
176
|
-
fail('CODEX_SANDBOX_CONTROLLER_PROCESS_INACTIVE', 'requested controller process is not alive');
|
|
177
|
-
if (requestedState === 'unknown')
|
|
178
|
-
fail('CODEX_SANDBOX_CONTROLLER_PROCESS_UNKNOWN', 'requested controller process state is unknown');
|
|
179
|
-
const randomHex = options.randomHex ?? (() => crypto.randomBytes(32).toString('hex'));
|
|
180
|
-
const leaseId = randomHex();
|
|
181
|
-
const leaseSecret = randomHex();
|
|
182
|
-
const controllerInstanceDigest = randomHex();
|
|
183
|
-
if (![leaseId, leaseSecret, controllerInstanceDigest].every((value) => HEX_256.test(value))) {
|
|
184
|
-
fail('CODEX_SANDBOX_CONTROLLER_CREDENTIAL_INVALID', 'generated controller credential is invalid');
|
|
185
|
-
}
|
|
186
|
-
const registration = Object.freeze({
|
|
187
|
-
version: 1,
|
|
188
|
-
taskId: params.manifest.taskId,
|
|
189
|
-
controlGeneration: params.manifest.generation,
|
|
190
|
-
containerId: params.manifest.containerIdentity.id,
|
|
191
|
-
leaseId,
|
|
192
|
-
leaseSecretHash: secretHash(leaseSecret),
|
|
193
|
-
controllerInstanceDigest,
|
|
194
|
-
controllerProcess: params.controllerProcess,
|
|
195
|
-
buildIdentity: params.buildIdentity,
|
|
196
|
-
issuedAt: now,
|
|
197
|
-
expiresAt: now + CONTROLLER_TTL_MS
|
|
198
|
-
});
|
|
199
|
-
options.beforeCommit?.();
|
|
200
|
-
const current = readRaw(file);
|
|
201
|
-
if ((!initial && current)
|
|
202
|
-
|| (initial && (!current || current.raw !== initial.raw || !sameFile(current.stat, initial.stat)))) {
|
|
203
|
-
fail('CODEX_SANDBOX_CONTROLLER_OWNERSHIP_LOST', 'controller registration changed before commit');
|
|
204
|
-
}
|
|
205
|
-
atomicWrite(file, registration);
|
|
206
|
-
return Object.freeze({
|
|
207
|
-
version: 1,
|
|
208
|
-
status: 'opened',
|
|
209
|
-
changed: true,
|
|
210
|
-
lease: Object.freeze({
|
|
211
|
-
version: 1,
|
|
212
|
-
leaseId,
|
|
213
|
-
leaseSecret,
|
|
214
|
-
taskId: registration.taskId,
|
|
215
|
-
controlGeneration: registration.controlGeneration,
|
|
216
|
-
controllerInstanceDigest,
|
|
217
|
-
controllerProcess: registration.controllerProcess,
|
|
218
|
-
buildIdentity: registration.buildIdentity,
|
|
219
|
-
issuedAt: registration.issuedAt,
|
|
220
|
-
expiresAt: registration.expiresAt
|
|
221
|
-
}),
|
|
222
|
-
error: null
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
export function closeCodexControllerRegistration(params) {
|
|
226
|
-
if (params.manifest.mode !== 'task-bound' || !params.manifest.taskId) {
|
|
227
|
-
fail('SANDBOX_CONTROL_BRANCH_ONLY', 'branch-only sandboxes cannot close a Codex controller registration');
|
|
228
|
-
}
|
|
229
|
-
const file = registrationPath(params.manifestPath);
|
|
230
|
-
const existingRaw = readRaw(file);
|
|
231
|
-
if (!existingRaw)
|
|
232
|
-
return Object.freeze({ version: 1, status: 'closed', changed: false, lease: null, error: null });
|
|
233
|
-
const existing = parseRegistration(existingRaw.raw);
|
|
234
|
-
if (existing.taskId !== params.manifest.taskId
|
|
235
|
-
|| existing.controlGeneration !== params.manifest.generation
|
|
236
|
-
|| existing.containerId !== params.manifest.containerIdentity.id
|
|
237
|
-
|| params.proof.version !== 1
|
|
238
|
-
|| existing.leaseId !== params.proof.leaseId
|
|
239
|
-
|| !safeEqual(existing.leaseSecretHash, secretHash(params.proof.leaseSecret))
|
|
240
|
-
|| JSON.stringify(existing.controllerProcess) !== JSON.stringify(params.proof.controllerProcess)) {
|
|
241
|
-
fail('CODEX_SANDBOX_CONTROLLER_PROOF_INVALID', 'controller close proof is invalid');
|
|
242
|
-
}
|
|
243
|
-
const current = readRaw(file);
|
|
244
|
-
if (!current || current.raw !== existingRaw.raw || !sameFile(current.stat, existingRaw.stat)) {
|
|
245
|
-
fail('CODEX_SANDBOX_CONTROLLER_OWNERSHIP_LOST', 'controller registration changed before close');
|
|
246
|
-
}
|
|
247
|
-
fs.unlinkSync(file);
|
|
248
|
-
return Object.freeze({ version: 1, status: 'closed', changed: true, lease: null, error: null });
|
|
249
|
-
}
|
|
250
|
-
export function resolveCodexControllerBinding(params) {
|
|
251
|
-
if (params.manifest.mode !== 'task-bound' || !params.manifest.taskId) {
|
|
252
|
-
fail('SANDBOX_CONTROL_BRANCH_ONLY', 'branch-only sandboxes cannot resolve a Codex controller registration');
|
|
253
|
-
}
|
|
254
|
-
const registration = readCodexControllerRegistration(params.manifestPath);
|
|
255
|
-
assertRegistrationBinding(registration, params.manifest, params.buildIdentity);
|
|
256
|
-
if (registration.expiresAt <= (params.now ?? Date.now())) {
|
|
257
|
-
fail('CODEX_SANDBOX_CONTROLLER_LEASE_EXPIRED', 'controller lease is expired');
|
|
258
|
-
}
|
|
259
|
-
if (params.proof.version !== 1
|
|
260
|
-
|| registration.leaseId !== params.proof.leaseId
|
|
261
|
-
|| !safeEqual(registration.leaseSecretHash, secretHash(params.proof.leaseSecret))
|
|
262
|
-
|| JSON.stringify(registration.controllerProcess) !== JSON.stringify(params.proof.controllerProcess)) {
|
|
263
|
-
fail('CODEX_SANDBOX_CONTROLLER_PROOF_INVALID', 'controller proof is invalid');
|
|
264
|
-
}
|
|
265
|
-
const state = (params.probeProcess ?? ((identity) => defaultProbe(params.manifest, identity)))(registration.controllerProcess);
|
|
266
|
-
if (state === 'dead')
|
|
267
|
-
fail('CODEX_SANDBOX_CONTROLLER_PROCESS_INACTIVE', 'controller process is not alive');
|
|
268
|
-
if (state === 'unknown')
|
|
269
|
-
fail('CODEX_SANDBOX_CONTROLLER_PROCESS_UNKNOWN', 'controller process state is unknown');
|
|
270
|
-
return Object.freeze({
|
|
271
|
-
instanceDigest: registration.controllerInstanceDigest,
|
|
272
|
-
controlGeneration: registration.controlGeneration
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
//# sourceMappingURL=controller-registration.js.map
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { runCodexSandboxController, verifyCodexSandboxControllerContextWithWarnings } from './sandbox-controller.js';
|
|
2
|
-
import { ensureInternalHandlerRoute, internalHandlerRoute } from '../../../internal/cli-route-inventory.js';
|
|
3
|
-
const USAGE = 'Usage: agent-infra-internal codex-sandbox-controller <run|verify-context> [options]\n';
|
|
4
|
-
function fail(code, message, exitCode = 1) {
|
|
5
|
-
process.stdout.write(`${JSON.stringify({
|
|
6
|
-
status: 'failed',
|
|
7
|
-
changed: false,
|
|
8
|
-
error: { code, message }
|
|
9
|
-
})}\n`);
|
|
10
|
-
process.exitCode = exitCode;
|
|
11
|
-
}
|
|
12
|
-
function parse(args) {
|
|
13
|
-
if (args[0] === '--help' || args[0] === '-h') {
|
|
14
|
-
process.stdout.write(USAGE);
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
const operation = args[0];
|
|
18
|
-
if (!operation || ![
|
|
19
|
-
internalHandlerRoute('codex-sandbox-controller', 'run', operation),
|
|
20
|
-
internalHandlerRoute('codex-sandbox-controller', 'verify-context', operation)
|
|
21
|
-
].some(Boolean)) {
|
|
22
|
-
process.stderr.write(USAGE);
|
|
23
|
-
fail('CODEX_SANDBOX_CONTROLLER_PAYLOAD_INVALID', 'operation must be run or verify-context', 2);
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
const allowed = new Set([
|
|
27
|
-
'--context',
|
|
28
|
-
'--executor-model',
|
|
29
|
-
'--executor-reasoning-effort',
|
|
30
|
-
'--reviewer-model',
|
|
31
|
-
'--reviewer-reasoning-effort'
|
|
32
|
-
]);
|
|
33
|
-
const values = {};
|
|
34
|
-
for (let index = 1; index < args.length; index += 1) {
|
|
35
|
-
const flag = args[index];
|
|
36
|
-
if (flag === '--task-id' || flag.startsWith('--task-id=')
|
|
37
|
-
|| flag === '--task-ref' || flag.startsWith('--task-ref=')) {
|
|
38
|
-
fail('CODEX_SANDBOX_CONTROLLER_IDENTITY_OPTION_UNSUPPORTED', 'controller task identity options are not supported', 2);
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
const value = args[++index];
|
|
42
|
-
if (!allowed.has(flag) || Object.hasOwn(values, flag) || !value || value.startsWith('--')) {
|
|
43
|
-
fail('CODEX_SANDBOX_CONTROLLER_PAYLOAD_INVALID', `invalid option '${flag}'`, 2);
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
values[flag] = value;
|
|
47
|
-
}
|
|
48
|
-
return { operation, values };
|
|
49
|
-
}
|
|
50
|
-
async function codexSandboxController(args = []) {
|
|
51
|
-
if (!ensureInternalHandlerRoute('codex-sandbox-controller', args))
|
|
52
|
-
return;
|
|
53
|
-
const parsed = parse(args);
|
|
54
|
-
if (!parsed || process.exitCode)
|
|
55
|
-
return;
|
|
56
|
-
try {
|
|
57
|
-
if (internalHandlerRoute('codex-sandbox-controller', 'verify-context', parsed.operation)) {
|
|
58
|
-
const contextPath = parsed.values['--context']
|
|
59
|
-
?? process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
|
|
60
|
-
if (!contextPath)
|
|
61
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_MISSING');
|
|
62
|
-
const verified = verifyCodexSandboxControllerContextWithWarnings(contextPath);
|
|
63
|
-
const context = verified.context;
|
|
64
|
-
process.stdout.write(`${JSON.stringify({
|
|
65
|
-
status: 'ready',
|
|
66
|
-
changed: false,
|
|
67
|
-
context: {
|
|
68
|
-
taskId: context.taskId,
|
|
69
|
-
controlGeneration: context.controlGeneration,
|
|
70
|
-
controllerInstanceDigest: context.controllerInstanceDigest,
|
|
71
|
-
expiresAt: context.expiresAt,
|
|
72
|
-
buildIdentity: context.buildIdentity,
|
|
73
|
-
hookDefinitionHash: context.hookDefinitionHash,
|
|
74
|
-
},
|
|
75
|
-
...(verified.warnings.length > 0 ? { warnings: verified.warnings } : {}),
|
|
76
|
-
error: null
|
|
77
|
-
})}\n`);
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
const policy = [
|
|
81
|
-
'--executor-model',
|
|
82
|
-
'--executor-reasoning-effort',
|
|
83
|
-
'--reviewer-model',
|
|
84
|
-
'--reviewer-reasoning-effort'
|
|
85
|
-
];
|
|
86
|
-
if (policy.some((flag) => parsed.values[flag] !== undefined)
|
|
87
|
-
&& policy.some((flag) => parsed.values[flag] === undefined)) {
|
|
88
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_MODEL_POLICY_INVALID');
|
|
89
|
-
}
|
|
90
|
-
const exitCode = await runCodexSandboxController({
|
|
91
|
-
executorModel: parsed.values['--executor-model'],
|
|
92
|
-
executorReasoningEffort: parsed.values['--executor-reasoning-effort'],
|
|
93
|
-
reviewerModel: parsed.values['--reviewer-model'],
|
|
94
|
-
reviewerReasoningEffort: parsed.values['--reviewer-reasoning-effort']
|
|
95
|
-
});
|
|
96
|
-
process.exitCode = exitCode;
|
|
97
|
-
}
|
|
98
|
-
catch (error) {
|
|
99
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
100
|
-
const code = /^([A-Z][A-Z0-9_]+)/u.exec(message)?.[1]
|
|
101
|
-
?? 'CODEX_SANDBOX_CONTROLLER_FAILED';
|
|
102
|
-
fail(code, message);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
export { codexSandboxController };
|
|
106
|
-
//# sourceMappingURL=sandbox-controller-cli.js.map
|