a2acalling 0.3.6 โ 0.4.0
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/SKILL.md +265 -0
- package/bin/cli.js +263 -20
- package/package.json +1 -1
- package/scripts/install-openclaw.js +76 -113
- package/src/dashboard/public/app.js +5 -1
- package/src/lib/disclosure.js +95 -4
- package/src/lib/external-ip.js +200 -0
- package/src/lib/invite-host.js +219 -0
- package/src/lib/port-scanner.js +38 -0
- package/src/routes/dashboard.js +8 -2
- package/src/server.js +45 -8
package/SKILL.md
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: a2a
|
|
3
|
+
description: "Agent-to-agent A2A for OpenClaw. Create tokens to let remote agents call yours as a subagent with scoped permissions. Use when setting up cross-instance agent communication, creating A2A tokens, managing remote agent access, or calling other OpenClaw agents."
|
|
4
|
+
metadata:
|
|
5
|
+
{
|
|
6
|
+
"openclaw":
|
|
7
|
+
{
|
|
8
|
+
"emoji": "๐ค",
|
|
9
|
+
"requires": { "bins": ["node"] },
|
|
10
|
+
"install":
|
|
11
|
+
[
|
|
12
|
+
{
|
|
13
|
+
"id": "npm",
|
|
14
|
+
"kind": "node",
|
|
15
|
+
"package": "a2acalling",
|
|
16
|
+
"bins": ["a2a"],
|
|
17
|
+
"label": "Install A2A Calling (npm)",
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
"routes": "/api/a2a",
|
|
21
|
+
"tools": ["a2a_call"],
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# A2A
|
|
27
|
+
|
|
28
|
+
Enable agent-to-agent communication across OpenClaw instances.
|
|
29
|
+
|
|
30
|
+
## FIRST RUN GATE (Check Before EVERY /a2a Command)
|
|
31
|
+
|
|
32
|
+
Before processing ANY /a2a command, check onboarding status:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
cat ~/.config/openclaw/a2a-config.json 2>/dev/null | grep '"onboardingComplete"'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**If the file does not exist or `onboardingComplete` is `false`:**
|
|
39
|
+
- DO NOT process the requested command yet
|
|
40
|
+
- Tell the user: "A2A needs initial setup. Let me configure your agent's disclosure topics first."
|
|
41
|
+
- Run the Quickstart flow below to completion
|
|
42
|
+
- Only THEN proceed with the user's original command
|
|
43
|
+
|
|
44
|
+
**If `onboardingComplete` is `true`:** proceed normally.
|
|
45
|
+
|
|
46
|
+
**Context gathering for onboarding:** Read ALL available context to generate personalized tier topics:
|
|
47
|
+
- **Primary:** USER.md, HEARTBEAT.md, SOUL.md
|
|
48
|
+
- **Skills:** SKILL.md files (workspace + ~/.openclaw/skills/*)
|
|
49
|
+
- **Memory:** memory/*.md files
|
|
50
|
+
- **Project:** CLAUDE.md
|
|
51
|
+
|
|
52
|
+
Extract: professional context, interests, goals, skills, sensitive areas. Group them into Public/Friends/Family tiers based on sensitivity.
|
|
53
|
+
|
|
54
|
+
## Commands
|
|
55
|
+
|
|
56
|
+
### Quickstart
|
|
57
|
+
|
|
58
|
+
User says: `/a2a quickstart`, `/a2a start`, "set up A2A", "get started with A2A", "configure what my agent shares"
|
|
59
|
+
|
|
60
|
+
Full onboarding flow: generates a disclosure manifest that controls what topics your agent leads with, discusses, or deflects during A2A calls โ scoped by access tier (public, friends, family).
|
|
61
|
+
|
|
62
|
+
This onboarding is required before the first `/a2a call`. The owner must approve permissions first.
|
|
63
|
+
|
|
64
|
+
Flow:
|
|
65
|
+
|
|
66
|
+
1. Scan USER.md, HEARTBEAT.md, SOUL.md to generate a default manifest
|
|
67
|
+
2. Present the manifest as a numbered text list grouped by tier:
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
PUBLIC TIER (anyone can see):
|
|
71
|
+
Lead with:
|
|
72
|
+
1. [topic] โ [detail]
|
|
73
|
+
2. [topic] โ [detail]
|
|
74
|
+
Discuss freely:
|
|
75
|
+
3. [topic] โ [detail]
|
|
76
|
+
Deflect:
|
|
77
|
+
4. [topic] โ [detail]
|
|
78
|
+
|
|
79
|
+
FRIENDS TIER (trusted contacts):
|
|
80
|
+
Lead with:
|
|
81
|
+
5. [topic] โ [detail]
|
|
82
|
+
...
|
|
83
|
+
|
|
84
|
+
FAMILY TIER (inner circle):
|
|
85
|
+
...
|
|
86
|
+
|
|
87
|
+
NEVER DISCLOSE:
|
|
88
|
+
N. [item]
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
3. User edits via text commands:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
move 3 to friends.lead โ Move topic #3 to friends tier lead_with
|
|
95
|
+
remove 5 โ Remove topic #5
|
|
96
|
+
add public.discuss "Topic" "Detail about it" โ Add new topic
|
|
97
|
+
edit 2 detail "Updated desc" โ Edit topic #2's detail
|
|
98
|
+
done โ Save manifest and finish
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
4. Manifest saved to `~/.config/openclaw/a2a-disclosure.json`
|
|
102
|
+
|
|
103
|
+
### Invite (Create & Share Token)
|
|
104
|
+
|
|
105
|
+
User says: `/a2a invite`, `/a2a invite public`, `/a2a invite friends`, `/a2a invite family`, "create an invite", "generate an A2A invite"
|
|
106
|
+
|
|
107
|
+
**IMPORTANT: You MUST output the full formatted invite below. Do NOT shorten it, summarize it, or skip sections. The entire block is the deliverable.**
|
|
108
|
+
|
|
109
|
+
1. Determine the tier from the user's command (default: `public`).
|
|
110
|
+
2. Run: `a2a create --name "AGENT_NAME" --owner "OWNER_NAME" --expires never --permissions TIER`
|
|
111
|
+
Use the agent's real name and owner name from workspace context.
|
|
112
|
+
3. Extract the `a2a://` invite URL from the CLI output.
|
|
113
|
+
4. Read topics from the config: `cat ~/.config/openclaw/a2a-config.json` โ get the tier's `topics` and `goals` arrays.
|
|
114
|
+
5. Output the invite to the user as EXACTLY this format (fill in real values):
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
๐๐ฃ๏ธ **Agent-to-Agent Call Invite**
|
|
119
|
+
|
|
120
|
+
๐ค **OWNER_NAME** would like your agent to call **AGENT_NAME** and explore where our owners might collaborate.
|
|
121
|
+
|
|
122
|
+
๐ฌ topic1 ยท topic2 ยท topic3 ยท topic4
|
|
123
|
+
๐ฏ goal1 ยท goal2 ยท goal3
|
|
124
|
+
|
|
125
|
+
a2a://hostname/fed_xxxxx
|
|
126
|
+
|
|
127
|
+
โโ setup โโ
|
|
128
|
+
npm i -g a2acalling && a2a add "a2a://hostname/fed_xxxxx" "AGENT_NAME" && a2a call "AGENT_NAME" "Hello from my owner!"
|
|
129
|
+
https://github.com/onthegonow/a2a_calling
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
Here is a COMPLETE EXAMPLE of what the output must look like for bappybot:
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
๐๐ฃ๏ธ **Agent-to-Agent Call Invite**
|
|
138
|
+
|
|
139
|
+
๐ค **Ben Pollack** would like your agent to call **bappybot** and explore where our owners might collaborate.
|
|
140
|
+
|
|
141
|
+
๐ฌ chat ยท openclaw ยท a2a-protocol ยท decentralization ยท community-living ยท snow-adventures ยท interactive-art ยท music-education
|
|
142
|
+
๐ฏ grow-network ยท spread-a2a-awareness ยท find-collaborators ยท build-in-public
|
|
143
|
+
|
|
144
|
+
a2a://149.28.213.47:3001/fed_AbCdEfGhIjKlMnOpQrStUvWx
|
|
145
|
+
|
|
146
|
+
โโ setup โโ
|
|
147
|
+
npm i -g a2acalling && a2a add "a2a://149.28.213.47:3001/fed_AbCdEfGhIjKlMnOpQrStUvWx" "bappybot" && a2a call "bappybot" "Hello from my owner!"
|
|
148
|
+
https://github.com/onthegonow/a2a_calling
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
Formatting rules:
|
|
153
|
+
- Join topics with ` ยท ` (middle dot). Show ALL topics from the tier config, not just "chat".
|
|
154
|
+
- Join goals with ` ยท `. Omit the ๐ฏ line only if there are zero goals.
|
|
155
|
+
- The setup line is ONE single copy-pasteable command.
|
|
156
|
+
- GitHub link is always the last line.
|
|
157
|
+
- If the token expires, add `โฐ EXPIRY_DATE` below the invite URL.
|
|
158
|
+
- Never truncate, abbreviate, or skip any part of this template.
|
|
159
|
+
|
|
160
|
+
### Create Token (Advanced)
|
|
161
|
+
|
|
162
|
+
User says: `/a2a create`, "create an A2A token", "let another agent call me"
|
|
163
|
+
|
|
164
|
+
For users who want fine-grained control over token options:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
a2a create --name "NAME" --expires DURATION --permissions LEVEL
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Options:
|
|
171
|
+
- `--name, -n` โ Token label
|
|
172
|
+
- `--expires, -e` โ `1h`, `1d`, `7d`, `30d`, `never` (default: `1d`)
|
|
173
|
+
- `--permissions, -p` โ `public`, `friends`, `family` (default: `public`)
|
|
174
|
+
- `--disclosure, -d` โ `public`, `minimal`, `none` (default: `minimal`)
|
|
175
|
+
- `--notify` โ `all`, `summary`, `none` (default: `all`)
|
|
176
|
+
|
|
177
|
+
After creating, format the output as the invite block described above.
|
|
178
|
+
|
|
179
|
+
### List Tokens
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
a2a list
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Revoke Token
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
a2a revoke TOKEN_ID
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Add Remote Agent
|
|
192
|
+
|
|
193
|
+
When user shares an invite URL:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
a2a add "a2a://host/token" "Agent Name"
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Calling Remote Agents
|
|
200
|
+
|
|
201
|
+
When task delegation to a known remote agent would help, or user asks to contact an A2A agent:
|
|
202
|
+
|
|
203
|
+
```javascript
|
|
204
|
+
// Use a2a_call tool
|
|
205
|
+
a2a_call({
|
|
206
|
+
endpoint: "a2a://host/token",
|
|
207
|
+
message: "Your question here",
|
|
208
|
+
conversation_id: "optional-for-continuity"
|
|
209
|
+
})
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Handling Incoming Calls
|
|
213
|
+
|
|
214
|
+
When receiving an A2A call, the agent operates within the token's permission scope.
|
|
215
|
+
|
|
216
|
+
Each tier carries a `capabilities[]` array. `context-read` is always available โ the agent can read its own knowledge base to formulate answers. Higher tiers unlock caller-facing capabilities:
|
|
217
|
+
|
|
218
|
+
| Tier | Default Capabilities |
|
|
219
|
+
|------|---------------------|
|
|
220
|
+
| `public` | `context-read` |
|
|
221
|
+
| `friends` | `context-read`, `calendar.read`, `email.read`, `search` |
|
|
222
|
+
| `family` | `context-read`, `calendar`, `email`, `search`, `tools`, `memory` |
|
|
223
|
+
|
|
224
|
+
Topics and goals act as information filters โ they control what the agent proactively shares, discusses, or deflects.
|
|
225
|
+
|
|
226
|
+
Apply disclosure level:
|
|
227
|
+
- `public` โ Share any non-private info
|
|
228
|
+
- `minimal` โ Direct answers only, no owner context
|
|
229
|
+
- `none` โ Confirm capability only
|
|
230
|
+
|
|
231
|
+
## Owner Notifications
|
|
232
|
+
|
|
233
|
+
When `notify: all`, send to owner:
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
๐ค A2A call received
|
|
237
|
+
|
|
238
|
+
From: [Caller] ([host])
|
|
239
|
+
Token: "[name]" (expires [date])
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
[Transcript]
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
๐ [N] calls | Expires in [time]
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Owner can reply to inject into the conversation.
|
|
249
|
+
|
|
250
|
+
## Update
|
|
251
|
+
|
|
252
|
+
Check for and install the latest version. Handles both npm global installs and git clones. Re-syncs SKILL.md and config after update.
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
a2a update --check # Check for updates without installing
|
|
256
|
+
a2a update # Update to latest version
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Rate Limits
|
|
260
|
+
|
|
261
|
+
Per token: 10/min, 100/hr, 1000/day
|
|
262
|
+
|
|
263
|
+
## Protocol Reference
|
|
264
|
+
|
|
265
|
+
See [docs/protocol.md](docs/protocol.md) for full specification.
|
package/bin/cli.js
CHANGED
|
@@ -39,6 +39,23 @@ function getConvStore() {
|
|
|
39
39
|
|
|
40
40
|
const store = new TokenStore();
|
|
41
41
|
|
|
42
|
+
// Check onboarding status โ warns but does not block
|
|
43
|
+
function checkOnboarding(commandName) {
|
|
44
|
+
try {
|
|
45
|
+
const { A2AConfig } = require('../src/lib/config');
|
|
46
|
+
const config = new A2AConfig();
|
|
47
|
+
if (!config.isOnboarded()) {
|
|
48
|
+
console.warn('\n\u26a0\ufe0f A2A onboarding not complete.');
|
|
49
|
+
console.warn(' Run "a2a quickstart" first to set up your agent\'s disclosure topics and permissions.');
|
|
50
|
+
console.warn(' Without onboarding, invites will have empty topics and calls use generic responses.\n');
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
} catch (e) {
|
|
55
|
+
return true; // Don't block if config is broken
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
42
59
|
// Format relative time
|
|
43
60
|
function formatTimeAgo(date) {
|
|
44
61
|
const seconds = Math.floor((new Date() - date) / 1000);
|
|
@@ -73,14 +90,29 @@ function parseArgs(argv) {
|
|
|
73
90
|
return args;
|
|
74
91
|
}
|
|
75
92
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
93
|
+
async function resolveInviteHostname() {
|
|
94
|
+
const { resolveInviteHost } = require('../src/lib/invite-host');
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
const { A2AConfig } = require('../src/lib/config');
|
|
98
|
+
const config = new A2AConfig();
|
|
99
|
+
const resolved = await resolveInviteHost({
|
|
100
|
+
config,
|
|
101
|
+
defaultPort: process.env.PORT || process.env.A2A_PORT || 3001
|
|
102
|
+
});
|
|
103
|
+
return resolved;
|
|
104
|
+
} catch (err) {
|
|
105
|
+
return resolveInviteHost({
|
|
106
|
+
fallbackHost: process.env.OPENCLAW_HOSTNAME || process.env.HOSTNAME || 'localhost',
|
|
107
|
+
defaultPort: process.env.PORT || process.env.A2A_PORT || 3001
|
|
108
|
+
});
|
|
109
|
+
}
|
|
79
110
|
}
|
|
80
111
|
|
|
81
112
|
// Commands
|
|
82
113
|
const commands = {
|
|
83
|
-
create: (args) => {
|
|
114
|
+
create: async (args) => {
|
|
115
|
+
checkOnboarding('create');
|
|
84
116
|
// Parse max-calls: number, 'unlimited', or default (unlimited)
|
|
85
117
|
let maxCalls = null; // Default: unlimited
|
|
86
118
|
if (args.flags['max-calls']) {
|
|
@@ -106,13 +138,21 @@ const commands = {
|
|
|
106
138
|
allowedTopics: customTopics
|
|
107
139
|
});
|
|
108
140
|
|
|
109
|
-
const
|
|
141
|
+
const resolvedHost = await resolveInviteHostname();
|
|
142
|
+
const hostname = resolvedHost.host;
|
|
110
143
|
const inviteUrl = `a2a://${hostname}/${token}`;
|
|
111
144
|
|
|
112
145
|
const expiresText = record.expires_at
|
|
113
146
|
? new Date(record.expires_at).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })
|
|
114
147
|
: 'never';
|
|
115
148
|
|
|
149
|
+
if (resolvedHost.warnings && resolvedHost.warnings.length) {
|
|
150
|
+
for (const w of resolvedHost.warnings) {
|
|
151
|
+
console.warn(`\nโ ๏ธ ${w}`);
|
|
152
|
+
}
|
|
153
|
+
console.warn('');
|
|
154
|
+
}
|
|
155
|
+
|
|
116
156
|
// Auto-link to contact if specified
|
|
117
157
|
const linkContact = args.flags.link || args.flags.l;
|
|
118
158
|
if (linkContact) {
|
|
@@ -636,6 +676,7 @@ https://github.com/onthegonow/a2a_calling`;
|
|
|
636
676
|
},
|
|
637
677
|
|
|
638
678
|
call: async (args) => {
|
|
679
|
+
checkOnboarding('call');
|
|
639
680
|
let target = args._[1];
|
|
640
681
|
const message = args._.slice(2).join(' ') || args.flags.message || args.flags.m;
|
|
641
682
|
|
|
@@ -723,29 +764,60 @@ https://github.com/onthegonow/a2a_calling`;
|
|
|
723
764
|
},
|
|
724
765
|
|
|
725
766
|
server: (args) => {
|
|
726
|
-
const
|
|
727
|
-
|
|
728
|
-
|
|
767
|
+
const explicitPort = args.flags.port || args.flags.p || process.env.PORT;
|
|
768
|
+
if (explicitPort) {
|
|
769
|
+
process.env.PORT = explicitPort;
|
|
770
|
+
console.log(`Starting A2A server on port ${explicitPort}...`);
|
|
771
|
+
} else {
|
|
772
|
+
console.log('Starting A2A server (scanning for available port)...');
|
|
773
|
+
}
|
|
729
774
|
require('../src/server.js');
|
|
730
775
|
},
|
|
731
776
|
|
|
732
777
|
quickstart: (args) => {
|
|
733
|
-
|
|
778
|
+
// Auto-complete onboarding if not done
|
|
779
|
+
try {
|
|
780
|
+
const { A2AConfig } = require('../src/lib/config');
|
|
781
|
+
const { readContextFiles, generateDefaultManifest, saveManifest } = require('../src/lib/disclosure');
|
|
782
|
+
const conf = new A2AConfig();
|
|
783
|
+
if (!conf.isOnboarded()) {
|
|
784
|
+
console.log('Setting up disclosure manifest from workspace context...');
|
|
785
|
+
const workspaceDir = process.env.A2A_WORKSPACE || process.cwd();
|
|
786
|
+
const contextFiles = readContextFiles(workspaceDir);
|
|
787
|
+
const manifest = generateDefaultManifest(contextFiles);
|
|
788
|
+
saveManifest(manifest);
|
|
789
|
+
|
|
790
|
+
const sources = [];
|
|
791
|
+
if (contextFiles.user) sources.push('USER.md');
|
|
792
|
+
if (contextFiles.heartbeat) sources.push('HEARTBEAT.md');
|
|
793
|
+
if (contextFiles.soul) sources.push('SOUL.md');
|
|
794
|
+
if (contextFiles.skill) sources.push('SKILL.md');
|
|
795
|
+
if (contextFiles.memory) sources.push('memory/*.md');
|
|
796
|
+
if (contextFiles.skills) sources.push('installed skills');
|
|
797
|
+
if (contextFiles.claude) sources.push('CLAUDE.md');
|
|
798
|
+
console.log(` Sources: ${sources.length > 0 ? sources.join(', ') : '(none found - using defaults)'}`);
|
|
799
|
+
|
|
800
|
+
conf.completeOnboarding();
|
|
801
|
+
console.log(' \u2705 Disclosure manifest generated and onboarding marked complete.\n');
|
|
802
|
+
}
|
|
803
|
+
} catch (e) {
|
|
804
|
+
// Non-fatal, continue with quickstart
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
const http = require('http');
|
|
808
|
+
const { splitHostPort } = require('../src/lib/invite-host');
|
|
809
|
+
const resolveHostPromise = resolveInviteHostname();
|
|
734
810
|
const name = args.flags.name || args.flags.n || 'My Agent';
|
|
735
811
|
const owner = args.flags.owner || args.flags.o || null;
|
|
736
|
-
|
|
812
|
+
|
|
737
813
|
console.log(`\n๐ A2A Quickstart\n${'โ'.repeat(50)}\n`);
|
|
738
814
|
|
|
739
815
|
// Step 1: Check server
|
|
740
816
|
console.log('1๏ธโฃ Checking server status...');
|
|
741
|
-
const
|
|
742
|
-
const serverHost = hostname.split(':')[0];
|
|
743
|
-
const serverPort = hostname.split(':')[1] || 3001;
|
|
744
|
-
|
|
745
|
-
const checkServer = () => new Promise((resolve) => {
|
|
817
|
+
const checkServer = (port) => new Promise((resolve) => {
|
|
746
818
|
const req = http.request({
|
|
747
|
-
hostname:
|
|
748
|
-
port
|
|
819
|
+
hostname: '127.0.0.1',
|
|
820
|
+
port,
|
|
749
821
|
path: '/api/a2a/ping',
|
|
750
822
|
timeout: 2000
|
|
751
823
|
}, (res) => {
|
|
@@ -756,10 +828,15 @@ https://github.com/onthegonow/a2a_calling`;
|
|
|
756
828
|
req.end();
|
|
757
829
|
});
|
|
758
830
|
|
|
759
|
-
|
|
831
|
+
resolveHostPromise.then(resolved => {
|
|
832
|
+
const parsed = splitHostPort(resolved.host);
|
|
833
|
+
const serverPort = parsed.port || 3001;
|
|
834
|
+
return checkServer(serverPort).then(serverOk => ({ serverOk, resolved, serverPort }));
|
|
835
|
+
}).then(({ serverOk, resolved, serverPort }) => {
|
|
836
|
+
const hostname = resolved.host;
|
|
760
837
|
if (!serverOk) {
|
|
761
838
|
console.log(' โ ๏ธ Server not running!');
|
|
762
|
-
console.log(` Run: A2A_HOSTNAME="${hostname}" a2a server\n`);
|
|
839
|
+
console.log(` Run: A2A_HOSTNAME="${hostname}" a2a server --port ${serverPort}\n`);
|
|
763
840
|
} else {
|
|
764
841
|
console.log(' โ
Server running\n');
|
|
765
842
|
}
|
|
@@ -779,6 +856,13 @@ https://github.com/onthegonow/a2a_calling`;
|
|
|
779
856
|
month: 'short', day: 'numeric', year: 'numeric'
|
|
780
857
|
});
|
|
781
858
|
|
|
859
|
+
if (resolved.warnings && resolved.warnings.length) {
|
|
860
|
+
for (const w of resolved.warnings) {
|
|
861
|
+
console.warn(`\nโ ๏ธ ${w}`);
|
|
862
|
+
}
|
|
863
|
+
console.warn('');
|
|
864
|
+
}
|
|
865
|
+
|
|
782
866
|
// Step 3: Show the invite
|
|
783
867
|
const ownerText = owner ? `${owner}` : 'Someone';
|
|
784
868
|
const topicsList = record.allowed_topics.join(' ยท ');
|
|
@@ -813,6 +897,157 @@ https://github.com/onthegonow/a2a_calling
|
|
|
813
897
|
require('../scripts/install-openclaw.js');
|
|
814
898
|
},
|
|
815
899
|
|
|
900
|
+
update: async (args) => {
|
|
901
|
+
const { execSync } = require('child_process');
|
|
902
|
+
const path = require('path');
|
|
903
|
+
const pkg = require('../package.json');
|
|
904
|
+
const currentVersion = pkg.version;
|
|
905
|
+
const checkOnly = args.flags.check || args.flags.c;
|
|
906
|
+
|
|
907
|
+
console.log(`\n๐ฆ A2A Update\n${'โ'.repeat(50)}\n`);
|
|
908
|
+
console.log(` Installed: v${currentVersion}`);
|
|
909
|
+
|
|
910
|
+
// Detect install method
|
|
911
|
+
const pkgRoot = path.resolve(__dirname, '..');
|
|
912
|
+
const isGitRepo = require('fs').existsSync(path.join(pkgRoot, '.git'));
|
|
913
|
+
|
|
914
|
+
if (isGitRepo) {
|
|
915
|
+
// Git clone โ use git pull
|
|
916
|
+
console.log(` Source: git (${pkgRoot})\n`);
|
|
917
|
+
|
|
918
|
+
if (checkOnly) {
|
|
919
|
+
try {
|
|
920
|
+
execSync('git fetch --quiet', { cwd: pkgRoot, stdio: 'pipe' });
|
|
921
|
+
const behind = execSync('git rev-list HEAD..@{u} --count', { cwd: pkgRoot, encoding: 'utf8' }).trim();
|
|
922
|
+
if (behind === '0') {
|
|
923
|
+
console.log(' \u2705 Already up to date.\n');
|
|
924
|
+
} else {
|
|
925
|
+
console.log(` \u2b06\ufe0f ${behind} commit(s) behind. Run "a2a update" to pull.\n`);
|
|
926
|
+
}
|
|
927
|
+
} catch (e) {
|
|
928
|
+
console.log(' \u26a0\ufe0f Could not check remote (no upstream or network error).\n');
|
|
929
|
+
}
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
console.log(' Pulling latest...');
|
|
934
|
+
try {
|
|
935
|
+
const output = execSync('git pull --ff-only 2>&1', { cwd: pkgRoot, encoding: 'utf8' });
|
|
936
|
+
console.log(` ${output.trim()}\n`);
|
|
937
|
+
} catch (e) {
|
|
938
|
+
const stderr = e.stderr ? e.stderr.toString() : e.message;
|
|
939
|
+
console.error(` \u274c Git pull failed: ${stderr.trim()}`);
|
|
940
|
+
console.error(' Try: cd ' + pkgRoot + ' && git pull manually.\n');
|
|
941
|
+
process.exit(1);
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
// Re-install deps if package.json changed
|
|
945
|
+
console.log(' Installing dependencies...');
|
|
946
|
+
try {
|
|
947
|
+
execSync('npm install --production 2>&1', { cwd: pkgRoot, encoding: 'utf8', timeout: 120000 });
|
|
948
|
+
} catch (e) {
|
|
949
|
+
console.warn(' \u26a0\ufe0f npm install had warnings (non-fatal).');
|
|
950
|
+
}
|
|
951
|
+
} else {
|
|
952
|
+
// npm global install โ use npm update
|
|
953
|
+
console.log(' Source: npm global\n');
|
|
954
|
+
|
|
955
|
+
// Check latest version on npm
|
|
956
|
+
let latestVersion;
|
|
957
|
+
try {
|
|
958
|
+
latestVersion = execSync('npm view a2acalling version 2>/dev/null', { encoding: 'utf8', timeout: 15000 }).trim();
|
|
959
|
+
console.log(` Latest: v${latestVersion}`);
|
|
960
|
+
} catch (e) {
|
|
961
|
+
console.error(' \u274c Could not check npm registry. Check your network.\n');
|
|
962
|
+
process.exit(1);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
if (latestVersion === currentVersion) {
|
|
966
|
+
console.log('\n \u2705 Already up to date.\n');
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
if (checkOnly) {
|
|
971
|
+
console.log(`\n \u2b06\ufe0f Update available: v${currentVersion} \u2192 v${latestVersion}`);
|
|
972
|
+
console.log(' Run "a2a update" to install.\n');
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
console.log(`\n Updating v${currentVersion} \u2192 v${latestVersion}...`);
|
|
977
|
+
try {
|
|
978
|
+
execSync('npm install -g a2acalling@latest 2>&1', { encoding: 'utf8', timeout: 120000 });
|
|
979
|
+
console.log(' \u2705 npm package updated.\n');
|
|
980
|
+
} catch (e) {
|
|
981
|
+
const stderr = e.stderr ? e.stderr.toString() : e.message;
|
|
982
|
+
console.error(` \u274c npm update failed: ${stderr.trim()}`);
|
|
983
|
+
console.error(' Try: npm install -g a2acalling@latest manually.\n');
|
|
984
|
+
process.exit(1);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
// Re-run install to sync SKILL.md and config
|
|
989
|
+
console.log(' Syncing SKILL.md and config...');
|
|
990
|
+
try {
|
|
991
|
+
const installScript = path.join(pkgRoot, 'scripts', 'install-openclaw.js');
|
|
992
|
+
execSync(`node "${installScript}" install 2>&1`, { encoding: 'utf8', timeout: 30000 });
|
|
993
|
+
} catch (e) {
|
|
994
|
+
console.warn(' \u26a0\ufe0f Post-update install sync had warnings (non-fatal).');
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
// Show new version
|
|
998
|
+
try {
|
|
999
|
+
delete require.cache[require.resolve('../package.json')];
|
|
1000
|
+
const newPkg = require('../package.json');
|
|
1001
|
+
console.log(`\n \u2705 Updated to v${newPkg.version}\n`);
|
|
1002
|
+
} catch (e) {
|
|
1003
|
+
console.log('\n \u2705 Update complete.\n');
|
|
1004
|
+
}
|
|
1005
|
+
},
|
|
1006
|
+
|
|
1007
|
+
onboard: (args) => {
|
|
1008
|
+
const { A2AConfig } = require('../src/lib/config');
|
|
1009
|
+
const { readContextFiles, generateDefaultManifest, saveManifest, MANIFEST_FILE } = require('../src/lib/disclosure');
|
|
1010
|
+
const config = new A2AConfig();
|
|
1011
|
+
|
|
1012
|
+
if (config.isOnboarded() && !args.flags.force) {
|
|
1013
|
+
console.log('\u2705 Onboarding already complete. Use --force to re-run.');
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
const workspaceDir = process.env.A2A_WORKSPACE || process.cwd();
|
|
1018
|
+
console.log('\n\ud83d\ude80 A2A Onboarding\n' + '\u2550'.repeat(50) + '\n');
|
|
1019
|
+
console.log('Scanning workspace for context...\n');
|
|
1020
|
+
|
|
1021
|
+
const contextFiles = readContextFiles(workspaceDir);
|
|
1022
|
+
// Print what was found
|
|
1023
|
+
const sources = {
|
|
1024
|
+
'USER.md': contextFiles.user,
|
|
1025
|
+
'HEARTBEAT.md': contextFiles.heartbeat,
|
|
1026
|
+
'SOUL.md': contextFiles.soul,
|
|
1027
|
+
'SKILL.md': contextFiles.skill,
|
|
1028
|
+
'CLAUDE.md': contextFiles.claude,
|
|
1029
|
+
'memory/*.md': contextFiles.memory,
|
|
1030
|
+
'Installed skills': contextFiles.skills
|
|
1031
|
+
};
|
|
1032
|
+
for (const [name, content] of Object.entries(sources)) {
|
|
1033
|
+
console.log(` ${content ? '\u2705' : '\u274c'} ${name}`);
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
const manifest = generateDefaultManifest(contextFiles);
|
|
1037
|
+
saveManifest(manifest);
|
|
1038
|
+
|
|
1039
|
+
const agentName = args.flags.name || config.getAgent().name || process.env.A2A_AGENT_NAME || '';
|
|
1040
|
+
const hostname = args.flags.hostname || config.getAgent().hostname || process.env.A2A_HOSTNAME || '';
|
|
1041
|
+
if (agentName) config.setAgent({ name: agentName });
|
|
1042
|
+
if (hostname) config.setAgent({ hostname });
|
|
1043
|
+
|
|
1044
|
+
config.completeOnboarding();
|
|
1045
|
+
|
|
1046
|
+
console.log(`\n\u2705 Onboarding complete!`);
|
|
1047
|
+
console.log(` Manifest: ${MANIFEST_FILE}`);
|
|
1048
|
+
console.log(` Next: a2a quickstart or a2a server\n`);
|
|
1049
|
+
},
|
|
1050
|
+
|
|
816
1051
|
help: () => {
|
|
817
1052
|
console.log(`A2A Calling - Agent-to-Agent Communication
|
|
818
1053
|
|
|
@@ -871,9 +1106,17 @@ Server:
|
|
|
871
1106
|
--name, -n Agent name for the invite
|
|
872
1107
|
--owner, -o Owner name (human behind the agent)
|
|
873
1108
|
|
|
1109
|
+
onboard Generate disclosure manifest from workspace context
|
|
1110
|
+
--force Re-run even if already onboarded
|
|
1111
|
+
--name Agent name
|
|
1112
|
+
--hostname Agent hostname
|
|
1113
|
+
|
|
1114
|
+
update Update A2A to latest version (npm or git pull)
|
|
1115
|
+
--check, -c Check for updates without installing
|
|
1116
|
+
|
|
874
1117
|
install Install A2A for OpenClaw
|
|
875
1118
|
setup Auto setup (gateway-aware dashboard install)
|
|
876
|
-
|
|
1119
|
+
|
|
877
1120
|
Examples:
|
|
878
1121
|
a2a create --name "bappybot" --owner "Benjamin Pollack" --expires 7d
|
|
879
1122
|
a2a create --name "custom" --topics "chat,calendar.read,email.read"
|