a2acalling 0.3.6 โ†’ 0.4.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/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
- // Get hostname for invite URLs
77
- function getHostname() {
78
- return process.env.A2A_HOSTNAME || process.env.OPENCLAW_HOSTNAME || process.env.HOSTNAME || 'localhost';
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 hostname = getHostname();
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 port = args.flags.port || args.flags.p || process.env.PORT || 3001;
727
- process.env.PORT = port;
728
- console.log(`Starting A2A server on port ${port}...`);
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
- const hostname = process.env.A2A_HOSTNAME || process.env.HOSTNAME || 'localhost:3001';
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 http = require('http');
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: serverHost === 'localhost' ? '127.0.0.1' : serverHost,
748
- port: serverPort,
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
- checkServer().then(serverOk => {
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"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "a2acalling",
3
- "version": "0.3.6",
3
+ "version": "0.4.1",
4
4
  "description": "Agent-to-agent calling for OpenClaw - A2A agent communication",
5
5
  "main": "src/index.js",
6
6
  "bin": {