@11agents/cli 0.1.13 → 0.1.14
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 +9 -1
- package/bin/11agents.js +14 -0
- package/package.json +1 -1
- package/src/commands/logs.js +96 -0
- package/src/commands/runtime.js +41 -0
package/README.md
CHANGED
|
@@ -84,6 +84,13 @@ Run the daemon in the background:
|
|
|
84
84
|
|
|
85
85
|
Background mode writes its pid to `~/.11agents/daemon.pid` and logs to `~/.11agents/daemon.log`.
|
|
86
86
|
|
|
87
|
+
Inspect daemon and task execution logs:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
11agents logs daemon --tail 200
|
|
91
|
+
11agents logs task <taskId> --project <slug>
|
|
92
|
+
```
|
|
93
|
+
|
|
87
94
|
Useful daemon options:
|
|
88
95
|
|
|
89
96
|
```bash
|
|
@@ -99,10 +106,11 @@ On startup, and every 30 minutes after that, the daemon syncs project metadata a
|
|
|
99
106
|
- Agent-local skills: `~/.11agents/<project>/agents/<agent>/skills/`
|
|
100
107
|
- Cloud database snapshot: `~/.11agents/<project>/database/snapshot.json`
|
|
101
108
|
- Task scratch directory: `~/.11agents/<project>/tmp/<taskId>/`
|
|
109
|
+
- Task execution logs: `~/.11agents/<project>/runs/<taskId>/`
|
|
102
110
|
|
|
103
111
|
Codex runs from `~/.11agents/<project>/` by default. Treat that directory as read-only project context. Task code may write temporary files only under `./tmp/<taskId>/`; the daemon removes that task scratch directory after the task finishes. Agent environment variables from the control plane are injected into the Codex child process and are not written to disk.
|
|
104
112
|
|
|
105
|
-
The built-in Codex worker starts task executions with `codex --
|
|
113
|
+
The built-in Codex worker starts task executions with `codex --yolo exec` and prefixes the task prompt with `/goal ` by default so remote runtime tasks can run without approval prompts or sandbox restrictions. To opt a daemon back into a Codex sandbox, start it with `--codex-sandbox read-only`, `--codex-sandbox workspace-write`, or `--codex-sandbox danger-full-access`.
|
|
106
114
|
|
|
107
115
|
The built-in task runner currently supports Codex tasks. A custom handler may export:
|
|
108
116
|
|
package/bin/11agents.js
CHANGED
|
@@ -3,6 +3,7 @@ import { readFile } from 'node:fs/promises'
|
|
|
3
3
|
import { fileURLToPath } from 'node:url'
|
|
4
4
|
import { parseArgs } from '../src/args.js'
|
|
5
5
|
import { knowledgeStatus, syncKnowledge } from '../src/commands/knowledge.js'
|
|
6
|
+
import { showDaemonLog, showTaskLog } from '../src/commands/logs.js'
|
|
6
7
|
import { runNode } from '../src/commands/node.js'
|
|
7
8
|
import { pushArtifact, pushBatch, pushObservation } from '../src/commands/push.js'
|
|
8
9
|
import { registerRuntime, scanRuntime, startRuntimeDaemon } from '../src/commands/runtime.js'
|
|
@@ -24,6 +25,8 @@ Usage:
|
|
|
24
25
|
11agents daemon status
|
|
25
26
|
11agents daemon stop
|
|
26
27
|
11agents daemon start --handler ./worker.js # optional custom worker override
|
|
28
|
+
11agents logs daemon [--tail 200]
|
|
29
|
+
11agents logs task <task-id> [--project <slug>] [--tail 120]
|
|
27
30
|
11agents mcp start
|
|
28
31
|
11agents validate <file>
|
|
29
32
|
11agents push batch <file>
|
|
@@ -116,6 +119,17 @@ async function main() {
|
|
|
116
119
|
return
|
|
117
120
|
}
|
|
118
121
|
|
|
122
|
+
if (command === 'logs' && subcommand === 'daemon') {
|
|
123
|
+
await showDaemonLog(flags)
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (command === 'logs' && subcommand === 'task') {
|
|
128
|
+
if (!target) throw new Error('logs task requires a task id')
|
|
129
|
+
await showTaskLog(target, flags)
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
|
|
119
133
|
if (command === 'mcp' && (!subcommand || subcommand === 'start')) {
|
|
120
134
|
await startMcpServer()
|
|
121
135
|
return
|
package/package.json
CHANGED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { readdir, readFile } from 'node:fs/promises'
|
|
2
|
+
import { homedir } from 'node:os'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { backgroundPaths } from '../daemon-process.js'
|
|
5
|
+
import { flag } from '../args.js'
|
|
6
|
+
|
|
7
|
+
function tailText(text, lines) {
|
|
8
|
+
const count = Math.max(1, Number(lines) || 200)
|
|
9
|
+
const parts = String(text || '').replace(/\n$/, '').split('\n')
|
|
10
|
+
return parts.slice(-count).join('\n')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function readText(filePath, fallback = '') {
|
|
14
|
+
try {
|
|
15
|
+
return await readFile(filePath, 'utf8')
|
|
16
|
+
} catch (error) {
|
|
17
|
+
if (error?.code === 'ENOENT') return fallback
|
|
18
|
+
throw error
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function readJson(filePath, fallback = {}) {
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(await readFile(filePath, 'utf8'))
|
|
25
|
+
} catch (error) {
|
|
26
|
+
if (error?.code === 'ENOENT') return fallback
|
|
27
|
+
return fallback
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function readDaemonLog({ homeDir = homedir(), tail = 200 } = {}) {
|
|
32
|
+
const text = await readText(backgroundPaths(homeDir).logPath, '')
|
|
33
|
+
return tailText(text, tail)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function findTaskRunDir({ taskId, project = '', homeDir = homedir() }) {
|
|
37
|
+
if (!taskId) throw new Error('task id is required')
|
|
38
|
+
const baseDir = path.join(homeDir, '.11agents')
|
|
39
|
+
if (project) return path.join(baseDir, project, 'runs', taskId)
|
|
40
|
+
|
|
41
|
+
const entries = await readdir(baseDir, { withFileTypes: true }).catch(error => {
|
|
42
|
+
if (error?.code === 'ENOENT') return []
|
|
43
|
+
throw error
|
|
44
|
+
})
|
|
45
|
+
for (const entry of entries) {
|
|
46
|
+
if (!entry.isDirectory()) continue
|
|
47
|
+
const runDir = path.join(baseDir, entry.name, 'runs', taskId)
|
|
48
|
+
const meta = await readJson(path.join(runDir, 'meta.json'), null)
|
|
49
|
+
if (meta) return runDir
|
|
50
|
+
}
|
|
51
|
+
return path.join(baseDir, 'project', 'runs', taskId)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function formatTaskLog({ taskId, project = '', homeDir = homedir(), tail = 120 } = {}) {
|
|
55
|
+
const runDir = await findTaskRunDir({ taskId, project, homeDir })
|
|
56
|
+
const meta = await readJson(path.join(runDir, 'meta.json'), null)
|
|
57
|
+
if (!meta) throw new Error(`task log not found: ${taskId}`)
|
|
58
|
+
|
|
59
|
+
const stdout = tailText(await readText(path.join(runDir, 'stdout.log'), ''), tail)
|
|
60
|
+
const stderr = tailText(await readText(path.join(runDir, 'stderr.log'), ''), tail)
|
|
61
|
+
const completion = await readText(path.join(runDir, 'completion.json'), '')
|
|
62
|
+
return [
|
|
63
|
+
`task: ${meta.task_id || taskId}`,
|
|
64
|
+
`provider: ${meta.provider || ''}`,
|
|
65
|
+
`workdir: ${meta.workdir || ''}`,
|
|
66
|
+
`command: ${meta.command_line || ''}`,
|
|
67
|
+
`exit_code: ${meta.exit_code ?? ''}`,
|
|
68
|
+
`run_dir: ${runDir}`,
|
|
69
|
+
'',
|
|
70
|
+
'stdout:',
|
|
71
|
+
stdout,
|
|
72
|
+
'',
|
|
73
|
+
'stderr:',
|
|
74
|
+
stderr,
|
|
75
|
+
'',
|
|
76
|
+
'completion:',
|
|
77
|
+
completion.trim(),
|
|
78
|
+
].join('\n').trimEnd()
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function showDaemonLog(flags = {}, deps = {}) {
|
|
82
|
+
const log = await readDaemonLog({ homeDir: deps.homeDir, tail: flag(flags, 'tail', '200') })
|
|
83
|
+
;(deps.log || console.log)(log)
|
|
84
|
+
return log
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function showTaskLog(taskId, flags = {}, deps = {}) {
|
|
88
|
+
const text = await formatTaskLog({
|
|
89
|
+
taskId,
|
|
90
|
+
project: flag(flags, 'project'),
|
|
91
|
+
homeDir: deps.homeDir,
|
|
92
|
+
tail: flag(flags, 'tail', '120'),
|
|
93
|
+
})
|
|
94
|
+
;(deps.log || console.log)(text)
|
|
95
|
+
return text
|
|
96
|
+
}
|
package/src/commands/runtime.js
CHANGED
|
@@ -693,6 +693,19 @@ async function appendTaskMemoryDelta({ task, completion, workdir }) {
|
|
|
693
693
|
return { written: true, path: indexPath }
|
|
694
694
|
}
|
|
695
695
|
|
|
696
|
+
async function writeRunFile(runDir, fileName, content) {
|
|
697
|
+
if (!runDir) return
|
|
698
|
+
await mkdir(runDir, { recursive: true })
|
|
699
|
+
await writeFile(path.join(runDir, fileName), content)
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
async function updateRunMeta(runDir, patch) {
|
|
703
|
+
if (!runDir) return
|
|
704
|
+
const metaPath = path.join(runDir, 'meta.json')
|
|
705
|
+
const current = await readJsonFile(metaPath, {})
|
|
706
|
+
await writeRunFile(runDir, 'meta.json', JSON.stringify({ ...current, ...patch }, null, 2))
|
|
707
|
+
}
|
|
708
|
+
|
|
696
709
|
function databaseSyncSpec(task) {
|
|
697
710
|
const spec = task.database || task.cloud_database || task.workspace?.database || null
|
|
698
711
|
if (!spec || typeof spec !== 'object') return null
|
|
@@ -755,7 +768,9 @@ function agentEnvironment(task) {
|
|
|
755
768
|
async function prepareRuntimeTask(task, flags, deps, config) {
|
|
756
769
|
const workdir = flag(flags, 'codex-workdir') || projectDirForTask(task, flags, deps)
|
|
757
770
|
const tmpDir = path.join(workdir, 'tmp', sanitizeTaskId(task.id))
|
|
771
|
+
const runDir = path.join(workdir, 'runs', sanitizeTaskId(task.id))
|
|
758
772
|
await mkdir(tmpDir, { recursive: true })
|
|
773
|
+
await mkdir(runDir, { recursive: true })
|
|
759
774
|
|
|
760
775
|
const database = await syncDatabaseIfNeeded({ task, workdir, config, flags, deps })
|
|
761
776
|
const skills = await materializeSkillsIfChanged({ task, workdir, flags, deps })
|
|
@@ -770,6 +785,7 @@ async function prepareRuntimeTask(task, flags, deps, config) {
|
|
|
770
785
|
return {
|
|
771
786
|
workdir,
|
|
772
787
|
tmp_dir: tmpDir,
|
|
788
|
+
run_dir: runDir,
|
|
773
789
|
project_slug: projectSlugForTask(task, flags),
|
|
774
790
|
readonly: true,
|
|
775
791
|
env,
|
|
@@ -874,10 +890,22 @@ async function runCodex({ task, prompt, flags = {}, deps }) {
|
|
|
874
890
|
if (profile) args.splice(execIndex + 1, 0, '--profile', profile)
|
|
875
891
|
|
|
876
892
|
const commandLine = [codexBin, ...args].map(value => JSON.stringify(String(value))).join(' ')
|
|
893
|
+
const runDir = task.execution_context?.run_dir
|
|
894
|
+
await writeRunFile(runDir, 'prompt.md', `/goal ${prompt}`)
|
|
895
|
+
await updateRunMeta(runDir, {
|
|
896
|
+
provider: 'codex',
|
|
897
|
+
command: codexBin,
|
|
898
|
+
args,
|
|
899
|
+
command_line: commandLine,
|
|
900
|
+
workdir,
|
|
901
|
+
})
|
|
877
902
|
deps.log(JSON.stringify({ running: 'codex exec', command: commandLine, workdir }, null, 2))
|
|
878
903
|
const result = await deps.runProcess(codexBin, args, { input: `/goal ${prompt}`, cwd: workdir, env: task.execution_context?.env || process.env })
|
|
879
904
|
const output = String(result.stdout || '').trim()
|
|
880
905
|
const error = String(result.stderr || '').trim()
|
|
906
|
+
await writeRunFile(runDir, 'stdout.log', String(result.stdout || ''))
|
|
907
|
+
await writeRunFile(runDir, 'stderr.log', String(result.stderr || ''))
|
|
908
|
+
await updateRunMeta(runDir, { exit_code: result.code })
|
|
881
909
|
if (result.code !== 0) {
|
|
882
910
|
const body = error || output || `codex exited with status ${result.code}`
|
|
883
911
|
const trustHint = body.includes('--skip-git-repo-check')
|
|
@@ -1002,6 +1030,15 @@ async function claimAndRunRuntimeTasks(registration, flags, deps, handlerModule,
|
|
|
1002
1030
|
if (!completion) {
|
|
1003
1031
|
executionContext = await prepareRuntimeTask(runtimeTask, flags, deps, config)
|
|
1004
1032
|
runtimeTask.execution_context = executionContext
|
|
1033
|
+
await updateRunMeta(executionContext.run_dir, {
|
|
1034
|
+
task_id: String(runtimeTask.id || ''),
|
|
1035
|
+
runtime_id: String(runtimeTask.runtime_id || ''),
|
|
1036
|
+
provider: runtimeTask.runtime?.provider || runtime.provider || '',
|
|
1037
|
+
project_slug: executionContext.project_slug,
|
|
1038
|
+
agent: agentNameForTask(runtimeTask),
|
|
1039
|
+
issue_title: runtimeTask.issue?.title || '',
|
|
1040
|
+
started_at: new Date().toISOString(),
|
|
1041
|
+
})
|
|
1005
1042
|
try {
|
|
1006
1043
|
completion = await runWithRuntimeHeartbeat(
|
|
1007
1044
|
() => handlerModule.handleRuntimeTask(runtimeTask),
|
|
@@ -1019,6 +1056,10 @@ async function claimAndRunRuntimeTasks(registration, flags, deps, handlerModule,
|
|
|
1019
1056
|
await rm(executionContext.tmp_dir, { recursive: true, force: true })
|
|
1020
1057
|
}
|
|
1021
1058
|
}
|
|
1059
|
+
if (executionContext) {
|
|
1060
|
+
await writeRunFile(executionContext.run_dir, 'completion.json', JSON.stringify(normalizeTaskCompletion(runtimeTask, completion), null, 2))
|
|
1061
|
+
await updateRunMeta(executionContext.run_dir, { ended_at: new Date().toISOString() })
|
|
1062
|
+
}
|
|
1022
1063
|
if (executionContext) {
|
|
1023
1064
|
await appendTaskMemoryDelta({ task: runtimeTask, completion, workdir: executionContext.workdir })
|
|
1024
1065
|
}
|