@fitlab-ai/agent-infra 0.9.4 → 0.9.6
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 +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/bin/internal-cli.js +13 -3
- package/dist/lib/agent-clients/adapter.js +2 -2
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +492 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +175 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +255 -0
- package/dist/lib/agent-clients/adapters/codex.js +3 -3
- package/dist/lib/git/workflow.js +0 -1
- package/dist/lib/git/worktree-identity.js +64 -0
- package/dist/lib/internal/codex-lifecycle.js +300 -0
- package/dist/lib/internal/release-workflow.js +108 -11
- package/dist/lib/internal/sandbox-control.js +43 -6
- package/dist/lib/internal/task-create.js +56 -0
- package/dist/lib/internal/task-orchestration.js +71 -31
- package/dist/lib/platform/issue-comments.js +2 -1
- package/dist/lib/release/workflow.js +29 -21
- package/dist/lib/sandbox/commands/create.js +16 -16
- package/dist/lib/sandbox/commands/prune.js +40 -16
- package/dist/lib/sandbox/commands/rm.js +318 -89
- package/dist/lib/sandbox/control/client.js +152 -22
- package/dist/lib/sandbox/control/executor.js +136 -0
- package/dist/lib/sandbox/control/lifecycle.js +348 -0
- package/dist/lib/sandbox/control/protocol.js +73 -18
- package/dist/lib/sandbox/control/server.js +235 -80
- package/dist/lib/sandbox/control/state.js +181 -0
- package/dist/lib/sandbox/index.js +2 -2
- package/dist/lib/sandbox/managed-fs.js +14 -2
- package/dist/lib/sandbox/mounts.js +35 -24
- package/dist/lib/sandbox/recovery.js +262 -45
- package/dist/lib/sandbox/workspace-view.js +80 -11
- package/dist/lib/sandbox/worktree-safety.js +196 -0
- package/dist/lib/server/process-control.js +6 -0
- package/dist/lib/task/activity-log.js +32 -5
- package/dist/lib/task/artifact-lifecycle.js +5 -4
- package/dist/lib/task/codex-orchestration.js +161 -0
- package/dist/lib/task/commands/status.js +3 -0
- package/dist/lib/task/commands/validate.js +289 -0
- package/dist/lib/task/commit-finalization.js +50 -12
- package/dist/lib/task/create-service.js +137 -0
- package/dist/lib/task/create.js +333 -0
- package/dist/lib/task/delegation-receipts.js +25 -0
- package/dist/lib/task/events.js +10 -4
- package/dist/lib/task/finalization-artifacts.js +2 -1
- package/dist/lib/task/implementation-inputs.js +1 -7
- package/dist/lib/task/index.js +9 -0
- package/dist/lib/task/orchestration.js +283 -18
- package/dist/lib/task/review-fingerprint.js +4 -0
- package/dist/lib/task/verification-engine.js +38 -6
- package/dist/lib/task/verification.js +1 -0
- package/dist/lib/task/workspace-snapshot.js +78 -3
- package/lib/agent-clients/adapter.ts +4 -4
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +569 -0
- package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +323 -0
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +298 -0
- package/lib/agent-clients/adapters/codex.ts +3 -3
- package/lib/git/workflow.ts +0 -1
- package/lib/git/worktree-identity.ts +86 -0
- package/lib/internal/codex-lifecycle.ts +308 -0
- package/lib/internal/release-workflow.ts +107 -11
- package/lib/internal/sandbox-control.ts +38 -6
- package/lib/internal/task-create.ts +61 -0
- package/lib/internal/task-orchestration.ts +57 -31
- package/lib/platform/issue-comments.ts +2 -1
- package/lib/release/workflow.ts +69 -20
- package/lib/sandbox/commands/create.ts +18 -21
- package/lib/sandbox/commands/prune.ts +53 -15
- package/lib/sandbox/commands/rm.ts +368 -103
- package/lib/sandbox/control/client.ts +173 -25
- package/lib/sandbox/control/executor.ts +163 -0
- package/lib/sandbox/control/lifecycle.ts +342 -0
- package/lib/sandbox/control/protocol.ts +110 -48
- package/lib/sandbox/control/server.ts +252 -85
- package/lib/sandbox/control/state.ts +196 -0
- package/lib/sandbox/index.ts +2 -2
- package/lib/sandbox/managed-fs.ts +17 -2
- package/lib/sandbox/mounts.ts +31 -13
- package/lib/sandbox/recovery.ts +301 -60
- package/lib/sandbox/workspace-view.ts +89 -12
- package/lib/sandbox/worktree-safety.ts +234 -0
- package/lib/server/process-control.ts +11 -0
- package/lib/task/activity-log.ts +46 -7
- package/lib/task/artifact-lifecycle.ts +5 -4
- package/lib/task/codex-orchestration.ts +233 -0
- package/lib/task/commands/status.ts +2 -0
- package/lib/task/commands/validate.ts +282 -0
- package/lib/task/commit-finalization.ts +75 -12
- package/lib/task/create-service.ts +171 -0
- package/lib/task/create.ts +372 -0
- package/lib/task/delegation-receipts.ts +52 -3
- package/lib/task/events.ts +9 -4
- package/lib/task/finalization-artifacts.ts +2 -1
- package/lib/task/implementation-inputs.ts +1 -5
- package/lib/task/index.ts +8 -0
- package/lib/task/orchestration.ts +381 -22
- package/lib/task/review-fingerprint.ts +9 -0
- package/lib/task/verification-engine.ts +42 -8
- package/lib/task/verification.ts +2 -1
- package/lib/task/workspace-snapshot.ts +98 -4
- package/package.json +7 -7
- package/templates/.agents/hooks/lifecycle-delegation.js +88 -8
- package/templates/.agents/rules/lifecycle-orchestration.en.md +1 -1
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +1 -1
- package/templates/.agents/rules/no-mid-flow-questions.en.md +1 -0
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +1 -0
- package/templates/.agents/rules/task-management.en.md +1 -0
- package/templates/.agents/rules/task-management.zh-CN.md +1 -0
- package/templates/.agents/skills/commit/SKILL.en.md +3 -9
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +6 -10
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +4 -3
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +5 -4
- package/templates/.agents/skills/create-task/SKILL.en.md +26 -43
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +26 -43
- package/templates/.agents/skills/post-release/SKILL.en.md +32 -6
- package/templates/.agents/skills/post-release/SKILL.zh-CN.md +32 -6
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +42 -0
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +42 -0
- package/templates/.agents/skills/run-manual-validation/config/verify.en.json +14 -0
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +14 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +44 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +44 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +3 -3
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +3 -3
- package/templates/.claude/commands/run-manual-validation.en.md +9 -0
- package/templates/.claude/commands/run-manual-validation.zh-CN.md +9 -0
- package/templates/.codex/hooks.json +50 -0
- package/templates/.opencode/commands/run-manual-validation.en.md +11 -0
- package/templates/.opencode/commands/run-manual-validation.zh-CN.md +11 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import crypto from 'node:crypto';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { preflightCodexLifecycleEvidence, resolveCodexTerminal, resolveCodexThread } from '../agent-clients/adapters/codex-lifecycle/app-server.js';
|
|
6
|
+
import { createCodexLifecycleStore } from '../agent-clients/adapters/codex-lifecycle/store.js';
|
|
7
|
+
import { activateCodexOrchestrationDelegation, activateCodexSpawnDelegation, reconcileCodexOrchestrationDelegation, sealCodexOrchestrationDelegation, sealCodexParentDelegation } from '../task/codex-orchestration.js';
|
|
8
|
+
const USAGE = 'Usage: agent-infra-internal codex-lifecycle <hook-event|resolve-start|resolve-stop|preflight|consume> [options]\n';
|
|
9
|
+
const MANAGED_AGENT = /^agent-infra-lifecycle-(executor|reviewer)$/;
|
|
10
|
+
function output(value) {
|
|
11
|
+
process.stdout.write(`${JSON.stringify(value)}\n`);
|
|
12
|
+
}
|
|
13
|
+
function failure(code, message) {
|
|
14
|
+
output({ status: 'failed', changed: false, evidence: null, diagnostics: [], error: { code, message } });
|
|
15
|
+
process.exitCode = 1;
|
|
16
|
+
}
|
|
17
|
+
function outputBridgeResult(result) {
|
|
18
|
+
if (result.error?.code === 'ORCHESTRATION_DELEGATION_MISSING') {
|
|
19
|
+
output({ status: 'ignored', changed: false, evidence: null, diagnostics: [], error: null });
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
output(result);
|
|
23
|
+
if (result.status !== 'running')
|
|
24
|
+
process.exitCode = 1;
|
|
25
|
+
}
|
|
26
|
+
function parse(args) {
|
|
27
|
+
if (args[0] === '--help' || args[0] === '-h') {
|
|
28
|
+
process.stdout.write(USAGE);
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const operation = args[0];
|
|
32
|
+
if (!operation) {
|
|
33
|
+
failure('CODEX_LIFECYCLE_PAYLOAD_INVALID', 'operation is required');
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const allowed = {
|
|
37
|
+
'hook-event': ['--event', '--bridge'],
|
|
38
|
+
'resolve-start': ['--child-id'],
|
|
39
|
+
'resolve-stop': ['--child-id'],
|
|
40
|
+
preflight: ['--format', '--session-id', '--turn-id', '--tool-use-id'],
|
|
41
|
+
consume: ['--child-id', '--consumer']
|
|
42
|
+
}[operation];
|
|
43
|
+
if (!allowed) {
|
|
44
|
+
failure('CODEX_LIFECYCLE_PAYLOAD_INVALID', 'unknown operation');
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const values = {};
|
|
48
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
49
|
+
const flag = args[index];
|
|
50
|
+
if (!allowed.includes(flag) || Object.hasOwn(values, flag)) {
|
|
51
|
+
failure('CODEX_LIFECYCLE_PAYLOAD_INVALID', `invalid or duplicate option '${flag}'`);
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const value = args[++index];
|
|
55
|
+
if (!value || value.startsWith('--')) {
|
|
56
|
+
failure('CODEX_LIFECYCLE_PAYLOAD_INVALID', `option '${flag}' requires a value`);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
values[flag] = value;
|
|
60
|
+
}
|
|
61
|
+
return Object.freeze({ operation, values: Object.freeze(values) });
|
|
62
|
+
}
|
|
63
|
+
function cliVersion() {
|
|
64
|
+
const result = spawnSync('codex', ['--version'], { encoding: 'utf8' });
|
|
65
|
+
const version = /codex-cli\s+(\d+\.\d+\.\d+)/.exec(result.stdout ?? '')?.[1];
|
|
66
|
+
if (result.status !== 0 || !version)
|
|
67
|
+
throw new Error('Codex CLI version is unavailable');
|
|
68
|
+
return version;
|
|
69
|
+
}
|
|
70
|
+
function hookDefinitionHash() {
|
|
71
|
+
const file = path.join(process.cwd(), '.codex', 'hooks.json');
|
|
72
|
+
return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
73
|
+
}
|
|
74
|
+
async function readStdin() {
|
|
75
|
+
let input = '';
|
|
76
|
+
process.stdin.setEncoding('utf8');
|
|
77
|
+
for await (const chunk of process.stdin) {
|
|
78
|
+
input += chunk;
|
|
79
|
+
if (Buffer.byteLength(input) > 64 * 1024)
|
|
80
|
+
throw new Error('Codex lifecycle input exceeds 64 KiB');
|
|
81
|
+
}
|
|
82
|
+
return JSON.parse(input || '{}');
|
|
83
|
+
}
|
|
84
|
+
function recordFromPayload(phase, payload) {
|
|
85
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
86
|
+
throw new Error('Codex lifecycle hook payload must be an object');
|
|
87
|
+
}
|
|
88
|
+
const value = payload;
|
|
89
|
+
const text = (key) => typeof value[key] === 'string' ? value[key] : '';
|
|
90
|
+
if (!MANAGED_AGENT.test(text('nativeAgent')))
|
|
91
|
+
return null;
|
|
92
|
+
if (phase === 'pre-tool')
|
|
93
|
+
return {
|
|
94
|
+
type: 'hook-spawn',
|
|
95
|
+
sessionId: text('sessionId'),
|
|
96
|
+
turnId: text('turnId'),
|
|
97
|
+
toolUseId: text('toolUseId'),
|
|
98
|
+
nativeAgent: text('nativeAgent'),
|
|
99
|
+
...(text('requestedModel') ? { requestedModel: text('requestedModel') } : {}),
|
|
100
|
+
...(text('requestedReasoningEffort') ? { requestedReasoningEffort: text('requestedReasoningEffort') } : {}),
|
|
101
|
+
hookDefinitionHash: text('hookDefinitionHash')
|
|
102
|
+
};
|
|
103
|
+
if (phase === 'subagent-start')
|
|
104
|
+
return {
|
|
105
|
+
type: 'hook-child',
|
|
106
|
+
sessionId: text('sessionId'),
|
|
107
|
+
turnId: text('turnId'),
|
|
108
|
+
childThreadId: text('childThreadId'),
|
|
109
|
+
nativeAgent: text('nativeAgent')
|
|
110
|
+
};
|
|
111
|
+
if (phase === 'subagent-stop')
|
|
112
|
+
return {
|
|
113
|
+
type: 'hook-stop',
|
|
114
|
+
sessionId: text('sessionId'),
|
|
115
|
+
turnId: text('turnId'),
|
|
116
|
+
childThreadId: text('childThreadId'),
|
|
117
|
+
nativeAgent: text('nativeAgent')
|
|
118
|
+
};
|
|
119
|
+
if (phase === 'post-tool')
|
|
120
|
+
return null;
|
|
121
|
+
throw new Error(`unknown Codex lifecycle hook phase '${phase}'`);
|
|
122
|
+
}
|
|
123
|
+
function payloadText(payload, key) {
|
|
124
|
+
return payload && typeof payload === 'object' && !Array.isArray(payload)
|
|
125
|
+
&& typeof payload[key] === 'string'
|
|
126
|
+
? payload[key]
|
|
127
|
+
: '';
|
|
128
|
+
}
|
|
129
|
+
async function codexLifecycle(args = []) {
|
|
130
|
+
const parsed = parse(args);
|
|
131
|
+
if (!parsed || process.exitCode)
|
|
132
|
+
return;
|
|
133
|
+
try {
|
|
134
|
+
if (parsed.operation === 'preflight') {
|
|
135
|
+
const format = parsed.values['--format'] ?? 'json';
|
|
136
|
+
if (!['text', 'json'].includes(format))
|
|
137
|
+
throw new Error("format must be 'text' or 'json'");
|
|
138
|
+
const runtimeFields = [
|
|
139
|
+
parsed.values['--session-id'],
|
|
140
|
+
parsed.values['--turn-id'],
|
|
141
|
+
parsed.values['--tool-use-id']
|
|
142
|
+
];
|
|
143
|
+
if (runtimeFields.some(Boolean) && !runtimeFields.every(Boolean)) {
|
|
144
|
+
throw new Error('preflight runtime identity requires --session-id, --turn-id, and --tool-use-id together');
|
|
145
|
+
}
|
|
146
|
+
const runtimeIdentity = runtimeFields.every(Boolean) ? {
|
|
147
|
+
sessionId: runtimeFields[0],
|
|
148
|
+
turnId: runtimeFields[1],
|
|
149
|
+
toolUseId: runtimeFields[2]
|
|
150
|
+
} : undefined;
|
|
151
|
+
const result = await preflightCodexLifecycleEvidence(process.cwd(), runtimeIdentity);
|
|
152
|
+
if (format === 'text') {
|
|
153
|
+
process.stdout.write(`Codex lifecycle static preflight: ready\nCLI: ${result.cliVersion}\nRuntime hook liveness: ${result.runtimeLiveness ? 'observed' : 'not-yet-observed'}\n`);
|
|
154
|
+
}
|
|
155
|
+
else
|
|
156
|
+
output({ status: 'ready', changed: false, evidence: result, diagnostics: result.diagnostics, error: null });
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const store = createCodexLifecycleStore({ cliVersion: cliVersion() });
|
|
160
|
+
if (parsed.operation === 'hook-event') {
|
|
161
|
+
const phase = parsed.values['--event'];
|
|
162
|
+
if (!phase || !['pre-tool', 'subagent-start', 'post-tool', 'subagent-stop'].includes(phase)) {
|
|
163
|
+
throw new Error('hook-event requires a known --event');
|
|
164
|
+
}
|
|
165
|
+
if (parsed.values['--bridge'] !== undefined && parsed.values['--bridge'] !== 'true') {
|
|
166
|
+
throw new Error("hook-event --bridge must be 'true'");
|
|
167
|
+
}
|
|
168
|
+
const payload = await readStdin();
|
|
169
|
+
if (phase === 'post-tool' && parsed.values['--bridge'] === 'true') {
|
|
170
|
+
const toolName = payloadText(payload, 'toolName');
|
|
171
|
+
if (toolName === 'collaborationspawn_agent') {
|
|
172
|
+
const nativeAgent = payloadText(payload, 'nativeAgent');
|
|
173
|
+
if (!MANAGED_AGENT.test(nativeAgent)) {
|
|
174
|
+
output({ status: 'ignored', changed: false, evidence: null, diagnostics: [], error: null });
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const requestedModel = payloadText(payload, 'requestedModel');
|
|
178
|
+
const requestedReasoningEffort = payloadText(payload, 'requestedReasoningEffort');
|
|
179
|
+
const bridged = await activateCodexSpawnDelegation({
|
|
180
|
+
sessionId: payloadText(payload, 'sessionId'),
|
|
181
|
+
turnId: payloadText(payload, 'turnId'),
|
|
182
|
+
toolUseId: payloadText(payload, 'toolUseId'),
|
|
183
|
+
transcriptPath: payloadText(payload, 'transcriptPath'),
|
|
184
|
+
nativeAgent,
|
|
185
|
+
taskName: payloadText(payload, 'taskName'),
|
|
186
|
+
...(requestedModel ? { requestedModel } : {}),
|
|
187
|
+
...(requestedReasoningEffort ? { requestedReasoningEffort } : {})
|
|
188
|
+
}, { store });
|
|
189
|
+
outputBridgeResult(bridged);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (toolName === 'collaborationwait_agent') {
|
|
193
|
+
outputBridgeResult(await sealCodexParentDelegation(payloadText(payload, 'sessionId'), { store }));
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
output({ status: 'ignored', changed: false, evidence: null, diagnostics: [], error: null });
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const event = recordFromPayload(phase, payload);
|
|
200
|
+
if (!event) {
|
|
201
|
+
if (phase === 'post-tool' && parsed.values['--bridge'] === 'true') {
|
|
202
|
+
const value = payload;
|
|
203
|
+
const childThreadId = String(value.childThreadId ?? '');
|
|
204
|
+
const reconciled = reconcileCodexOrchestrationDelegation(childThreadId);
|
|
205
|
+
outputBridgeResult(reconciled);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
output({ status: 'ignored', changed: false, evidence: null, diagnostics: [], error: null });
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (parsed.values['--bridge'] === 'true' && event.type === 'hook-stop') {
|
|
212
|
+
try {
|
|
213
|
+
if (store.read(event.childThreadId).consumer) {
|
|
214
|
+
const bridged = await sealCodexOrchestrationDelegation(event.childThreadId, { store });
|
|
215
|
+
outputBridgeResult(bridged);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
// The normal apply path below owns missing or invalid evidence errors.
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (parsed.values['--bridge'] === 'true' && event.type === 'hook-child') {
|
|
224
|
+
const resolved = await resolveCodexThread(event.childThreadId);
|
|
225
|
+
store.apply({
|
|
226
|
+
...event,
|
|
227
|
+
parentThreadId: resolved.resolution.thread.parentThreadId
|
|
228
|
+
});
|
|
229
|
+
const bridged = await activateCodexOrchestrationDelegation(event.childThreadId, {
|
|
230
|
+
store,
|
|
231
|
+
resolveThread: async () => resolved
|
|
232
|
+
});
|
|
233
|
+
outputBridgeResult(bridged);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (event.type === 'hook-child') {
|
|
237
|
+
const resolved = await resolveCodexThread(event.childThreadId);
|
|
238
|
+
const result = store.apply({
|
|
239
|
+
...event,
|
|
240
|
+
parentThreadId: resolved.resolution.thread.parentThreadId
|
|
241
|
+
});
|
|
242
|
+
output({ status: result.state.status, changed: true, evidence: result.state, diagnostics: resolved.diagnostics, error: result.state.error });
|
|
243
|
+
if (result.state.status === 'invalid')
|
|
244
|
+
process.exitCode = 1;
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const result = store.apply(event);
|
|
248
|
+
if (parsed.values['--bridge'] === 'true' && event.type === 'hook-stop') {
|
|
249
|
+
const bridged = await sealCodexOrchestrationDelegation(event.childThreadId, { store });
|
|
250
|
+
outputBridgeResult(bridged);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
output({ status: result.state.status, changed: true, evidence: result.state, diagnostics: [], error: result.state.error });
|
|
254
|
+
if (result.state.status === 'invalid')
|
|
255
|
+
process.exitCode = 1;
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
const childThreadId = parsed.values['--child-id'];
|
|
259
|
+
if (!childThreadId)
|
|
260
|
+
throw new Error(`operation '${parsed.operation}' requires --child-id`);
|
|
261
|
+
if (parsed.operation === 'resolve-start') {
|
|
262
|
+
if (store.read(childThreadId).state.spawn?.hookDefinitionHash !== hookDefinitionHash()) {
|
|
263
|
+
throw new Error('Codex lifecycle hook definition hash is stale');
|
|
264
|
+
}
|
|
265
|
+
const resolved = await resolveCodexThread(childThreadId);
|
|
266
|
+
let latest = store.apply(resolved.resolution.thread);
|
|
267
|
+
for (const reroute of resolved.reroutes)
|
|
268
|
+
latest = store.apply(reroute);
|
|
269
|
+
latest = store.apply(resolved.resolution.settings);
|
|
270
|
+
output({ status: latest.state.status, changed: true, evidence: latest.state.startEvidence, diagnostics: resolved.diagnostics, error: latest.state.error });
|
|
271
|
+
if (latest.state.status !== 'start-ready')
|
|
272
|
+
process.exitCode = 1;
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
if (parsed.operation === 'resolve-stop') {
|
|
276
|
+
const terminal = await resolveCodexTerminal(childThreadId);
|
|
277
|
+
const latest = store.apply(terminal);
|
|
278
|
+
if (latest.state.status !== 'stop-ready') {
|
|
279
|
+
throw new Error(`Codex lifecycle stop evidence is not ready (status=${latest.state.status})`);
|
|
280
|
+
}
|
|
281
|
+
output({ status: latest.state.status, changed: true, evidence: latest.state.stopEvidence, diagnostics: [], error: latest.state.error });
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
if (parsed.operation === 'consume') {
|
|
285
|
+
const consumer = parsed.values['--consumer'];
|
|
286
|
+
if (!consumer)
|
|
287
|
+
throw new Error('consume requires --consumer');
|
|
288
|
+
const consumed = store.consume(childThreadId, consumer, hookDefinitionHash());
|
|
289
|
+
output({ status: 'consumed', changed: true, evidence: {
|
|
290
|
+
start: consumed.state.startEvidence,
|
|
291
|
+
stop: consumed.state.stopEvidence
|
|
292
|
+
}, diagnostics: [], error: null });
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
failure('CODEX_LIFECYCLE_FAILED', error instanceof Error ? error.message : String(error));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
export { codexLifecycle };
|
|
300
|
+
//# sourceMappingURL=codex-lifecycle.js.map
|
|
@@ -154,11 +154,47 @@ function inspectLocalReleaseFacts(cwd, version, run = command) {
|
|
|
154
154
|
const localTagConflict = Boolean(tagCommit && !localTag && !localTagAncestor);
|
|
155
155
|
const postSubject = `chore: prepare next dev iteration after v${version}`;
|
|
156
156
|
const postLog = localTag || localTagAncestor
|
|
157
|
-
? git(cwd, ['log', '--format=%s', `${tag}..HEAD`], run) ?? ''
|
|
157
|
+
? git(cwd, ['log', '--format=%H%x00%s', `${tag}..HEAD`], run) ?? ''
|
|
158
158
|
: '';
|
|
159
|
-
const postCommit = postLog.split('\n').
|
|
159
|
+
const postCommit = postLog.split('\n').map((line) => line.split('\0'))
|
|
160
|
+
.find(([, subject]) => subject === postSubject)?.[0] ?? null;
|
|
160
161
|
return { localTag, localTagAncestor, localTagConflict, postCommit };
|
|
161
162
|
}
|
|
163
|
+
function inspectPostReleaseFacts(cwd, commit, run = command) {
|
|
164
|
+
const inspected = inspectGitWorkflow(cwd);
|
|
165
|
+
const snapshot = inspected.snapshot;
|
|
166
|
+
const branch = snapshot?.branch ?? '';
|
|
167
|
+
const remoteLine = branch ? git(cwd, ['ls-remote', '--heads', 'origin', `refs/heads/${branch}`], run) : null;
|
|
168
|
+
const remoteHead = remoteLine?.split(/\s+/)[0] ?? null;
|
|
169
|
+
const packageJson = commit ? git(cwd, ['show', `${commit}:package.json`], run, false) : null;
|
|
170
|
+
let newVersion = null;
|
|
171
|
+
if (packageJson) {
|
|
172
|
+
try {
|
|
173
|
+
const version = JSON.parse(packageJson).version;
|
|
174
|
+
newVersion = typeof version === 'string' ? version : null;
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
newVersion = null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const paths = commit ? git(cwd, ['diff-tree', '--root', '--no-commit-id', '--name-only', '-r', '-z', commit], run, false) : null;
|
|
181
|
+
const changedPaths = paths ? paths.split('\0').filter(Boolean)
|
|
182
|
+
.sort((left, right) => Buffer.from(left).compare(Buffer.from(right))) : [];
|
|
183
|
+
const digest = commit ? git(cwd, ['show', `${commit}:${DEMO_DIGEST_PATH}`], run) : null;
|
|
184
|
+
return {
|
|
185
|
+
commit,
|
|
186
|
+
isHead: Boolean(commit && snapshot?.head === commit),
|
|
187
|
+
published: Boolean(commit && remoteHead === commit),
|
|
188
|
+
branch,
|
|
189
|
+
upstream: snapshot?.upstream ?? null,
|
|
190
|
+
remoteHead,
|
|
191
|
+
newVersion,
|
|
192
|
+
changedPaths,
|
|
193
|
+
demoInputSha256: digest && /^[0-9a-f]{64}$/.test(digest) ? digest : null,
|
|
194
|
+
worktree: snapshot?.worktree ?? [],
|
|
195
|
+
staged: snapshot?.staged ?? []
|
|
196
|
+
};
|
|
197
|
+
}
|
|
162
198
|
function releaseSmokeStatus(workflows, version, tagCommit) {
|
|
163
199
|
const manualTitle = `Post-Release Smoke v${version}`;
|
|
164
200
|
const targets = workflows.filter((run) => {
|
|
@@ -209,12 +245,15 @@ async function inspectFacts(cwd, version) {
|
|
|
209
245
|
const workflows = platform.workflows;
|
|
210
246
|
const smoke = releaseSmokeStatus(workflows, version, localTagTarget);
|
|
211
247
|
return {
|
|
212
|
-
|
|
248
|
+
localTag: local.localTag,
|
|
249
|
+
localTagAncestor: local.localTagAncestor,
|
|
250
|
+
localTagConflict: local.localTagConflict,
|
|
213
251
|
remoteBranch: Boolean(remoteBranch && remoteBranch.split(/\s+/)[0] === head), remoteTag: Boolean(remoteTag),
|
|
214
252
|
githubRelease: platform.platform.type !== 'github'
|
|
215
253
|
? true
|
|
216
254
|
: platform.status === 'blocked' ? null : Boolean(platform.release?.published),
|
|
217
|
-
npm: npm.published, homebrew: homebrew.published, smoke
|
|
255
|
+
npm: npm.published, homebrew: homebrew.published, smoke,
|
|
256
|
+
post: inspectPostReleaseFacts(cwd, local.postCommit)
|
|
218
257
|
};
|
|
219
258
|
}
|
|
220
259
|
function parsePorcelainPath(record) {
|
|
@@ -245,13 +284,21 @@ function updateReleaseMetadata(cwd, version) {
|
|
|
245
284
|
fs.writeFileSync(filePath, content);
|
|
246
285
|
}
|
|
247
286
|
}
|
|
287
|
+
function optionValues(args, name) {
|
|
288
|
+
const values = [];
|
|
289
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
290
|
+
if (args[index] === name)
|
|
291
|
+
values.push(args[index + 1] ?? '');
|
|
292
|
+
}
|
|
293
|
+
return values;
|
|
294
|
+
}
|
|
248
295
|
async function releaseWorkflow(args = []) {
|
|
249
296
|
const [action, rawVersion, ...rest] = args;
|
|
250
297
|
const version = String(rawVersion || '').replace(/^v/, '');
|
|
251
298
|
const cwdIndex = rest.indexOf('--cwd');
|
|
252
299
|
const cwd = path.resolve(cwdIndex >= 0 && rest[cwdIndex + 1] ? rest[cwdIndex + 1] : process.cwd());
|
|
253
|
-
if (!['inspect', 'prepare', 'publish', 'post'].includes(action || '') || !semver.valid(version)) {
|
|
254
|
-
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'RELEASE_INPUT_INVALID', message: 'Usage: release-workflow inspect|prepare|publish|post <version>' } })}\n`);
|
|
300
|
+
if (!['inspect', 'prepare', 'publish', 'post-prepare', 'post-publish'].includes(action || '') || !semver.valid(version)) {
|
|
301
|
+
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'RELEASE_INPUT_INVALID', message: 'Usage: release-workflow inspect|prepare|publish|post-prepare|post-publish <version>' } })}\n`);
|
|
255
302
|
process.exitCode = 1;
|
|
256
303
|
return;
|
|
257
304
|
}
|
|
@@ -331,10 +378,56 @@ async function releaseWorkflow(args = []) {
|
|
|
331
378
|
process.exitCode = result.status === 'failed' ? 1 : result.status === 'degraded' ? 2 : 0;
|
|
332
379
|
return;
|
|
333
380
|
}
|
|
381
|
+
if (action === 'post-publish') {
|
|
382
|
+
const expectedValues = optionValues(rest, '--expected-sha256');
|
|
383
|
+
if (expectedValues.length !== 1 || !/^sha256:[0-9a-f]{64}$/.test(expectedValues[0])) {
|
|
384
|
+
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, snapshot: before, error: { code: 'RELEASE_INPUT_INVALID', message: 'post-publish requires one --expected-sha256 sha256:<64 hex>' } })}\n`);
|
|
385
|
+
process.exitCode = 1;
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
if (before.phase === 'complete') {
|
|
389
|
+
process.stdout.write(`${JSON.stringify({ status: 'no-op', changed: false, snapshot: before, error: null })}\n`);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
const confirmation = 'postConfirmation' in before ? before.postConfirmation : null;
|
|
393
|
+
if (before.phase !== 'post-prepared' || !confirmation) {
|
|
394
|
+
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, snapshot: before, error: { code: confirmation ? 'RELEASE_PHASE_INVALID' : 'RELEASE_POST_CONFIRMATION_UNAVAILABLE', message: 'Post-release facts cannot authorize publish' } })}\n`);
|
|
395
|
+
process.exitCode = 1;
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
if (confirmation.sha256 !== expectedValues[0]) {
|
|
399
|
+
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, snapshot: before, error: { code: 'RELEASE_POST_SNAPSHOT_MISMATCH', message: 'Post-release confirmation snapshot changed' } })}\n`);
|
|
400
|
+
process.exitCode = 1;
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
const channelsComplete = before.facts.githubRelease === true && before.facts.npm === true && before.facts.homebrew === true;
|
|
404
|
+
if (!channelsComplete || before.facts.smoke !== 'success') {
|
|
405
|
+
process.stdout.write(`${JSON.stringify({ status: before.facts.smoke === 'failed' ? 'failed' : 'blocked', changed: false, snapshot: before, error: { code: 'RELEASE_CHANNELS_PENDING', message: 'Release channels or smoke workflow are not complete' } })}\n`);
|
|
406
|
+
process.exitCode = before.facts.smoke === 'failed' ? 1 : 2;
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
const pushed = pushGitRefs({ cwd, remote: 'origin', refs: [before.facts.post.branch] });
|
|
410
|
+
const snapshot = releaseSnapshot(version, await inspectFacts(cwd, version));
|
|
411
|
+
if (snapshot.phase === 'complete') {
|
|
412
|
+
process.stdout.write(`${JSON.stringify({ ...pushed, status: 'applied', changed: true, snapshot, error: null })}\n`);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
const blocked = pushed.status !== 'failed';
|
|
416
|
+
process.stdout.write(`${JSON.stringify({ ...pushed, status: blocked ? 'blocked' : 'failed', snapshot, error: pushed.error ?? { code: 'RELEASE_PROGRESS_PENDING', message: 'Post-release push is not visible at the expected remote head' } })}\n`);
|
|
417
|
+
process.exitCode = blocked ? 2 : 1;
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
334
420
|
if (before.phase === 'complete') {
|
|
335
421
|
process.stdout.write(`${JSON.stringify({ status: 'no-op', changed: false, snapshot: before, error: null })}\n`);
|
|
336
422
|
return;
|
|
337
423
|
}
|
|
424
|
+
if (before.phase === 'post-prepared') {
|
|
425
|
+
const confirmation = 'postConfirmation' in before ? before.postConfirmation : null;
|
|
426
|
+
const status = confirmation ? 'no-op' : 'blocked';
|
|
427
|
+
process.stdout.write(`${JSON.stringify({ status, changed: false, snapshot: before, error: confirmation ? null : { code: 'RELEASE_POST_CONFIRMATION_UNAVAILABLE', message: 'Post commit is not the clean current HEAD' } })}\n`);
|
|
428
|
+
process.exitCode = confirmation ? 0 : 2;
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
338
431
|
const channelsComplete = before.facts.githubRelease === true && before.facts.npm === true && before.facts.homebrew === true;
|
|
339
432
|
if (!['published', 'post-pending'].includes(before.phase) || !channelsComplete || before.facts.smoke !== 'success') {
|
|
340
433
|
process.stdout.write(`${JSON.stringify({ status: before.facts.smoke === 'failed' ? 'failed' : 'blocked', changed: false, snapshot: before, error: { code: 'RELEASE_CHANNELS_PENDING', message: 'Release channels or smoke workflow are not complete' } })}\n`);
|
|
@@ -373,10 +466,14 @@ async function releaseWorkflow(args = []) {
|
|
|
373
466
|
process.exitCode = 1;
|
|
374
467
|
return;
|
|
375
468
|
}
|
|
376
|
-
const
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
469
|
+
const snapshot = releaseSnapshot(version, await inspectFacts(cwd, version));
|
|
470
|
+
const confirmation = 'postConfirmation' in snapshot ? snapshot.postConfirmation : null;
|
|
471
|
+
if (snapshot.phase !== 'post-prepared' || !confirmation) {
|
|
472
|
+
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: true, demo, snapshot, error: { code: 'RELEASE_POST_CONFIRMATION_UNAVAILABLE', message: 'Prepared post commit did not produce a confirmation snapshot' } })}\n`);
|
|
473
|
+
process.exitCode = 1;
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
process.stdout.write(`${JSON.stringify({ status: 'applied', changed: true, demo, snapshot, error: null })}\n`);
|
|
380
477
|
}
|
|
381
|
-
export { changedPaths, computeDemoInputDigest, inspectFacts, inspectLocalReleaseFacts, inspectPostWorktree, releaseSmokeStatus, releaseWorkflow, runOptionalDemo };
|
|
478
|
+
export { changedPaths, computeDemoInputDigest, inspectFacts, inspectLocalReleaseFacts, inspectPostReleaseFacts, inspectPostWorktree, releaseSmokeStatus, releaseWorkflow, runOptionalDemo };
|
|
382
479
|
//# sourceMappingURL=release-workflow.js.map
|
|
@@ -1,24 +1,61 @@
|
|
|
1
|
-
import { requestSandboxControl } from '../sandbox/control/client.js';
|
|
1
|
+
import { requestSandboxControl, SandboxControlClientError } from '../sandbox/control/client.js';
|
|
2
2
|
import { serveSandboxControl } from '../sandbox/control/server.js';
|
|
3
|
-
|
|
3
|
+
import { runSandboxControlExecutor } from '../sandbox/control/executor.js';
|
|
4
|
+
async function sandboxControl(args) {
|
|
4
5
|
const [operation, ...rest] = args;
|
|
5
6
|
if (operation === 'serve') {
|
|
6
7
|
const manifestIndex = rest.indexOf('--manifest');
|
|
7
8
|
const manifest = manifestIndex >= 0 ? rest[manifestIndex + 1] : undefined;
|
|
8
9
|
if (!manifest)
|
|
9
10
|
throw new Error('sandbox-control serve requires --manifest');
|
|
10
|
-
|
|
11
|
+
const controller = new AbortController();
|
|
12
|
+
const abort = () => controller.abort();
|
|
13
|
+
process.once('SIGINT', abort);
|
|
14
|
+
process.once('SIGTERM', abort);
|
|
15
|
+
try {
|
|
16
|
+
await serveSandboxControl(manifest, controller.signal);
|
|
17
|
+
}
|
|
18
|
+
finally {
|
|
19
|
+
process.off('SIGINT', abort);
|
|
20
|
+
process.off('SIGTERM', abort);
|
|
21
|
+
}
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (operation === 'execute') {
|
|
25
|
+
const requestIndex = rest.indexOf('--request');
|
|
26
|
+
const nonceIndex = rest.indexOf('--nonce');
|
|
27
|
+
const request = requestIndex >= 0 ? rest[requestIndex + 1] : undefined;
|
|
28
|
+
const nonce = nonceIndex >= 0 ? rest[nonceIndex + 1] : undefined;
|
|
29
|
+
if (!request || !nonce)
|
|
30
|
+
throw new Error('sandbox-control execute requires --request and --nonce');
|
|
31
|
+
await runSandboxControlExecutor(request, nonce);
|
|
11
32
|
return;
|
|
12
33
|
}
|
|
13
34
|
if (operation === 'client') {
|
|
14
35
|
const [family = '', ...commandArgs] = rest;
|
|
15
|
-
|
|
36
|
+
let response;
|
|
37
|
+
try {
|
|
38
|
+
response = requestSandboxControl({ family, args: commandArgs });
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
if (!(error instanceof SandboxControlClientError))
|
|
42
|
+
throw error;
|
|
43
|
+
process.stderr.write(`${error.detail.message}\n`);
|
|
44
|
+
process.exitCode = error.detail.retryable ? 75 : 1;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
16
47
|
process.stdout.write(response.stdout);
|
|
17
48
|
process.stderr.write(response.stderr);
|
|
18
|
-
|
|
49
|
+
if (response.phase === 'rejected') {
|
|
50
|
+
process.stderr.write(response.error?.message ?? response.stderr);
|
|
51
|
+
process.exitCode = response.error?.retryable ? 75 : 1;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
process.exitCode = response.exitCode ?? 1;
|
|
55
|
+
}
|
|
19
56
|
return;
|
|
20
57
|
}
|
|
21
|
-
throw new Error('Usage: agent-infra-internal sandbox-control serve --manifest <path> | client <family> [args...]');
|
|
58
|
+
throw new Error('Usage: agent-infra-internal sandbox-control serve --manifest <path> | execute --request <path> --nonce <nonce> | client <family> [args...]');
|
|
22
59
|
}
|
|
23
60
|
export { sandboxControl };
|
|
24
61
|
//# sourceMappingURL=sandbox-control.js.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { requestSandboxTaskCreate, SandboxControlClientError } from '../sandbox/control/client.js';
|
|
4
|
+
import { SANDBOX_CONTROL_MAX_BYTES } from '../sandbox/control/protocol.js';
|
|
5
|
+
import { validateTaskCreateCandidate } from '../task/create.js';
|
|
6
|
+
import { createTask } from '../task/create-service.js';
|
|
7
|
+
function failed(code, message, retryable = false) {
|
|
8
|
+
return {
|
|
9
|
+
status: retryable ? 'blocked' : 'failed', changed: false, task: { id: null, shortId: null }, issue: null,
|
|
10
|
+
operations: [], warnings: [], error: { code, message, retryable }
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function output(result) {
|
|
14
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
15
|
+
process.exitCode = result.status === 'blocked' ? 2 : result.status === 'failed' ? 1 : 0;
|
|
16
|
+
}
|
|
17
|
+
function taskCreate(args) {
|
|
18
|
+
const inputIndex = args.indexOf('--input');
|
|
19
|
+
const input = inputIndex >= 0 ? args[inputIndex + 1] : undefined;
|
|
20
|
+
if (!input || args.length !== 2 || inputIndex !== 0) {
|
|
21
|
+
output(failed('TASK_CREATE_PAYLOAD_INVALID', 'Usage: agent-infra-internal task-create --input <candidate.json>'));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const inputPath = path.resolve(input);
|
|
26
|
+
const stat = fs.lstatSync(inputPath);
|
|
27
|
+
if (!stat.isFile() || stat.isSymbolicLink())
|
|
28
|
+
throw new Error('TASK_CREATE_INPUT_INVALID: input must be a regular file');
|
|
29
|
+
if (stat.size > SANDBOX_CONTROL_MAX_BYTES)
|
|
30
|
+
throw new Error('TASK_CREATE_INPUT_TOO_LARGE: input exceeds the control limit');
|
|
31
|
+
const candidate = validateTaskCreateCandidate(JSON.parse(fs.readFileSync(inputPath, 'utf8')));
|
|
32
|
+
if (process.env.AGENT_INFRA_CONTROL_TOKEN) {
|
|
33
|
+
const response = requestSandboxTaskCreate({ candidate });
|
|
34
|
+
if (response.phase === 'rejected') {
|
|
35
|
+
output(failed(response.error?.code ?? 'SANDBOX_CONTROL_REJECTED', response.error?.message ?? response.stderr, response.error?.retryable ?? false));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
process.stdout.write(response.stdout);
|
|
39
|
+
process.stderr.write(response.stderr);
|
|
40
|
+
process.exitCode = response.exitCode;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
output(createTask(candidate, { repoRoot: process.cwd() }));
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
if (error instanceof SandboxControlClientError) {
|
|
47
|
+
output(failed(error.detail.code, error.detail.message, error.detail.retryable));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
51
|
+
const code = /^([A-Z][A-Z0-9_]+)/.exec(message)?.[1] ?? 'TASK_CREATE_INPUT_INVALID';
|
|
52
|
+
output(failed(code, message));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export { taskCreate };
|
|
56
|
+
//# sourceMappingURL=task-create.js.map
|