@11agents/cli 0.1.7 → 0.1.9
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 +19 -2
- package/package.json +1 -1
- package/src/commands/runtime.js +147 -56
- package/src/credentials.js +5 -2
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ export ELEVENAGENTS_SERVER="http://localhost:8082"
|
|
|
27
27
|
|
|
28
28
|
On startup, the CLI prints its current version and target server to stderr. It also checks npm for a newer `@11agents/cli` package and prints an upgrade command when one is available.
|
|
29
29
|
|
|
30
|
-
Project-scoped MCP
|
|
30
|
+
Project-scoped MCP, knowledge-base sync, and database sync tokens can be stored in `~/.11agents/credentials`:
|
|
31
31
|
|
|
32
32
|
```yaml
|
|
33
33
|
# tokens:
|
|
@@ -35,7 +35,24 @@ flatkey: gtm_xxxxxxxxxxxxxxxxxxxx
|
|
|
35
35
|
voc-ai: gtm_yyyyyyyyyyyyyyyyyyyy
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
When syncing
|
|
38
|
+
When syncing project data, the CLI chooses tokens in this order: explicit tool/command token, matching project token from `~/.11agents/credentials`, `GTM_SWARM_TOKEN`, then the daemon control token as a compatibility fallback.
|
|
39
|
+
|
|
40
|
+
For runtime tasks, token lookup tries the cloud `workspace.slug`, cloud `workspace.name`, `--project`, and `--workspace`. Use the project slug when you know it; adding the display name is also safe when the slug/name differ:
|
|
41
|
+
|
|
42
|
+
```yaml
|
|
43
|
+
# tokens:
|
|
44
|
+
flatkey-prod: gtm_xxxxxxxxxxxxxxxxxxxx
|
|
45
|
+
Flat Key: gtm_xxxxxxxxxxxxxxxxxxxx
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The credentials file must live under the same `HOME` used by the daemon process. If the daemon is launched by a service manager or SSH session under a different user, confirm with `11agents daemon status` and check that user has the file at `~/.11agents/credentials`. After changing credentials or upgrading the CLI, restart the daemon:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm install -g @11agents/cli@latest
|
|
52
|
+
11agents --version
|
|
53
|
+
11agents daemon stop
|
|
54
|
+
11agents daemon start --background
|
|
55
|
+
```
|
|
39
56
|
|
|
40
57
|
## Runtime Pool
|
|
41
58
|
|
package/package.json
CHANGED
package/src/commands/runtime.js
CHANGED
|
@@ -106,6 +106,27 @@ async function runWithDaemonRetry(label, operation, deps, retryState) {
|
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
async function runWithTaskRetry(label, operation, deps, { maxAttempts = 3 } = {}) {
|
|
110
|
+
let failures = 0
|
|
111
|
+
while (true) {
|
|
112
|
+
try {
|
|
113
|
+
return await operation()
|
|
114
|
+
} catch (error) {
|
|
115
|
+
failures += 1
|
|
116
|
+
if (failures >= maxAttempts) throw error
|
|
117
|
+
const delay = retryDelayMs(failures)
|
|
118
|
+
deps.log(JSON.stringify({
|
|
119
|
+
retrying: label,
|
|
120
|
+
error: errorMessage(error),
|
|
121
|
+
consecutive_failures: failures,
|
|
122
|
+
max_attempts: maxAttempts,
|
|
123
|
+
next_retry_ms: delay,
|
|
124
|
+
}, null, 2))
|
|
125
|
+
await deps.sleep(delay)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
109
130
|
export async function scanRuntime(flags = {}) {
|
|
110
131
|
const scan = await buildRuntimeScan({ env: scanEnvWithOverrides(flags), cliVersion: CLI_VERSION })
|
|
111
132
|
console.log(JSON.stringify(scan, null, 2))
|
|
@@ -183,7 +204,7 @@ async function syncRuntimeProjectMetadataBestEffort(flags, deps) {
|
|
|
183
204
|
|
|
184
205
|
function normalizeTaskCompletion(task, completion) {
|
|
185
206
|
const result = completion && typeof completion === 'object' ? completion : {}
|
|
186
|
-
|
|
207
|
+
const body = {
|
|
187
208
|
task_id: task.id,
|
|
188
209
|
runtime_id: task.runtime_id,
|
|
189
210
|
machine_key: task.runtime?.machine_key || '',
|
|
@@ -191,6 +212,26 @@ function normalizeTaskCompletion(task, completion) {
|
|
|
191
212
|
memory_delta: String(result.memory_delta || result.memoryDelta || ''),
|
|
192
213
|
status: result.status ? String(result.status) : null,
|
|
193
214
|
}
|
|
215
|
+
if (result.knowledge_snapshot || result.knowledgeSnapshot) {
|
|
216
|
+
body.knowledge_snapshot = result.knowledge_snapshot || result.knowledgeSnapshot
|
|
217
|
+
}
|
|
218
|
+
return body
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function extractJsonObject(text) {
|
|
222
|
+
const source = String(text || '').trim()
|
|
223
|
+
if (!source) return null
|
|
224
|
+
const fenced = source.match(/```(?:json)?\s*([\s\S]*?)```/i)
|
|
225
|
+
const candidates = fenced ? [fenced[1].trim(), source] : [source]
|
|
226
|
+
for (const candidate of candidates) {
|
|
227
|
+
try {
|
|
228
|
+
const parsed = JSON.parse(candidate)
|
|
229
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) return parsed
|
|
230
|
+
} catch {
|
|
231
|
+
// Try the next representation.
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return null
|
|
194
235
|
}
|
|
195
236
|
|
|
196
237
|
async function loadTaskHandler(handlerPath, deps) {
|
|
@@ -272,6 +313,16 @@ async function projectSyncToken(project, flags = {}, deps = {}) {
|
|
|
272
313
|
})
|
|
273
314
|
}
|
|
274
315
|
|
|
316
|
+
function projectTokenCandidatesForTask(task, flags = {}) {
|
|
317
|
+
return [
|
|
318
|
+
task.workspace?.slug,
|
|
319
|
+
task.workspace?.name,
|
|
320
|
+
task.workspace_slug,
|
|
321
|
+
flag(flags, 'project'),
|
|
322
|
+
flag(flags, 'workspace'),
|
|
323
|
+
].filter(Boolean)
|
|
324
|
+
}
|
|
325
|
+
|
|
275
326
|
async function writeFileIfMissing(filePath, content) {
|
|
276
327
|
try {
|
|
277
328
|
await writeFile(filePath, content, { flag: 'wx' })
|
|
@@ -447,7 +498,7 @@ async function syncDatabaseIfNeeded({ task, workdir, config, flags, deps }) {
|
|
|
447
498
|
if (!spec.syncRequired && localRevision === spec.cloudRevision) return { synced: false, cloud_revision: localRevision }
|
|
448
499
|
|
|
449
500
|
const project = projectSlugForTask(task)
|
|
450
|
-
const token = await projectSyncToken(
|
|
501
|
+
const token = await projectSyncToken(projectTokenCandidatesForTask(task, flags), flags, deps)
|
|
451
502
|
const syncConfig = { ...config, token }
|
|
452
503
|
const result = await deps.requestJson(`/api/projects/${project}/database/sync`, {
|
|
453
504
|
method: 'POST',
|
|
@@ -561,24 +612,18 @@ function buildCodexPrompt(task) {
|
|
|
561
612
|
source_url: deepOrganize.source_url,
|
|
562
613
|
source_type: deepOrganize.source_type || (deepOrganize.source_url.includes('github.com') ? 'github' : 'url'),
|
|
563
614
|
output_dir: task.execution_context?.workdir ? `${task.execution_context.workdir}/knowledge_base` : './knowledge_base',
|
|
564
|
-
callback: '
|
|
615
|
+
callback: 'Prefer the 11agents MCP knowledge_sync tool with mode=push. If MCP/local writes are unavailable, include a knowledge_snapshot object in the final task completion JSON so the platform writes cloud knowledge during task completion.',
|
|
565
616
|
}),
|
|
566
617
|
'',
|
|
567
|
-
'Build the knowledge base using
|
|
568
|
-
'-
|
|
569
|
-
'-
|
|
570
|
-
'-
|
|
571
|
-
'-
|
|
572
|
-
'-
|
|
573
|
-
'-
|
|
574
|
-
'-
|
|
575
|
-
'-
|
|
576
|
-
'- index.md should include Identity, Node Map, and Execution Instructions sections with wiki-style references to important files.',
|
|
577
|
-
'- CLAUDE.md should include the exact read order and output/update rules for runtime agents.',
|
|
578
|
-
'- If source_type is github, inspect README, docs, package manifests, examples, and source tree. Prefer concise durable explanations over raw dumps.',
|
|
579
|
-
'- If source_type is url, read the page and prefer /llms.txt or /llms-full.txt from that origin when available.',
|
|
580
|
-
'- Keep file paths stable and descriptive. Do not write temporary research into the durable wiki.',
|
|
581
|
-
'- When done, use MCP to push the local knowledge_base back to cloud.',
|
|
618
|
+
'Build the knowledge base using https://github.com/lewislulu/llm-wiki-skill:',
|
|
619
|
+
'- Install or inspect llm-wiki-skill when possible, then follow its compile/ingest/query/lint/audit workflow.',
|
|
620
|
+
'- Use MCP knowledge_sync mode=pull first when available so existing cloud knowledge is included.',
|
|
621
|
+
'- Use MCP knowledge_sync mode=push after organizing when available.',
|
|
622
|
+
'- If shell, apply_patch, local file writes, skill installation, or MCP push are unavailable, still produce the organized result as a knowledge_snapshot in the task completion payload.',
|
|
623
|
+
'- knowledge_snapshot must contain nodes and edges arrays compatible with 11agents knowledge sync. Each node should include id, kind, title, body, and local_path.',
|
|
624
|
+
'- When using the built-in Codex worker, finish with a JSON object or fenced ```json block containing comment, memory_delta, status, and knowledge_snapshot.',
|
|
625
|
+
'- Do not report blocked only because shell, apply_patch, local file writes, or MCP push are unavailable. Report blocked only if source research and a structured knowledge_snapshot are also impossible.',
|
|
626
|
+
'- Preserve useful existing knowledge, remove duplication, and make the wiki directly usable by future runtime agents.',
|
|
582
627
|
].join('\n') : '',
|
|
583
628
|
'',
|
|
584
629
|
'Work in this repository and make the needed changes. When finished, respond with a concise summary for the task thread.',
|
|
@@ -620,6 +665,17 @@ async function runCodex({ task, prompt, flags = {}, deps }) {
|
|
|
620
665
|
status: 'failed',
|
|
621
666
|
}
|
|
622
667
|
}
|
|
668
|
+
if (knowledgeDeepOrganizeSpec(task)) {
|
|
669
|
+
const structured = extractJsonObject(output)
|
|
670
|
+
if (structured?.knowledge_snapshot || structured?.knowledgeSnapshot) {
|
|
671
|
+
return {
|
|
672
|
+
...structured,
|
|
673
|
+
comment: String(structured.comment || structured.summary || output || `Codex completed task ${task.id}.`),
|
|
674
|
+
memory_delta: String(structured.memory_delta || structured.memoryDelta || `Codex completed task ${task.id}.`),
|
|
675
|
+
status: structured.status ? String(structured.status) : 'in_review',
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
623
679
|
return {
|
|
624
680
|
comment: output || `Codex completed task ${task.id}.`,
|
|
625
681
|
memory_delta: `Codex completed task ${task.id}.`,
|
|
@@ -677,51 +733,86 @@ async function claimAndRunRuntimeTasks(registration, flags, deps, handlerModule,
|
|
|
677
733
|
}
|
|
678
734
|
|
|
679
735
|
deps.log(JSON.stringify({ claimed: runtimeTask.id, runtime_id: runtime.id }, null, 2))
|
|
736
|
+
let completion = null
|
|
737
|
+
let executionContext = null
|
|
680
738
|
if (runtimeTask.workspace?.slug) {
|
|
681
|
-
const token = await projectSyncToken(runtimeTask
|
|
739
|
+
const token = await projectSyncToken(projectTokenCandidatesForTask(runtimeTask, flags), flags, deps)
|
|
682
740
|
const syncConfig = { ...config, token }
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
741
|
+
try {
|
|
742
|
+
await runWithTaskRetry('sync knowledge base', () => (
|
|
743
|
+
deps.syncKnowledge({
|
|
744
|
+
project: runtimeTask.workspace.slug,
|
|
745
|
+
mode: 'pull',
|
|
746
|
+
server: flags.server,
|
|
747
|
+
token,
|
|
748
|
+
}, {
|
|
749
|
+
requestJson: (apiPath, options = {}) => deps.requestJson(apiPath, { ...options, config: syncConfig }),
|
|
750
|
+
log: () => {},
|
|
751
|
+
})
|
|
752
|
+
), deps)
|
|
753
|
+
} catch (error) {
|
|
754
|
+
completion = {
|
|
755
|
+
comment: `Knowledge sync pull failed before task execution: ${errorMessage(error)}`,
|
|
756
|
+
status: 'failed',
|
|
757
|
+
}
|
|
758
|
+
}
|
|
694
759
|
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
760
|
+
if (!completion) {
|
|
761
|
+
executionContext = await prepareRuntimeTask(runtimeTask, flags, deps, config)
|
|
762
|
+
runtimeTask.execution_context = executionContext
|
|
763
|
+
try {
|
|
764
|
+
completion = await handlerModule.handleRuntimeTask(runtimeTask)
|
|
765
|
+
} catch (error) {
|
|
766
|
+
completion = {
|
|
767
|
+
comment: error instanceof Error ? error.message : String(error),
|
|
768
|
+
status: 'failed',
|
|
769
|
+
}
|
|
770
|
+
} finally {
|
|
771
|
+
await rm(executionContext.tmp_dir, { recursive: true, force: true })
|
|
704
772
|
}
|
|
705
|
-
} finally {
|
|
706
|
-
await rm(executionContext.tmp_dir, { recursive: true, force: true })
|
|
707
773
|
}
|
|
708
|
-
|
|
774
|
+
if (executionContext) {
|
|
775
|
+
await appendTaskMemoryDelta({ task: runtimeTask, completion, workdir: executionContext.workdir })
|
|
776
|
+
}
|
|
709
777
|
|
|
710
|
-
if (runtimeTask.workspace?.slug) {
|
|
711
|
-
const
|
|
712
|
-
const
|
|
778
|
+
if (executionContext && runtimeTask.workspace?.slug) {
|
|
779
|
+
const isDeepOrganize = Boolean(knowledgeDeepOrganizeSpec(runtimeTask))
|
|
780
|
+
const syncBack = isDeepOrganize ? deps.mcpKnowledgeSync : deps.syncKnowledge
|
|
781
|
+
const token = await projectSyncToken(projectTokenCandidatesForTask(runtimeTask, flags), flags, deps)
|
|
713
782
|
const syncConfig = { ...config, token }
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
}, {
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
783
|
+
const pushKnowledge = () => syncBack({
|
|
784
|
+
project: runtimeTask.workspace.slug,
|
|
785
|
+
mode: 'push',
|
|
786
|
+
server: flags.server,
|
|
787
|
+
token,
|
|
788
|
+
}, {
|
|
789
|
+
requestJson: (apiPath, options = {}) => deps.requestJson(apiPath, { ...options, config: syncConfig }),
|
|
790
|
+
log: () => {},
|
|
791
|
+
})
|
|
792
|
+
if (isDeepOrganize) {
|
|
793
|
+
try {
|
|
794
|
+
await pushKnowledge()
|
|
795
|
+
} catch (error) {
|
|
796
|
+
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.`
|
|
797
|
+
completion = {
|
|
798
|
+
...(completion && typeof completion === 'object' ? completion : {}),
|
|
799
|
+
comment: [String(completion?.comment || completion?.summary || '').trim(), message].filter(Boolean).join('\n\n'),
|
|
800
|
+
memory_delta: [String(completion?.memory_delta || completion?.memoryDelta || '').trim(), message].filter(Boolean).join('\n'),
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
} else {
|
|
804
|
+
try {
|
|
805
|
+
await runWithTaskRetry('sync knowledge base back to cloud', pushKnowledge, deps)
|
|
806
|
+
} catch (error) {
|
|
807
|
+
const message = `Knowledge sync push failed before task completion: ${errorMessage(error)}`
|
|
808
|
+
completion = {
|
|
809
|
+
...(completion && typeof completion === 'object' ? completion : {}),
|
|
810
|
+
comment: [String(completion?.comment || completion?.summary || '').trim(), message].filter(Boolean).join('\n\n'),
|
|
811
|
+
memory_delta: [String(completion?.memory_delta || completion?.memoryDelta || '').trim(), message].filter(Boolean).join('\n'),
|
|
812
|
+
status: 'failed',
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
725
816
|
}
|
|
726
817
|
|
|
727
818
|
const body = normalizeTaskCompletion(runtimeTask, completion)
|
package/src/credentials.js
CHANGED
|
@@ -63,8 +63,11 @@ export async function resolveProjectToken(project, {
|
|
|
63
63
|
if (token) return token
|
|
64
64
|
|
|
65
65
|
const credentials = await readCredentials({ homeDir })
|
|
66
|
-
const
|
|
67
|
-
|
|
66
|
+
const projects = Array.isArray(project) ? project : [project]
|
|
67
|
+
for (const candidate of projects) {
|
|
68
|
+
const projectToken = credentials.tokens[slugify(candidate)]
|
|
69
|
+
if (projectToken) return projectToken
|
|
70
|
+
}
|
|
68
71
|
|
|
69
72
|
return env.GTM_SWARM_TOKEN || fallbackToken || ''
|
|
70
73
|
}
|