@11agents/cli 0.1.1
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 +103 -0
- package/bin/11agents.js +135 -0
- package/bin/gtm-swarm.js +74 -0
- package/examples/voc-mcp-tool-call-batch.json +45 -0
- package/examples/x-agent-batch.json +54 -0
- package/examples/x-observation-job-result.json +24 -0
- package/package.json +37 -0
- package/specs/agent-json-contract.md +77 -0
- package/src/args.js +43 -0
- package/src/client.js +42 -0
- package/src/commands/node.js +54 -0
- package/src/commands/push.js +56 -0
- package/src/commands/runtime.js +353 -0
- package/src/daemon-process.js +92 -0
- package/src/info.js +62 -0
- package/src/runtime-scan.js +114 -0
- package/src/schema.js +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# 11agents CLI
|
|
2
|
+
|
|
3
|
+
Local CLI for connecting AI coding runtimes to the 11agents control plane.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @11agents/cli@latest
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The CLI requires Node.js 22 or newer.
|
|
12
|
+
|
|
13
|
+
## Configure
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
export GTM_WRITES_TOKEN="<control-plane-token>"
|
|
17
|
+
export ELEVENAGENTS_MACHINE="mac-mini-01"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The default control plane is `https://app.11agents.ai`. Set `ELEVENAGENTS_SERVER` only when targeting a local or custom deployment:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
export ELEVENAGENTS_SERVER="http://localhost:8082"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`ELEVENAGENTS_MACHINE` is optional. If omitted, the CLI uses the local hostname.
|
|
27
|
+
|
|
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
|
+
|
|
30
|
+
## Runtime Pool
|
|
31
|
+
|
|
32
|
+
Scan local AI runtimes:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
11agents runtime scan
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Register this machine and its detected runtimes:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
11agents runtime register
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Run the foreground daemon:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
11agents daemon start
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Run the daemon in the background:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
11agents daemon start --background
|
|
54
|
+
11agents daemon status
|
|
55
|
+
11agents daemon stop
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Background mode writes its pid to `~/.11agents/daemon.pid` and logs to `~/.11agents/daemon.log`.
|
|
59
|
+
|
|
60
|
+
Useful daemon options:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
11agents daemon start --heartbeat-interval 15 --scan-interval 60 --task-interval 15
|
|
64
|
+
11agents daemon start --handler ./worker.js
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The built-in task runner currently supports Codex tasks. A custom handler may export:
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
export async function handleRuntimeTask(task) {
|
|
71
|
+
return {
|
|
72
|
+
status: 'in_review',
|
|
73
|
+
comment: `Handled task ${task.id}`,
|
|
74
|
+
memory_delta: ''
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Telemetry Compatibility
|
|
80
|
+
|
|
81
|
+
The package still includes the original `gtm-swarm` binary for swarm telemetry compatibility.
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
export GTM_SWARM_SERVER="https://<your-11agents-platform>"
|
|
85
|
+
export GTM_SWARM_TOKEN="<workspace-swarm-token>"
|
|
86
|
+
export GTM_SWARM_WORKSPACE="flatkey"
|
|
87
|
+
export GTM_SWARM_AGENT="x-growth-agent"
|
|
88
|
+
export GTM_SWARM_NODE="mac-mini-01"
|
|
89
|
+
|
|
90
|
+
gtm-swarm validate examples/x-agent-batch.json
|
|
91
|
+
gtm-swarm push batch examples/x-agent-batch.json
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Publish
|
|
95
|
+
|
|
96
|
+
First public release should go out on the beta dist-tag:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
npm login
|
|
100
|
+
npm test
|
|
101
|
+
npm pack --dry-run --json
|
|
102
|
+
npm publish --tag beta
|
|
103
|
+
```
|
package/bin/11agents.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile } from 'node:fs/promises'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
import { parseArgs } from '../src/args.js'
|
|
5
|
+
import { runNode } from '../src/commands/node.js'
|
|
6
|
+
import { pushArtifact, pushBatch, pushObservation } from '../src/commands/push.js'
|
|
7
|
+
import { registerRuntime, scanRuntime, startRuntimeDaemon } from '../src/commands/runtime.js'
|
|
8
|
+
import { startBackgroundDaemon, statusBackgroundDaemon, stopBackgroundDaemon } from '../src/daemon-process.js'
|
|
9
|
+
import { getControlConfig } from '../src/client.js'
|
|
10
|
+
import { printStartupInfo } from '../src/info.js'
|
|
11
|
+
import { validateTelemetryBatch } from '../src/schema.js'
|
|
12
|
+
|
|
13
|
+
function usage() {
|
|
14
|
+
console.log(`11agents CLI
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
11agents help
|
|
18
|
+
11agents runtime scan
|
|
19
|
+
11agents runtime register [--server <url>] [--token <token>] [--machine <key>]
|
|
20
|
+
11agents daemon start [--server <url>] [--token <token>] [--machine <key>] [--task-interval <seconds>] [--background]
|
|
21
|
+
11agents daemon status
|
|
22
|
+
11agents daemon stop
|
|
23
|
+
11agents daemon start --handler ./worker.js # optional custom worker override
|
|
24
|
+
11agents validate <file>
|
|
25
|
+
11agents push batch <file>
|
|
26
|
+
11agents push artifact --workspace <slug> --agent <key> --platform x --type post --external-id <id>
|
|
27
|
+
11agents push observation --workspace <slug> --agent <key> --platform x --type post --external-id <id> --metric views=123
|
|
28
|
+
11agents node run --workspace <slug> --agent <key> --node <node-id> --handler ./collect-x.js [--once]
|
|
29
|
+
|
|
30
|
+
Environment:
|
|
31
|
+
ELEVENAGENTS_SERVER default https://app.11agents.ai
|
|
32
|
+
GTM_SWARM_SERVER compatibility fallback for server URL
|
|
33
|
+
GTM_WRITES_TOKEN control-plane token for runtime registration
|
|
34
|
+
ELEVENAGENTS_MACHINE stable machine key, defaults to hostname
|
|
35
|
+
GTM_SWARM_TOKEN project swarm token for push/node commands`)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function main() {
|
|
39
|
+
const argv = process.argv.slice(2)
|
|
40
|
+
const { positional, flags } = parseArgs(argv)
|
|
41
|
+
const [command, subcommand, target] = positional
|
|
42
|
+
|
|
43
|
+
if (!command || command === 'help') {
|
|
44
|
+
usage()
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
await printStartupInfo({
|
|
49
|
+
server: getControlConfig({ server: flags.server }).server,
|
|
50
|
+
quiet: Boolean(flags.quiet),
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
if (command === 'runtime' && subcommand === 'scan') {
|
|
54
|
+
await scanRuntime(flags)
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (command === 'runtime' && subcommand === 'register') {
|
|
59
|
+
await registerRuntime(flags)
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (command === 'daemon' && subcommand === 'start') {
|
|
64
|
+
if (flags.background) {
|
|
65
|
+
const result = await startBackgroundDaemon({
|
|
66
|
+
argv,
|
|
67
|
+
scriptPath: fileURLToPath(import.meta.url),
|
|
68
|
+
})
|
|
69
|
+
if (result.alreadyRunning) {
|
|
70
|
+
console.log(`11agents daemon already running with pid ${result.pid}`)
|
|
71
|
+
} else {
|
|
72
|
+
console.log(`11agents daemon started with pid ${result.pid}`)
|
|
73
|
+
}
|
|
74
|
+
console.log(`log: ${result.logPath}`)
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
await startRuntimeDaemon(flags)
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (command === 'daemon' && subcommand === 'status') {
|
|
82
|
+
const status = await statusBackgroundDaemon()
|
|
83
|
+
if (status.running) console.log(`11agents daemon running with pid ${status.pid}`)
|
|
84
|
+
else if (status.stale) console.log(`11agents daemon not running; stale pid ${status.pid}`)
|
|
85
|
+
else console.log('11agents daemon not running')
|
|
86
|
+
console.log(`log: ${status.logPath}`)
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (command === 'daemon' && subcommand === 'stop') {
|
|
91
|
+
const result = await stopBackgroundDaemon()
|
|
92
|
+
if (result.stopped) console.log(`11agents daemon stopped pid ${result.pid}`)
|
|
93
|
+
else console.log('11agents daemon not running')
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (command === 'validate') {
|
|
98
|
+
const file = subcommand
|
|
99
|
+
if (!file) throw new Error('validate requires a file')
|
|
100
|
+
const json = JSON.parse(await readFile(file, 'utf-8'))
|
|
101
|
+
const result = validateTelemetryBatch(json)
|
|
102
|
+
if (!result.ok) throw new Error(result.error)
|
|
103
|
+
console.log('valid')
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (command === 'push' && subcommand === 'batch') {
|
|
108
|
+
if (!target) throw new Error('push batch requires a file')
|
|
109
|
+
await pushBatch(target)
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (command === 'push' && subcommand === 'artifact') {
|
|
114
|
+
await pushArtifact(flags)
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (command === 'push' && subcommand === 'observation') {
|
|
119
|
+
await pushObservation(flags)
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (command === 'node' && subcommand === 'run') {
|
|
124
|
+
await runNode(flags)
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
usage()
|
|
129
|
+
process.exitCode = 1
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
main().catch(error => {
|
|
133
|
+
console.error(error instanceof Error ? error.message : String(error))
|
|
134
|
+
process.exitCode = 1
|
|
135
|
+
})
|
package/bin/gtm-swarm.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile } from 'node:fs/promises'
|
|
3
|
+
import { parseArgs } from '../src/args.js'
|
|
4
|
+
import { pushArtifact, pushBatch, pushObservation } from '../src/commands/push.js'
|
|
5
|
+
import { runNode } from '../src/commands/node.js'
|
|
6
|
+
import { validateTelemetryBatch } from '../src/schema.js'
|
|
7
|
+
|
|
8
|
+
function usage() {
|
|
9
|
+
console.log(`GTM Swarm CLI
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
gtm-swarm help
|
|
13
|
+
gtm-swarm validate <file>
|
|
14
|
+
gtm-swarm push batch <file>
|
|
15
|
+
gtm-swarm push artifact --workspace <slug> --agent <key> --platform x --type post --external-id <id> [--url <url>] [--body <text>]
|
|
16
|
+
gtm-swarm push observation --workspace <slug> --agent <key> --platform x --type post --external-id <id> --metric views=123 --metric replies=4
|
|
17
|
+
gtm-swarm node run --workspace <slug> --agent <key> --node <node-id> --handler ./collect-x.js [--once]
|
|
18
|
+
|
|
19
|
+
Environment:
|
|
20
|
+
GTM_SWARM_SERVER default https://app.11agents.ai
|
|
21
|
+
GTM_SWARM_TOKEN bearer token
|
|
22
|
+
GTM_SWARM_WORKSPACE default workspace
|
|
23
|
+
GTM_SWARM_AGENT default agent key
|
|
24
|
+
GTM_SWARM_NODE default node id`)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function main() {
|
|
28
|
+
const { positional, flags } = parseArgs(process.argv.slice(2))
|
|
29
|
+
const [command, subcommand, target] = positional
|
|
30
|
+
|
|
31
|
+
if (!command || command === 'help') {
|
|
32
|
+
usage()
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (command === 'validate') {
|
|
37
|
+
const file = subcommand
|
|
38
|
+
if (!file) throw new Error('validate requires a file')
|
|
39
|
+
const json = JSON.parse(await readFile(file, 'utf-8'))
|
|
40
|
+
const result = validateTelemetryBatch(json)
|
|
41
|
+
if (!result.ok) throw new Error(result.error)
|
|
42
|
+
console.log('valid')
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (command === 'push' && subcommand === 'batch') {
|
|
47
|
+
if (!target) throw new Error('push batch requires a file')
|
|
48
|
+
await pushBatch(target)
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (command === 'push' && subcommand === 'artifact') {
|
|
53
|
+
await pushArtifact(flags)
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (command === 'push' && subcommand === 'observation') {
|
|
58
|
+
await pushObservation(flags)
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (command === 'node' && subcommand === 'run') {
|
|
63
|
+
await runNode(flags)
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
usage()
|
|
68
|
+
process.exitCode = 1
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
main().catch(error => {
|
|
72
|
+
console.error(error instanceof Error ? error.message : String(error))
|
|
73
|
+
process.exitCode = 1
|
|
74
|
+
})
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": "swarm.telemetry.v1",
|
|
3
|
+
"workspace": "voc-ai",
|
|
4
|
+
"agent_key": "voc-amazon-reviews-mcp",
|
|
5
|
+
"node_id": "vercel-prod",
|
|
6
|
+
"sent_at": "2026-05-25T10:00:02Z",
|
|
7
|
+
"artifacts": [
|
|
8
|
+
{
|
|
9
|
+
"platform": "mcp",
|
|
10
|
+
"artifact_type": "mcp_tool_call",
|
|
11
|
+
"external_id": "1766656800000-client_abc123-fetch_reviews-a8f21c",
|
|
12
|
+
"title": "fetch_reviews ok",
|
|
13
|
+
"created_at": "2026-05-25T10:00:00Z",
|
|
14
|
+
"payload": {
|
|
15
|
+
"service_name": "voc-amazon-reviews-mcp",
|
|
16
|
+
"metric_name": "mcp_tool_calls_total",
|
|
17
|
+
"tool": "fetch_reviews",
|
|
18
|
+
"status": "ok",
|
|
19
|
+
"client": "claude-code",
|
|
20
|
+
"error_type": "",
|
|
21
|
+
"source_catalog": "amazon-us",
|
|
22
|
+
"client_instance_id": "client_abc123",
|
|
23
|
+
"business_success": true,
|
|
24
|
+
"route": "POST /mcp",
|
|
25
|
+
"http_status": 200
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
"observations": [
|
|
30
|
+
{
|
|
31
|
+
"platform": "mcp",
|
|
32
|
+
"artifact_type": "mcp_tool_call",
|
|
33
|
+
"external_id": "1766656800000-client_abc123-fetch_reviews-a8f21c",
|
|
34
|
+
"observed_at": "2026-05-25T10:00:02Z",
|
|
35
|
+
"metrics": {
|
|
36
|
+
"calls": 1,
|
|
37
|
+
"latency_ms": 842,
|
|
38
|
+
"business_success": 1,
|
|
39
|
+
"http_2xx": 1,
|
|
40
|
+
"http_4xx": 0,
|
|
41
|
+
"http_5xx": 0
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": "swarm.telemetry.v1",
|
|
3
|
+
"workspace": "flatkey",
|
|
4
|
+
"agent_key": "x-growth-agent",
|
|
5
|
+
"node_id": "mac-mini-01",
|
|
6
|
+
"sent_at": "2026-05-25T09:30:00Z",
|
|
7
|
+
"artifacts": [
|
|
8
|
+
{
|
|
9
|
+
"platform": "x",
|
|
10
|
+
"artifact_type": "post",
|
|
11
|
+
"external_id": "1794312345678900000",
|
|
12
|
+
"url": "https://x.com/acme/status/1794312345678900000",
|
|
13
|
+
"body": "We shipped a new API key flow today.",
|
|
14
|
+
"created_at": "2026-05-25T08:10:00Z",
|
|
15
|
+
"payload": {
|
|
16
|
+
"account": "@acme"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"platform": "x",
|
|
21
|
+
"artifact_type": "reply",
|
|
22
|
+
"external_id": "1794312345678900001",
|
|
23
|
+
"url": "https://x.com/acme/status/1794312345678900001",
|
|
24
|
+
"body": "Thanks for the feedback. We added this to the roadmap.",
|
|
25
|
+
"created_at": "2026-05-25T08:12:00Z",
|
|
26
|
+
"payload": {
|
|
27
|
+
"account": "@acme",
|
|
28
|
+
"reply_to_external_id": "1794300000000000000"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"observations": [
|
|
33
|
+
{
|
|
34
|
+
"platform": "x",
|
|
35
|
+
"artifact_type": "post",
|
|
36
|
+
"external_id": "1794312345678900000",
|
|
37
|
+
"observed_at": "2026-05-25T09:25:00Z",
|
|
38
|
+
"metrics": {
|
|
39
|
+
"views": 1834,
|
|
40
|
+
"replies": 12
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"platform": "x",
|
|
45
|
+
"artifact_type": "reply",
|
|
46
|
+
"external_id": "1794312345678900001",
|
|
47
|
+
"observed_at": "2026-05-25T09:25:00Z",
|
|
48
|
+
"metrics": {
|
|
49
|
+
"views": 321,
|
|
50
|
+
"replies": 1
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"status": "completed",
|
|
3
|
+
"summary": "Collected 1 X post observation.",
|
|
4
|
+
"batch": {
|
|
5
|
+
"schema_version": "swarm.telemetry.v1",
|
|
6
|
+
"workspace": "flatkey",
|
|
7
|
+
"agent_key": "x-growth-agent",
|
|
8
|
+
"node_id": "mac-mini-01",
|
|
9
|
+
"sent_at": "2026-05-25T09:34:00Z",
|
|
10
|
+
"artifacts": [],
|
|
11
|
+
"observations": [
|
|
12
|
+
{
|
|
13
|
+
"platform": "x",
|
|
14
|
+
"artifact_type": "post",
|
|
15
|
+
"external_id": "1794312345678900000",
|
|
16
|
+
"observed_at": "2026-05-25T09:33:30Z",
|
|
17
|
+
"metrics": {
|
|
18
|
+
"views": 1901,
|
|
19
|
+
"replies": 13
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@11agents/cli",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "11agents local runtime and telemetry CLI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"11agents": "bin/11agents.js",
|
|
8
|
+
"gtm-swarm": "bin/gtm-swarm.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin/",
|
|
12
|
+
"src/",
|
|
13
|
+
"!src/**/*.test.js",
|
|
14
|
+
"examples/",
|
|
15
|
+
"specs/",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"test": "node --test src/*.test.js src/commands/*.test.js"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": "22.x"
|
|
23
|
+
},
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/11Agents/11agents-ai.git",
|
|
27
|
+
"directory": "gtm-swarm-cli"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://11agents.ai",
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/11Agents/11agents-ai/issues"
|
|
32
|
+
},
|
|
33
|
+
"license": "UNLICENSED",
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Agent JSON Contract
|
|
2
|
+
|
|
3
|
+
This is the short document an AI agent should read before pushing data to GTM Swarm.
|
|
4
|
+
|
|
5
|
+
## Rules
|
|
6
|
+
|
|
7
|
+
- Use `schema_version: "swarm.telemetry.v1"`.
|
|
8
|
+
- Use ISO 8601 timestamps with timezone, preferably UTC `Z`.
|
|
9
|
+
- `workspace` is the GTM workspace slug.
|
|
10
|
+
- `agent_key` is a stable machine-readable agent name.
|
|
11
|
+
- `node_id` is a stable machine name.
|
|
12
|
+
- `platform` and `artifact_type` are lowercase.
|
|
13
|
+
- `external_id` is required and should match the source platform ID.
|
|
14
|
+
- Metric values must be numbers. Put raw strings and nested metadata in `payload`.
|
|
15
|
+
|
|
16
|
+
## Batch
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"schema_version": "swarm.telemetry.v1",
|
|
21
|
+
"workspace": "flatkey",
|
|
22
|
+
"agent_key": "x-growth-agent",
|
|
23
|
+
"node_id": "mac-mini-01",
|
|
24
|
+
"sent_at": "2026-05-25T09:30:00Z",
|
|
25
|
+
"artifacts": [],
|
|
26
|
+
"observations": []
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Artifact
|
|
31
|
+
|
|
32
|
+
Use an artifact when the agent created or discovered a durable object.
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"platform": "x",
|
|
37
|
+
"artifact_type": "post",
|
|
38
|
+
"external_id": "1794312345678900000",
|
|
39
|
+
"url": "https://x.com/acme/status/1794312345678900000",
|
|
40
|
+
"body": "We shipped a new API key flow today.",
|
|
41
|
+
"created_at": "2026-05-25T08:10:00Z",
|
|
42
|
+
"payload": {
|
|
43
|
+
"account": "@acme"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Observation
|
|
49
|
+
|
|
50
|
+
Use an observation when the agent collects current metrics for an artifact.
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"platform": "x",
|
|
55
|
+
"artifact_type": "post",
|
|
56
|
+
"external_id": "1794312345678900000",
|
|
57
|
+
"observed_at": "2026-05-25T09:25:00Z",
|
|
58
|
+
"metrics": {
|
|
59
|
+
"views": 1834,
|
|
60
|
+
"replies": 12
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Push
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
export GTM_SWARM_SERVER="https://gtm.shulex.com"
|
|
69
|
+
export GTM_SWARM_TOKEN="..."
|
|
70
|
+
|
|
71
|
+
gtm-swarm push batch ./result.json
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
For examples, see:
|
|
75
|
+
|
|
76
|
+
- `examples/x-agent-batch.json`
|
|
77
|
+
- `examples/x-observation-job-result.json`
|
package/src/args.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export function parseArgs(argv) {
|
|
2
|
+
const positional = []
|
|
3
|
+
const flags = {}
|
|
4
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
5
|
+
const arg = argv[i]
|
|
6
|
+
if (!arg.startsWith('--')) {
|
|
7
|
+
positional.push(arg)
|
|
8
|
+
continue
|
|
9
|
+
}
|
|
10
|
+
const key = arg.slice(2)
|
|
11
|
+
const next = argv[i + 1]
|
|
12
|
+
if (!next || next.startsWith('--')) {
|
|
13
|
+
flags[key] = true
|
|
14
|
+
continue
|
|
15
|
+
}
|
|
16
|
+
if (flags[key] === undefined) flags[key] = next
|
|
17
|
+
else if (Array.isArray(flags[key])) flags[key].push(next)
|
|
18
|
+
else flags[key] = [flags[key], next]
|
|
19
|
+
i += 1
|
|
20
|
+
}
|
|
21
|
+
return { positional, flags }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function flag(flags, name, fallback = '') {
|
|
25
|
+
return flags[name] ?? fallback
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function listFlag(flags, name) {
|
|
29
|
+
const value = flags[name]
|
|
30
|
+
if (value === undefined) return []
|
|
31
|
+
return Array.isArray(value) ? value : [value]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function parseMetricFlags(values) {
|
|
35
|
+
const metrics = {}
|
|
36
|
+
for (const item of values) {
|
|
37
|
+
const [key, raw] = String(item).split('=')
|
|
38
|
+
const value = Number(raw)
|
|
39
|
+
if (!key || !Number.isFinite(value)) throw new Error(`invalid metric: ${item}`)
|
|
40
|
+
metrics[key] = value
|
|
41
|
+
}
|
|
42
|
+
return metrics
|
|
43
|
+
}
|
package/src/client.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export const DEFAULT_SERVER = 'https://app.11agents.ai'
|
|
2
|
+
|
|
3
|
+
export function getConfig(overrides = {}) {
|
|
4
|
+
const env = overrides.env || process.env
|
|
5
|
+
return {
|
|
6
|
+
server: overrides.server || env.ELEVENAGENTS_SERVER || env['11AGENTS_SERVER'] || env.GTM_SWARM_SERVER || DEFAULT_SERVER,
|
|
7
|
+
token: overrides.token || env.GTM_SWARM_TOKEN || '',
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getControlConfig(overrides = {}) {
|
|
12
|
+
const env = overrides.env || process.env
|
|
13
|
+
return {
|
|
14
|
+
server: overrides.server || env.ELEVENAGENTS_SERVER || env['11AGENTS_SERVER'] || env.GTM_SWARM_SERVER || DEFAULT_SERVER,
|
|
15
|
+
token: overrides.token || env.GTM_WRITES_TOKEN || '',
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function requestJson(path, { method = 'GET', body = null, config = getConfig() } = {}) {
|
|
20
|
+
const headers = { 'content-type': 'application/json' }
|
|
21
|
+
if (config.token) headers.authorization = `Bearer ${config.token}`
|
|
22
|
+
const response = await fetch(`${config.server.replace(/\/$/, '')}${path}`, {
|
|
23
|
+
method,
|
|
24
|
+
headers,
|
|
25
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
26
|
+
})
|
|
27
|
+
const data = await response.json().catch(() => ({}))
|
|
28
|
+
if (!response.ok) {
|
|
29
|
+
const error = new Error(data.error || `HTTP ${response.status}`)
|
|
30
|
+
error.status = response.status
|
|
31
|
+
throw error
|
|
32
|
+
}
|
|
33
|
+
return data
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function encodeQuery(params) {
|
|
37
|
+
const q = new URLSearchParams()
|
|
38
|
+
for (const [key, value] of Object.entries(params)) {
|
|
39
|
+
if (value !== undefined && value !== null && value !== '') q.set(key, String(value))
|
|
40
|
+
}
|
|
41
|
+
return q.toString()
|
|
42
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { pathToFileURL } from 'node:url'
|
|
2
|
+
import { flag } from '../args.js'
|
|
3
|
+
import { encodeQuery, requestJson } from '../client.js'
|
|
4
|
+
|
|
5
|
+
function sleep(ms) {
|
|
6
|
+
return new Promise(resolve => setTimeout(resolve, ms))
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function runNode(flags) {
|
|
10
|
+
const workspace = flag(flags, 'workspace', process.env.GTM_SWARM_WORKSPACE || '')
|
|
11
|
+
const agent_key = flag(flags, 'agent', process.env.GTM_SWARM_AGENT || '')
|
|
12
|
+
const node_id = flag(flags, 'node', process.env.GTM_SWARM_NODE || 'local')
|
|
13
|
+
const handlerPath = flag(flags, 'handler')
|
|
14
|
+
const once = Boolean(flags.once)
|
|
15
|
+
const intervalMs = Number(flag(flags, 'interval-ms', '5000'))
|
|
16
|
+
|
|
17
|
+
if (!workspace) throw new Error('--workspace or GTM_SWARM_WORKSPACE required')
|
|
18
|
+
if (!agent_key) throw new Error('--agent or GTM_SWARM_AGENT required')
|
|
19
|
+
if (!handlerPath) throw new Error('--handler required')
|
|
20
|
+
|
|
21
|
+
const handlerModule = await import(pathToFileURL(handlerPath).href)
|
|
22
|
+
if (typeof handlerModule.handleJob !== 'function') throw new Error('handler must export async function handleJob(job)')
|
|
23
|
+
|
|
24
|
+
while (true) {
|
|
25
|
+
const qs = encodeQuery({ workspace, agent_key, node_id })
|
|
26
|
+
const lease = await requestJson(`/api/swarm/jobs/lease?${qs}`)
|
|
27
|
+
if (!lease.job) {
|
|
28
|
+
if (once) {
|
|
29
|
+
console.log(JSON.stringify({ job: null }, null, 2))
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
await sleep(intervalMs)
|
|
33
|
+
continue
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.log(JSON.stringify({ leased: lease.job.id, kind: lease.job.kind }, null, 2))
|
|
37
|
+
let completion
|
|
38
|
+
try {
|
|
39
|
+
completion = await handlerModule.handleJob(lease.job)
|
|
40
|
+
} catch (error) {
|
|
41
|
+
completion = {
|
|
42
|
+
status: 'failed',
|
|
43
|
+
summary: error instanceof Error ? error.message : String(error),
|
|
44
|
+
error: 'handler_error',
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const result = await requestJson(`/api/swarm/jobs/${lease.job.id}/complete`, {
|
|
48
|
+
method: 'POST',
|
|
49
|
+
body: completion,
|
|
50
|
+
})
|
|
51
|
+
console.log(JSON.stringify(result, null, 2))
|
|
52
|
+
if (once) return
|
|
53
|
+
}
|
|
54
|
+
}
|