@11agents/cli 0.1.15 → 0.1.17
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 +132 -29
- package/src/mcp.js +32 -0
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 { readFileSync } from 'node:fs'
|
|
3
|
+
import { appendFileSync, readFileSync } from 'node:fs'
|
|
4
4
|
import { appendFile, mkdir, readFile, rm, writeFile } from 'node:fs/promises'
|
|
5
5
|
import os from 'node:os'
|
|
6
6
|
import { dirname, resolve } from 'node:path'
|
|
@@ -20,7 +20,7 @@ function sleep(ms) {
|
|
|
20
20
|
return new Promise(resolve => setTimeout(resolve, ms))
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export function runProcess(command, args, { input = '', cwd = process.cwd(), env = process.env } = {}) {
|
|
23
|
+
export function runProcess(command, args, { input = '', cwd = process.cwd(), env = process.env, onStdoutChunk } = {}) {
|
|
24
24
|
return new Promise(resolve => {
|
|
25
25
|
let settled = false
|
|
26
26
|
const child = spawn(command, args, { cwd, env, stdio: ['pipe', 'pipe', 'pipe'] })
|
|
@@ -31,7 +31,10 @@ export function runProcess(command, args, { input = '', cwd = process.cwd(), env
|
|
|
31
31
|
settled = true
|
|
32
32
|
resolve(result)
|
|
33
33
|
}
|
|
34
|
-
child.stdout.on('data', chunk => {
|
|
34
|
+
child.stdout.on('data', chunk => {
|
|
35
|
+
stdout += chunk
|
|
36
|
+
if (onStdoutChunk) onStdoutChunk(chunk)
|
|
37
|
+
})
|
|
35
38
|
child.stderr.on('data', chunk => { stderr += chunk })
|
|
36
39
|
child.on('error', error => {
|
|
37
40
|
settle({ code: 127, stdout, stderr: error.message })
|
|
@@ -74,6 +77,7 @@ function runtimeDeps(overrides = {}) {
|
|
|
74
77
|
log: overrides.log || (value => console.log(value)),
|
|
75
78
|
homeDir: overrides.homeDir || os.homedir(),
|
|
76
79
|
runCodex: overrides.runCodex || runCodex,
|
|
80
|
+
runClaude: overrides.runClaude || runClaude,
|
|
77
81
|
runProcess: overrides.runProcess || runProcess,
|
|
78
82
|
requestJson: overrides.requestJson || requestJson,
|
|
79
83
|
sleep: overrides.sleep || sleep,
|
|
@@ -889,12 +893,17 @@ async function prepareRuntimeTask(task, flags, deps, config) {
|
|
|
889
893
|
|
|
890
894
|
const database = await syncDatabaseIfNeeded({ task, workdir, config, flags, deps })
|
|
891
895
|
const skills = await materializeSkillsIfChanged({ task, workdir, flags, deps })
|
|
896
|
+
|
|
897
|
+
// Resolve and inject the project token so MCP subprocesses (e.g. `11agents mcp start`)
|
|
898
|
+
// spawned by the runtime agent can authenticate without needing credentials on disk.
|
|
899
|
+
const projectToken = await projectSyncToken(projectTokenCandidatesForTask(task, flags), flags, deps)
|
|
892
900
|
const env = {
|
|
893
901
|
...process.env,
|
|
894
902
|
...agentEnvironment(task),
|
|
895
903
|
ELEVENAGENTS_PROJECT_DIR: workdir,
|
|
896
904
|
ELEVENAGENTS_TASK_TMP: tmpDir,
|
|
897
905
|
ELEVENAGENTS_TASK_ID: String(task.id || ''),
|
|
906
|
+
...(projectToken ? { GTM_SWARM_TOKEN: projectToken } : {}),
|
|
898
907
|
}
|
|
899
908
|
|
|
900
909
|
return {
|
|
@@ -981,34 +990,20 @@ async function runCodex({ task, prompt, flags = {}, deps }) {
|
|
|
981
990
|
const runDir = task.execution_context?.run_dir
|
|
982
991
|
const lastMessagePath = runDir ? path.join(runDir, 'last_message.md') : ''
|
|
983
992
|
const jsonLogArgs = lastMessagePath ? ['--json', '--output-last-message', lastMessagePath] : ['--json']
|
|
984
|
-
const args = sandbox
|
|
985
|
-
? [
|
|
986
|
-
'--ask-for-approval',
|
|
987
|
-
'never',
|
|
988
|
-
'exec',
|
|
989
|
-
...jsonLogArgs,
|
|
990
|
-
'--skip-git-repo-check',
|
|
991
|
-
'--sandbox',
|
|
992
|
-
sandbox,
|
|
993
|
-
'-C',
|
|
994
|
-
workdir,
|
|
995
|
-
'-',
|
|
996
|
-
]
|
|
997
|
-
: [
|
|
998
|
-
'--yolo',
|
|
999
|
-
'exec',
|
|
1000
|
-
...jsonLogArgs,
|
|
1001
|
-
'--skip-git-repo-check',
|
|
1002
|
-
'-C',
|
|
1003
|
-
workdir,
|
|
1004
|
-
'-',
|
|
1005
|
-
]
|
|
1006
993
|
const model = flag(flags, 'codex-model')
|
|
1007
994
|
const profile = flag(flags, 'codex-profile')
|
|
1008
|
-
const execIndex = args.indexOf('exec')
|
|
1009
|
-
if (model) args.splice(execIndex + 1, 0, '--model', model)
|
|
1010
|
-
if (profile) args.splice(execIndex + 1, 0, '--profile', profile)
|
|
1011
995
|
|
|
996
|
+
function buildCodexArgs(withSandbox) {
|
|
997
|
+
const built = withSandbox
|
|
998
|
+
? ['--ask-for-approval', 'never', 'exec', ...jsonLogArgs, '--skip-git-repo-check', '--sandbox', withSandbox, '-C', workdir, '-']
|
|
999
|
+
: ['--yolo', 'exec', ...jsonLogArgs, '--skip-git-repo-check', '-C', workdir, '-']
|
|
1000
|
+
const execIdx = built.indexOf('exec')
|
|
1001
|
+
if (model) built.splice(execIdx + 1, 0, '--model', model)
|
|
1002
|
+
if (profile) built.splice(execIdx + 1, 0, '--profile', profile)
|
|
1003
|
+
return built
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
const args = buildCodexArgs(sandbox)
|
|
1012
1007
|
const commandLine = [codexBin, ...args].map(value => JSON.stringify(String(value))).join(' ')
|
|
1013
1008
|
await writeRunFile(runDir, 'prompt.md', `/goal ${prompt}`)
|
|
1014
1009
|
await updateRunMeta(runDir, {
|
|
@@ -1019,7 +1014,25 @@ async function runCodex({ task, prompt, flags = {}, deps }) {
|
|
|
1019
1014
|
workdir,
|
|
1020
1015
|
})
|
|
1021
1016
|
deps.log(JSON.stringify({ running: 'codex exec', command: commandLine, workdir }, null, 2))
|
|
1022
|
-
const
|
|
1017
|
+
const taskEnv = task.execution_context?.env || process.env
|
|
1018
|
+
const stdoutLogPath = runDir ? path.join(runDir, 'stdout.log') : ''
|
|
1019
|
+
if (stdoutLogPath) await writeRunFile(runDir, 'stdout.log', '')
|
|
1020
|
+
const onStdoutChunk = stdoutLogPath ? (chunk) => { try { appendFileSync(stdoutLogPath, chunk) } catch {} } : undefined
|
|
1021
|
+
let result = await deps.runProcess(codexBin, args, { input: `/goal ${prompt}`, cwd: workdir, env: taskEnv, onStdoutChunk })
|
|
1022
|
+
|
|
1023
|
+
// When --codex-sandbox is set but bwrap is unavailable in the container (missing CAP_NET_ADMIN),
|
|
1024
|
+
// fall back to --yolo exec which runs commands directly without bwrap.
|
|
1025
|
+
if (result.code !== 0 && sandbox) {
|
|
1026
|
+
const errOutput = String(result.stderr || '') + String(result.stdout || '')
|
|
1027
|
+
if (errOutput.includes('bwrap:') && (errOutput.includes('Operation not permitted') || errOutput.includes('RTM_NEWADDR'))) {
|
|
1028
|
+
const fallbackArgs = buildCodexArgs(null)
|
|
1029
|
+
const fallbackLine = [codexBin, ...fallbackArgs].map(v => JSON.stringify(String(v))).join(' ')
|
|
1030
|
+
deps.log(JSON.stringify({ warning: 'bwrap unavailable in this environment, retrying without sandbox', fallback_command: fallbackLine }))
|
|
1031
|
+
await updateRunMeta(runDir, { bwrap_fallback: true, fallback_command_line: fallbackLine })
|
|
1032
|
+
if (stdoutLogPath) await writeRunFile(runDir, 'stdout.log', '')
|
|
1033
|
+
result = await deps.runProcess(codexBin, fallbackArgs, { input: `/goal ${prompt}`, cwd: workdir, env: taskEnv, onStdoutChunk })
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1023
1036
|
const rawStdout = String(result.stdout || '')
|
|
1024
1037
|
const transcript = codexTranscriptFromJsonl(rawStdout)
|
|
1025
1038
|
const assistantText = codexAssistantTextFromJsonl(rawStdout)
|
|
@@ -1077,10 +1090,100 @@ async function runCodex({ task, prompt, flags = {}, deps }) {
|
|
|
1077
1090
|
}
|
|
1078
1091
|
}
|
|
1079
1092
|
|
|
1093
|
+
async function runClaude({ task, prompt, flags = {}, deps }) {
|
|
1094
|
+
const claudeBin = flag(flags, 'claude-bin', 'claude')
|
|
1095
|
+
const workdir = flag(flags, 'claude-workdir') || flag(flags, 'codex-workdir') || task.execution_context?.workdir || process.cwd()
|
|
1096
|
+
const model = flag(flags, 'claude-model')
|
|
1097
|
+
const runDir = task.execution_context?.run_dir
|
|
1098
|
+
|
|
1099
|
+
// --dangerously-skip-permissions bypasses bwrap sandboxing and auto-approves MCP tool calls,
|
|
1100
|
+
// which are both required for headless remote runtimes running inside containers.
|
|
1101
|
+
const args = ['--dangerously-skip-permissions', '--print']
|
|
1102
|
+
if (model) args.push('--model', model)
|
|
1103
|
+
|
|
1104
|
+
const commandLine = [claudeBin, ...args].map(a => JSON.stringify(String(a))).join(' ')
|
|
1105
|
+
await writeRunFile(runDir, 'prompt.md', prompt)
|
|
1106
|
+
await updateRunMeta(runDir, {
|
|
1107
|
+
provider: 'claude',
|
|
1108
|
+
command: claudeBin,
|
|
1109
|
+
args,
|
|
1110
|
+
command_line: commandLine,
|
|
1111
|
+
workdir,
|
|
1112
|
+
})
|
|
1113
|
+
|
|
1114
|
+
deps.log(JSON.stringify({ running: 'claude --print', command: commandLine, workdir }, null, 2))
|
|
1115
|
+
|
|
1116
|
+
const stdoutLogPath = runDir ? path.join(runDir, 'stdout.log') : ''
|
|
1117
|
+
if (stdoutLogPath) await writeRunFile(runDir, 'stdout.log', '')
|
|
1118
|
+
const onStdoutChunk = stdoutLogPath ? (chunk) => { try { appendFileSync(stdoutLogPath, chunk) } catch {} } : undefined
|
|
1119
|
+
const result = await deps.runProcess(claudeBin, args, {
|
|
1120
|
+
input: prompt,
|
|
1121
|
+
cwd: workdir,
|
|
1122
|
+
env: task.execution_context?.env || process.env,
|
|
1123
|
+
onStdoutChunk,
|
|
1124
|
+
})
|
|
1125
|
+
|
|
1126
|
+
const rawStdout = String(result.stdout || '')
|
|
1127
|
+
const output = rawStdout.trim()
|
|
1128
|
+
const error = String(result.stderr || '').trim()
|
|
1129
|
+
|
|
1130
|
+
await writeRunFile(runDir, 'stdout.log', rawStdout)
|
|
1131
|
+
await writeRunFile(runDir, 'stderr.log', String(result.stderr || ''))
|
|
1132
|
+
await updateRunMeta(runDir, { exit_code: result.code })
|
|
1133
|
+
|
|
1134
|
+
if (result.code !== 0) {
|
|
1135
|
+
return {
|
|
1136
|
+
comment: `${error || output || `claude exited with status ${result.code}`}\n\nClaude command: ${commandLine}`,
|
|
1137
|
+
status: 'failed',
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
if (knowledgeDeepOrganizeSpec(task)) {
|
|
1142
|
+
const structured = extractJsonObject(output)
|
|
1143
|
+
if (structured?.knowledge_snapshot || structured?.knowledgeSnapshot) {
|
|
1144
|
+
return {
|
|
1145
|
+
...structured,
|
|
1146
|
+
comment: String(structured.comment || structured.summary || output || `Claude completed task ${task.id}.`),
|
|
1147
|
+
memory_delta: String(structured.memory_delta || structured.memoryDelta || `Claude completed task ${task.id}.`),
|
|
1148
|
+
status: structured.status ? String(structured.status) : 'in_review',
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
const recoveredSnapshot = buildRecoveredDeepKnowledgeSnapshot(task, [
|
|
1152
|
+
structured?.comment || structured?.summary || '',
|
|
1153
|
+
structured?.memory_delta || structured?.memoryDelta || '',
|
|
1154
|
+
output,
|
|
1155
|
+
].filter(Boolean).join('\n\n'))
|
|
1156
|
+
if (recoveredSnapshot) {
|
|
1157
|
+
return {
|
|
1158
|
+
comment: [
|
|
1159
|
+
String(structured?.comment || structured?.summary || output || `Claude completed task ${task.id}.`).trim(),
|
|
1160
|
+
'Recovered a knowledge_snapshot from the worker output because local tooling or MCP push was unavailable.',
|
|
1161
|
+
].filter(Boolean).join('\n\n'),
|
|
1162
|
+
memory_delta: [
|
|
1163
|
+
String(structured?.memory_delta || structured?.memoryDelta || '').trim(),
|
|
1164
|
+
'Recovered deep knowledge organization snapshot from worker output.',
|
|
1165
|
+
].filter(Boolean).join('\n'),
|
|
1166
|
+
status: 'in_review',
|
|
1167
|
+
knowledge_snapshot: recoveredSnapshot,
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
return {
|
|
1173
|
+
comment: output || `Claude completed task ${task.id}.`,
|
|
1174
|
+
memory_delta: `Claude completed task ${task.id}.`,
|
|
1175
|
+
status: 'in_review',
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1080
1179
|
function defaultTaskHandler(flags, deps) {
|
|
1081
1180
|
return {
|
|
1082
1181
|
async handleRuntimeTask(task) {
|
|
1083
1182
|
const provider = task.runtime?.provider || ''
|
|
1183
|
+
if (provider === 'claude') {
|
|
1184
|
+
const prompt = buildCodexPrompt(task)
|
|
1185
|
+
return deps.runClaude({ task, prompt, flags, deps })
|
|
1186
|
+
}
|
|
1084
1187
|
if (provider !== 'codex') {
|
|
1085
1188
|
return {
|
|
1086
1189
|
comment: `unsupported runtime provider: ${provider || 'unknown'}`,
|
package/src/mcp.js
CHANGED
|
@@ -17,6 +17,22 @@ const TOOLS = [
|
|
|
17
17
|
},
|
|
18
18
|
},
|
|
19
19
|
},
|
|
20
|
+
{
|
|
21
|
+
name: 'database_sync',
|
|
22
|
+
description: 'Pull or push the cloud project database snapshot with a project token.',
|
|
23
|
+
inputSchema: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
required: ['project', 'mode'],
|
|
26
|
+
properties: {
|
|
27
|
+
project: { type: 'string' },
|
|
28
|
+
mode: { type: 'string', enum: ['pull', 'push'] },
|
|
29
|
+
token: { type: 'string' },
|
|
30
|
+
server: { type: 'string' },
|
|
31
|
+
snapshot: { type: 'object' },
|
|
32
|
+
metadata: { type: 'object' },
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
20
36
|
]
|
|
21
37
|
|
|
22
38
|
function textResult(value) {
|
|
@@ -64,6 +80,22 @@ export async function handleMcpRequest(request, deps = {}) {
|
|
|
64
80
|
})
|
|
65
81
|
return textResult(result)
|
|
66
82
|
}
|
|
83
|
+
if (name === 'database_sync') {
|
|
84
|
+
const token = await resolveProjectToken(args.project, {
|
|
85
|
+
homeDir: deps.homeDir,
|
|
86
|
+
token: args.token,
|
|
87
|
+
})
|
|
88
|
+
const server = args.server || 'https://app.11agents.ai'
|
|
89
|
+
const result = await (deps.requestJson || requestJson)(
|
|
90
|
+
`/api/projects/${encodeURIComponent(args.project)}/database/sync`,
|
|
91
|
+
{
|
|
92
|
+
method: 'POST',
|
|
93
|
+
body: { mode: args.mode || 'pull', snapshot: args.snapshot, metadata: args.metadata },
|
|
94
|
+
config: { token, server },
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
return textResult(result)
|
|
98
|
+
}
|
|
67
99
|
throw new Error(`unknown MCP tool: ${name}`)
|
|
68
100
|
}
|
|
69
101
|
|