@11agents/cli 0.1.22 → 0.1.24
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/package.json +1 -1
- package/src/commands/runtime.js +72 -26
package/package.json
CHANGED
package/src/commands/runtime.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process'
|
|
2
2
|
import { createHash } from 'node:crypto'
|
|
3
|
-
import {
|
|
3
|
+
import { createWriteStream, readFileSync } from 'node:fs'
|
|
4
4
|
import { appendFile, mkdir, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises'
|
|
5
5
|
import os from 'node:os'
|
|
6
6
|
import { dirname, resolve } from 'node:path'
|
|
@@ -180,6 +180,15 @@ async function runWithRuntimeHeartbeat(operation, registration, flags, deps, hea
|
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
+
function createChunkLogger(filePath) {
|
|
184
|
+
if (!filePath) return null
|
|
185
|
+
const stream = createWriteStream(filePath, { flags: 'a' })
|
|
186
|
+
return {
|
|
187
|
+
write: chunk => { stream.write(chunk) },
|
|
188
|
+
close: () => new Promise(resolve => stream.end(resolve)),
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
183
192
|
async function runWithDaemonRetry(label, operation, deps, retryState) {
|
|
184
193
|
while (true) {
|
|
185
194
|
try {
|
|
@@ -1134,8 +1143,13 @@ async function runCodex({ task, prompt, flags = {}, deps }) {
|
|
|
1134
1143
|
const taskEnv = task.execution_context?.env || process.env
|
|
1135
1144
|
const stdoutLogPath = runDir ? path.join(runDir, 'stdout.log') : ''
|
|
1136
1145
|
if (stdoutLogPath) await writeRunFile(runDir, 'stdout.log', '')
|
|
1137
|
-
|
|
1138
|
-
let result
|
|
1146
|
+
let stdoutLogger = createChunkLogger(stdoutLogPath)
|
|
1147
|
+
let result
|
|
1148
|
+
try {
|
|
1149
|
+
result = await deps.runProcess(codexBin, args, { input: `/goal ${prompt}`, cwd: workdir, env: taskEnv, onStdoutChunk: stdoutLogger?.write })
|
|
1150
|
+
} finally {
|
|
1151
|
+
await stdoutLogger?.close()
|
|
1152
|
+
}
|
|
1139
1153
|
|
|
1140
1154
|
// When --codex-sandbox is set but bwrap is unavailable in the container (missing CAP_NET_ADMIN),
|
|
1141
1155
|
// fall back to --yolo exec which runs commands directly without bwrap.
|
|
@@ -1147,7 +1161,12 @@ async function runCodex({ task, prompt, flags = {}, deps }) {
|
|
|
1147
1161
|
deps.log(JSON.stringify({ warning: 'bwrap unavailable in this environment, retrying without sandbox', fallback_command: fallbackLine }))
|
|
1148
1162
|
await updateRunMeta(runDir, { bwrap_fallback: true, fallback_command_line: fallbackLine })
|
|
1149
1163
|
if (stdoutLogPath) await writeRunFile(runDir, 'stdout.log', '')
|
|
1150
|
-
|
|
1164
|
+
stdoutLogger = createChunkLogger(stdoutLogPath)
|
|
1165
|
+
try {
|
|
1166
|
+
result = await deps.runProcess(codexBin, fallbackArgs, { input: `/goal ${prompt}`, cwd: workdir, env: taskEnv, onStdoutChunk: stdoutLogger?.write })
|
|
1167
|
+
} finally {
|
|
1168
|
+
await stdoutLogger?.close()
|
|
1169
|
+
}
|
|
1151
1170
|
}
|
|
1152
1171
|
}
|
|
1153
1172
|
const rawStdout = String(result.stdout || '')
|
|
@@ -1232,13 +1251,18 @@ async function runClaude({ task, prompt, flags = {}, deps }) {
|
|
|
1232
1251
|
|
|
1233
1252
|
const stdoutLogPath = runDir ? path.join(runDir, 'stdout.log') : ''
|
|
1234
1253
|
if (stdoutLogPath) await writeRunFile(runDir, 'stdout.log', '')
|
|
1235
|
-
const
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1254
|
+
const stdoutLogger = createChunkLogger(stdoutLogPath)
|
|
1255
|
+
let result
|
|
1256
|
+
try {
|
|
1257
|
+
result = await deps.runProcess(claudeBin, args, {
|
|
1258
|
+
input: prompt,
|
|
1259
|
+
cwd: workdir,
|
|
1260
|
+
env: task.execution_context?.env || process.env,
|
|
1261
|
+
onStdoutChunk: stdoutLogger?.write,
|
|
1262
|
+
})
|
|
1263
|
+
} finally {
|
|
1264
|
+
await stdoutLogger?.close()
|
|
1265
|
+
}
|
|
1242
1266
|
|
|
1243
1267
|
const rawStdout = String(result.stdout || '')
|
|
1244
1268
|
const output = rawStdout.trim()
|
|
@@ -1339,7 +1363,13 @@ async function runOneRuntimeTaskSlot(runtime, config, machineKey, registration,
|
|
|
1339
1363
|
},
|
|
1340
1364
|
}
|
|
1341
1365
|
|
|
1342
|
-
deps.log(JSON.stringify({
|
|
1366
|
+
deps.log(JSON.stringify({
|
|
1367
|
+
claimed: runtimeTask.id,
|
|
1368
|
+
task_id: runtimeTask.id,
|
|
1369
|
+
task_title: String(runtimeTask.issue?.title || runtimeTask.title || ''),
|
|
1370
|
+
project_slug: String(runtimeTask.workspace?.slug || runtimeTask.workspace_slug || ''),
|
|
1371
|
+
runtime_id: runtime.id,
|
|
1372
|
+
}, null, 2))
|
|
1343
1373
|
await writeCurrentClaim(deps, runtimeTask, machineKey)
|
|
1344
1374
|
let completion = null
|
|
1345
1375
|
let executionContext = null
|
|
@@ -1348,15 +1378,17 @@ async function runOneRuntimeTaskSlot(runtime, config, machineKey, registration,
|
|
|
1348
1378
|
const syncConfig = { ...config, token }
|
|
1349
1379
|
try {
|
|
1350
1380
|
await runWithTaskRetry('sync knowledge base', () => (
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1381
|
+
runWithRuntimeHeartbeat(() => (
|
|
1382
|
+
deps.syncKnowledge({
|
|
1383
|
+
project: runtimeTask.workspace.slug,
|
|
1384
|
+
mode: 'pull',
|
|
1385
|
+
server: flags.server,
|
|
1386
|
+
token,
|
|
1387
|
+
}, {
|
|
1388
|
+
requestJson: (apiPath, options = {}) => deps.requestJson(apiPath, { ...options, config: syncConfig }),
|
|
1389
|
+
log: () => {},
|
|
1390
|
+
})
|
|
1391
|
+
), registration, flags, deps, heartbeatIntervalMs)
|
|
1360
1392
|
), deps)
|
|
1361
1393
|
} catch (error) {
|
|
1362
1394
|
completion = {
|
|
@@ -1418,7 +1450,7 @@ async function runOneRuntimeTaskSlot(runtime, config, machineKey, registration,
|
|
|
1418
1450
|
})
|
|
1419
1451
|
if (isDeepOrganize) {
|
|
1420
1452
|
try {
|
|
1421
|
-
await pushKnowledge
|
|
1453
|
+
await runWithRuntimeHeartbeat(pushKnowledge, registration, flags, deps, heartbeatIntervalMs)
|
|
1422
1454
|
} catch (error) {
|
|
1423
1455
|
const message = `Knowledge sync push failed before task completion: ${errorMessage(error)}. If a knowledge_snapshot is included in completion, the platform will write it directly.`
|
|
1424
1456
|
completion = {
|
|
@@ -1429,7 +1461,9 @@ async function runOneRuntimeTaskSlot(runtime, config, machineKey, registration,
|
|
|
1429
1461
|
}
|
|
1430
1462
|
} else {
|
|
1431
1463
|
try {
|
|
1432
|
-
await runWithTaskRetry('sync knowledge base back to cloud',
|
|
1464
|
+
await runWithTaskRetry('sync knowledge base back to cloud', () => (
|
|
1465
|
+
runWithRuntimeHeartbeat(pushKnowledge, registration, flags, deps, heartbeatIntervalMs)
|
|
1466
|
+
), deps)
|
|
1433
1467
|
} catch (error) {
|
|
1434
1468
|
const message = `Knowledge sync push failed before task completion: ${errorMessage(error)}`
|
|
1435
1469
|
completion = {
|
|
@@ -1473,6 +1507,15 @@ async function claimAndRunRuntimeTasks(registration, flags, deps, handlerModule,
|
|
|
1473
1507
|
return results.filter(r => r.status === 'fulfilled' && r.value === true).length
|
|
1474
1508
|
}
|
|
1475
1509
|
|
|
1510
|
+
async function drainRuntimeTasks(registration, flags, deps, handlerModule, retryState = createRetryState(), heartbeatIntervalMs = 15000, maxConcurrent = 1) {
|
|
1511
|
+
let completed = 0
|
|
1512
|
+
while (true) {
|
|
1513
|
+
const claimed = await claimAndRunRuntimeTasks(registration, flags, deps, handlerModule, retryState, heartbeatIntervalMs, maxConcurrent)
|
|
1514
|
+
completed += claimed
|
|
1515
|
+
if (claimed === 0) return completed
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1476
1519
|
export async function startRuntimeDaemon(flags = {}, deps = {}) {
|
|
1477
1520
|
const resolvedDeps = runtimeDeps(deps)
|
|
1478
1521
|
const heartbeatIntervalMs = Number(flag(flags, 'heartbeat-interval', '15')) * 1000
|
|
@@ -1507,8 +1550,11 @@ export async function startRuntimeDaemon(flags = {}, deps = {}) {
|
|
|
1507
1550
|
'Runtime task failed locally: daemon restarted with a persisted claimed task that had not completed.'
|
|
1508
1551
|
), resolvedDeps, retryState)
|
|
1509
1552
|
await syncRuntimeProjectMetadataBestEffort(flags, resolvedDeps)
|
|
1510
|
-
|
|
1511
|
-
|
|
1553
|
+
if (once) {
|
|
1554
|
+
await claimAndRunRuntimeTasks(registration, flags, resolvedDeps, handlerModule, retryState, heartbeatIntervalMs, maxConcurrent)
|
|
1555
|
+
return
|
|
1556
|
+
}
|
|
1557
|
+
await drainRuntimeTasks(registration, flags, resolvedDeps, handlerModule, retryState, heartbeatIntervalMs, maxConcurrent)
|
|
1512
1558
|
|
|
1513
1559
|
let lastScan = Date.now()
|
|
1514
1560
|
let lastHeartbeat = Date.now()
|
|
@@ -1526,7 +1572,7 @@ export async function startRuntimeDaemon(flags = {}, deps = {}) {
|
|
|
1526
1572
|
lastHeartbeat = now
|
|
1527
1573
|
}
|
|
1528
1574
|
if (now - lastTaskPoll >= taskIntervalMs) {
|
|
1529
|
-
await
|
|
1575
|
+
await drainRuntimeTasks(registration, flags, resolvedDeps, handlerModule, retryState, heartbeatIntervalMs, maxConcurrent)
|
|
1530
1576
|
lastTaskPoll = now
|
|
1531
1577
|
}
|
|
1532
1578
|
if (now - lastProjectRefresh >= projectRefreshIntervalMs) {
|