@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@11agents/cli",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "description": "11agents local runtime and telemetry CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  import { spawn } from 'node:child_process'
2
2
  import { createHash } from 'node:crypto'
3
- import { appendFileSync, readFileSync } from 'node:fs'
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
- const onStdoutChunk = stdoutLogPath ? (chunk) => { try { appendFileSync(stdoutLogPath, chunk) } catch {} } : undefined
1138
- let result = await deps.runProcess(codexBin, args, { input: `/goal ${prompt}`, cwd: workdir, env: taskEnv, onStdoutChunk })
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
- result = await deps.runProcess(codexBin, fallbackArgs, { input: `/goal ${prompt}`, cwd: workdir, env: taskEnv, onStdoutChunk })
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 onStdoutChunk = stdoutLogPath ? (chunk) => { try { appendFileSync(stdoutLogPath, chunk) } catch {} } : undefined
1236
- const result = await deps.runProcess(claudeBin, args, {
1237
- input: prompt,
1238
- cwd: workdir,
1239
- env: task.execution_context?.env || process.env,
1240
- onStdoutChunk,
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({ claimed: runtimeTask.id, runtime_id: runtime.id }, null, 2))
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
- deps.syncKnowledge({
1352
- project: runtimeTask.workspace.slug,
1353
- mode: 'pull',
1354
- server: flags.server,
1355
- token,
1356
- }, {
1357
- requestJson: (apiPath, options = {}) => deps.requestJson(apiPath, { ...options, config: syncConfig }),
1358
- log: () => {},
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', pushKnowledge, deps)
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
- await claimAndRunRuntimeTasks(registration, flags, resolvedDeps, handlerModule, retryState, heartbeatIntervalMs, maxConcurrent)
1511
- if (once) return
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 claimAndRunRuntimeTasks(registration, flags, resolvedDeps, handlerModule, retryState, heartbeatIntervalMs, maxConcurrent)
1575
+ await drainRuntimeTasks(registration, flags, resolvedDeps, handlerModule, retryState, heartbeatIntervalMs, maxConcurrent)
1530
1576
  lastTaskPoll = now
1531
1577
  }
1532
1578
  if (now - lastProjectRefresh >= projectRefreshIntervalMs) {