@11agents/cli 0.1.4 → 0.1.5
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 +11 -1
- package/bin/11agents.js +1 -0
- package/package.json +1 -1
- package/src/commands/runtime.js +22 -5
- package/src/credentials.js +70 -0
- package/src/mcp.js +7 -2
package/README.md
CHANGED
|
@@ -27,6 +27,16 @@ 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 and knowledge-base sync tokens can be stored in `~/.11agents/credentials`:
|
|
31
|
+
|
|
32
|
+
```yaml
|
|
33
|
+
# tokens:
|
|
34
|
+
flatkey: gtm_xxxxxxxxxxxxxxxxxxxx
|
|
35
|
+
voc-ai: gtm_yyyyyyyyyyyyyyyyyyyy
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
When syncing a project knowledge base, 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
|
+
|
|
30
40
|
## Runtime Pool
|
|
31
41
|
|
|
32
42
|
Scan local AI runtimes:
|
|
@@ -95,7 +105,7 @@ Run the local MCP server over stdio:
|
|
|
95
105
|
11agents mcp start
|
|
96
106
|
```
|
|
97
107
|
|
|
98
|
-
Configure your MCP client to run that command
|
|
108
|
+
Configure your MCP client to run that command. The server automatically reads matching project tokens from `~/.11agents/credentials`; a tool-call token can still be passed explicitly when needed. The server exposes:
|
|
99
109
|
|
|
100
110
|
- `knowledge_sync` — pull/push the project knowledge base between cloud and `~/.11agents/<project>/knowledge_base/`.
|
|
101
111
|
|
package/bin/11agents.js
CHANGED
|
@@ -38,6 +38,7 @@ Environment:
|
|
|
38
38
|
GTM_WRITES_TOKEN control-plane token for runtime registration
|
|
39
39
|
ELEVENAGENTS_MACHINE stable machine key, defaults to hostname
|
|
40
40
|
GTM_SWARM_TOKEN project swarm token for push/node commands
|
|
41
|
+
~/.11agents/credentials project token map for MCP knowledge sync
|
|
41
42
|
|
|
42
43
|
Runtime task workspace:
|
|
43
44
|
Daemon project headquarters live under ~/.11agents/<project>/.
|
package/package.json
CHANGED
package/src/commands/runtime.js
CHANGED
|
@@ -9,6 +9,7 @@ import { pathToFileURL } from 'node:url'
|
|
|
9
9
|
import { fileURLToPath } from 'node:url'
|
|
10
10
|
import { flag } from '../args.js'
|
|
11
11
|
import { getControlConfig, requestJson } from '../client.js'
|
|
12
|
+
import { resolveProjectToken } from '../credentials.js'
|
|
12
13
|
import { syncKnowledge } from './knowledge.js'
|
|
13
14
|
import { buildRuntimeScan } from '../runtime-scan.js'
|
|
14
15
|
import { handleMcpRequest } from '../mcp.js'
|
|
@@ -206,6 +207,11 @@ function compactJson(value) {
|
|
|
206
207
|
}
|
|
207
208
|
|
|
208
209
|
async function mcpKnowledgeSync(flags = {}, deps = {}) {
|
|
210
|
+
const token = await resolveProjectToken(flags.project, {
|
|
211
|
+
homeDir: deps.homeDir,
|
|
212
|
+
token: flags.projectToken,
|
|
213
|
+
fallbackToken: flags.token,
|
|
214
|
+
})
|
|
209
215
|
const result = await handleMcpRequest({
|
|
210
216
|
method: 'tools/call',
|
|
211
217
|
params: {
|
|
@@ -214,7 +220,7 @@ async function mcpKnowledgeSync(flags = {}, deps = {}) {
|
|
|
214
220
|
project: flags.project,
|
|
215
221
|
mode: flags.mode,
|
|
216
222
|
server: flags.server,
|
|
217
|
-
token
|
|
223
|
+
token,
|
|
218
224
|
},
|
|
219
225
|
},
|
|
220
226
|
}, deps)
|
|
@@ -259,6 +265,13 @@ function projectDirForTask(task, flags = {}, deps) {
|
|
|
259
265
|
return buildRuntimeProjectDir(projectSlugForTask(task, flags), { homeDir: deps.homeDir })
|
|
260
266
|
}
|
|
261
267
|
|
|
268
|
+
async function projectSyncToken(project, flags = {}, deps = {}) {
|
|
269
|
+
return resolveProjectToken(project, {
|
|
270
|
+
homeDir: deps.homeDir,
|
|
271
|
+
fallbackToken: flags.token,
|
|
272
|
+
})
|
|
273
|
+
}
|
|
274
|
+
|
|
262
275
|
async function writeFileIfMissing(filePath, content) {
|
|
263
276
|
try {
|
|
264
277
|
await writeFile(filePath, content, { flag: 'wx' })
|
|
@@ -663,14 +676,16 @@ async function claimAndRunRuntimeTasks(registration, flags, deps, handlerModule,
|
|
|
663
676
|
|
|
664
677
|
deps.log(JSON.stringify({ claimed: runtimeTask.id, runtime_id: runtime.id }, null, 2))
|
|
665
678
|
if (runtimeTask.workspace?.slug) {
|
|
679
|
+
const token = await projectSyncToken(runtimeTask.workspace.slug, flags, deps)
|
|
680
|
+
const syncConfig = { ...config, token }
|
|
666
681
|
await runWithDaemonRetry('sync knowledge base', () => (
|
|
667
682
|
deps.syncKnowledge({
|
|
668
683
|
project: runtimeTask.workspace.slug,
|
|
669
684
|
mode: 'pull',
|
|
670
685
|
server: flags.server,
|
|
671
|
-
token
|
|
686
|
+
token,
|
|
672
687
|
}, {
|
|
673
|
-
requestJson: (apiPath, options = {}) => deps.requestJson(apiPath, { ...options, config }),
|
|
688
|
+
requestJson: (apiPath, options = {}) => deps.requestJson(apiPath, { ...options, config: syncConfig }),
|
|
674
689
|
log: () => {},
|
|
675
690
|
})
|
|
676
691
|
), deps, retryState)
|
|
@@ -692,14 +707,16 @@ async function claimAndRunRuntimeTasks(registration, flags, deps, handlerModule,
|
|
|
692
707
|
|
|
693
708
|
if (runtimeTask.workspace?.slug) {
|
|
694
709
|
const syncBack = knowledgeDeepOrganizeSpec(runtimeTask) ? deps.mcpKnowledgeSync : deps.syncKnowledge
|
|
710
|
+
const token = await projectSyncToken(runtimeTask.workspace.slug, flags, deps)
|
|
711
|
+
const syncConfig = { ...config, token }
|
|
695
712
|
await runWithDaemonRetry('sync knowledge base back to cloud', () => (
|
|
696
713
|
syncBack({
|
|
697
714
|
project: runtimeTask.workspace.slug,
|
|
698
715
|
mode: 'push',
|
|
699
716
|
server: flags.server,
|
|
700
|
-
token
|
|
717
|
+
token,
|
|
701
718
|
}, {
|
|
702
|
-
requestJson: (apiPath, options = {}) => deps.requestJson(apiPath, { ...options, config }),
|
|
719
|
+
requestJson: (apiPath, options = {}) => deps.requestJson(apiPath, { ...options, config: syncConfig }),
|
|
703
720
|
log: () => {},
|
|
704
721
|
})
|
|
705
722
|
), deps, retryState)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises'
|
|
2
|
+
import os from 'node:os'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
|
|
5
|
+
function slugify(value) {
|
|
6
|
+
return String(value || 'project')
|
|
7
|
+
.trim()
|
|
8
|
+
.toLowerCase()
|
|
9
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
10
|
+
.replace(/^-|-$/g, '') || 'project'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function cleanHeader(value) {
|
|
14
|
+
return String(value || '').trim().replace(/^#\s*/, '').trim()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function parseCredentials(content = '') {
|
|
18
|
+
const tokens = {}
|
|
19
|
+
let inTokens = false
|
|
20
|
+
|
|
21
|
+
for (const rawLine of String(content).split(/\r?\n/)) {
|
|
22
|
+
const line = rawLine.trim()
|
|
23
|
+
if (!line) continue
|
|
24
|
+
|
|
25
|
+
const header = cleanHeader(line)
|
|
26
|
+
if (/^tokens\s*:\s*$/.test(header)) {
|
|
27
|
+
inTokens = true
|
|
28
|
+
continue
|
|
29
|
+
}
|
|
30
|
+
if (line.startsWith('#')) continue
|
|
31
|
+
|
|
32
|
+
const match = line.match(/^([^:#]+):\s*(.+)$/)
|
|
33
|
+
if (!match) {
|
|
34
|
+
inTokens = false
|
|
35
|
+
continue
|
|
36
|
+
}
|
|
37
|
+
if (!inTokens && Object.keys(tokens).length === 0) inTokens = true
|
|
38
|
+
if (!inTokens) continue
|
|
39
|
+
|
|
40
|
+
const project = slugify(match[1])
|
|
41
|
+
const token = match[2].trim()
|
|
42
|
+
if (project && token) tokens[project] = token
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return { tokens }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function readCredentials({ homeDir = os.homedir() } = {}) {
|
|
49
|
+
try {
|
|
50
|
+
return parseCredentials(await readFile(path.join(homeDir, '.11agents', 'credentials'), 'utf8'))
|
|
51
|
+
} catch (error) {
|
|
52
|
+
if (error?.code === 'ENOENT') return { tokens: {} }
|
|
53
|
+
throw error
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function resolveProjectToken(project, {
|
|
58
|
+
homeDir = os.homedir(),
|
|
59
|
+
token = '',
|
|
60
|
+
fallbackToken = '',
|
|
61
|
+
env = process.env,
|
|
62
|
+
} = {}) {
|
|
63
|
+
if (token) return token
|
|
64
|
+
|
|
65
|
+
const credentials = await readCredentials({ homeDir })
|
|
66
|
+
const projectToken = credentials.tokens[slugify(project)]
|
|
67
|
+
if (projectToken) return projectToken
|
|
68
|
+
|
|
69
|
+
return env.GTM_SWARM_TOKEN || fallbackToken || ''
|
|
70
|
+
}
|
package/src/mcp.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { syncKnowledge } from './commands/knowledge.js'
|
|
2
2
|
import { requestJson } from './client.js'
|
|
3
|
+
import { resolveProjectToken } from './credentials.js'
|
|
3
4
|
|
|
4
5
|
const TOOLS = [
|
|
5
6
|
{
|
|
@@ -45,16 +46,20 @@ export async function handleMcpRequest(request, deps = {}) {
|
|
|
45
46
|
const name = request?.params?.name
|
|
46
47
|
const args = toolArgs(request)
|
|
47
48
|
if (name === 'knowledge_sync') {
|
|
49
|
+
const token = await resolveProjectToken(args.project, {
|
|
50
|
+
homeDir: deps.homeDir,
|
|
51
|
+
token: args.token,
|
|
52
|
+
})
|
|
48
53
|
const result = await syncKnowledge({
|
|
49
54
|
project: args.project,
|
|
50
55
|
mode: args.mode,
|
|
51
56
|
server: args.server,
|
|
52
|
-
token
|
|
57
|
+
token,
|
|
53
58
|
}, {
|
|
54
59
|
...deps,
|
|
55
60
|
requestJson: (apiPath, options = {}) => (deps.requestJson || requestJson)(apiPath, {
|
|
56
61
|
...options,
|
|
57
|
-
config: { token
|
|
62
|
+
config: { token, server: args.server || 'https://app.11agents.ai' },
|
|
58
63
|
}),
|
|
59
64
|
})
|
|
60
65
|
return textResult(result)
|