a2acalling 0.6.52 → 0.6.54

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.
@@ -0,0 +1,232 @@
1
+ {
2
+ "name": "a2a-callbook-macos",
3
+ "version": "0.1.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "a2a-callbook-macos",
9
+ "version": "0.1.0",
10
+ "devDependencies": {
11
+ "@tauri-apps/cli": "^2.10.0"
12
+ }
13
+ },
14
+ "node_modules/@tauri-apps/cli": {
15
+ "version": "2.10.0",
16
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.10.0.tgz",
17
+ "integrity": "sha512-ZwT0T+7bw4+DPCSWzmviwq5XbXlM0cNoleDKOYPFYqcZqeKY31KlpoMW/MOON/tOFBPgi31a2v3w9gliqwL2+Q==",
18
+ "dev": true,
19
+ "license": "Apache-2.0 OR MIT",
20
+ "bin": {
21
+ "tauri": "tauri.js"
22
+ },
23
+ "engines": {
24
+ "node": ">= 10"
25
+ },
26
+ "funding": {
27
+ "type": "opencollective",
28
+ "url": "https://opencollective.com/tauri"
29
+ },
30
+ "optionalDependencies": {
31
+ "@tauri-apps/cli-darwin-arm64": "2.10.0",
32
+ "@tauri-apps/cli-darwin-x64": "2.10.0",
33
+ "@tauri-apps/cli-linux-arm-gnueabihf": "2.10.0",
34
+ "@tauri-apps/cli-linux-arm64-gnu": "2.10.0",
35
+ "@tauri-apps/cli-linux-arm64-musl": "2.10.0",
36
+ "@tauri-apps/cli-linux-riscv64-gnu": "2.10.0",
37
+ "@tauri-apps/cli-linux-x64-gnu": "2.10.0",
38
+ "@tauri-apps/cli-linux-x64-musl": "2.10.0",
39
+ "@tauri-apps/cli-win32-arm64-msvc": "2.10.0",
40
+ "@tauri-apps/cli-win32-ia32-msvc": "2.10.0",
41
+ "@tauri-apps/cli-win32-x64-msvc": "2.10.0"
42
+ }
43
+ },
44
+ "node_modules/@tauri-apps/cli-darwin-arm64": {
45
+ "version": "2.10.0",
46
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.10.0.tgz",
47
+ "integrity": "sha512-avqHD4HRjrMamE/7R/kzJPcAJnZs0IIS+1nkDP5b+TNBn3py7N2aIo9LIpy+VQq0AkN8G5dDpZtOOBkmWt/zjA==",
48
+ "cpu": [
49
+ "arm64"
50
+ ],
51
+ "dev": true,
52
+ "license": "Apache-2.0 OR MIT",
53
+ "optional": true,
54
+ "os": [
55
+ "darwin"
56
+ ],
57
+ "engines": {
58
+ "node": ">= 10"
59
+ }
60
+ },
61
+ "node_modules/@tauri-apps/cli-darwin-x64": {
62
+ "version": "2.10.0",
63
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.10.0.tgz",
64
+ "integrity": "sha512-keDmlvJRStzVFjZTd0xYkBONLtgBC9eMTpmXnBXzsHuawV2q9PvDo2x6D5mhuoMVrJ9QWjgaPKBBCFks4dK71Q==",
65
+ "cpu": [
66
+ "x64"
67
+ ],
68
+ "dev": true,
69
+ "license": "Apache-2.0 OR MIT",
70
+ "optional": true,
71
+ "os": [
72
+ "darwin"
73
+ ],
74
+ "engines": {
75
+ "node": ">= 10"
76
+ }
77
+ },
78
+ "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": {
79
+ "version": "2.10.0",
80
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.10.0.tgz",
81
+ "integrity": "sha512-e5u0VfLZsMAC9iHaOEANumgl6lfnJx0Dtjkd8IJpysZ8jp0tJ6wrIkto2OzQgzcYyRCKgX72aKE0PFgZputA8g==",
82
+ "cpu": [
83
+ "arm"
84
+ ],
85
+ "dev": true,
86
+ "license": "Apache-2.0 OR MIT",
87
+ "optional": true,
88
+ "os": [
89
+ "linux"
90
+ ],
91
+ "engines": {
92
+ "node": ">= 10"
93
+ }
94
+ },
95
+ "node_modules/@tauri-apps/cli-linux-arm64-gnu": {
96
+ "version": "2.10.0",
97
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.10.0.tgz",
98
+ "integrity": "sha512-YrYYk2dfmBs5m+OIMCrb+JH/oo+4FtlpcrTCgiFYc7vcs6m3QDd1TTyWu0u01ewsCtK2kOdluhr/zKku+KP7HA==",
99
+ "cpu": [
100
+ "arm64"
101
+ ],
102
+ "dev": true,
103
+ "license": "Apache-2.0 OR MIT",
104
+ "optional": true,
105
+ "os": [
106
+ "linux"
107
+ ],
108
+ "engines": {
109
+ "node": ">= 10"
110
+ }
111
+ },
112
+ "node_modules/@tauri-apps/cli-linux-arm64-musl": {
113
+ "version": "2.10.0",
114
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.10.0.tgz",
115
+ "integrity": "sha512-GUoPdVJmrJRIXFfW3Rkt+eGK9ygOdyISACZfC/bCSfOnGt8kNdQIQr5WRH9QUaTVFIwxMlQyV3m+yXYP+xhSVA==",
116
+ "cpu": [
117
+ "arm64"
118
+ ],
119
+ "dev": true,
120
+ "license": "Apache-2.0 OR MIT",
121
+ "optional": true,
122
+ "os": [
123
+ "linux"
124
+ ],
125
+ "engines": {
126
+ "node": ">= 10"
127
+ }
128
+ },
129
+ "node_modules/@tauri-apps/cli-linux-riscv64-gnu": {
130
+ "version": "2.10.0",
131
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.10.0.tgz",
132
+ "integrity": "sha512-JO7s3TlSxshwsoKNCDkyvsx5gw2QAs/Y2GbR5UE2d5kkU138ATKoPOtxn8G1fFT1aDW4LH0rYAAfBpGkDyJJnw==",
133
+ "cpu": [
134
+ "riscv64"
135
+ ],
136
+ "dev": true,
137
+ "license": "Apache-2.0 OR MIT",
138
+ "optional": true,
139
+ "os": [
140
+ "linux"
141
+ ],
142
+ "engines": {
143
+ "node": ">= 10"
144
+ }
145
+ },
146
+ "node_modules/@tauri-apps/cli-linux-x64-gnu": {
147
+ "version": "2.10.0",
148
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.10.0.tgz",
149
+ "integrity": "sha512-Uvh4SUUp4A6DVRSMWjelww0GnZI3PlVy7VS+DRF5napKuIehVjGl9XD0uKoCoxwAQBLctvipyEK+pDXpJeoHng==",
150
+ "cpu": [
151
+ "x64"
152
+ ],
153
+ "dev": true,
154
+ "license": "Apache-2.0 OR MIT",
155
+ "optional": true,
156
+ "os": [
157
+ "linux"
158
+ ],
159
+ "engines": {
160
+ "node": ">= 10"
161
+ }
162
+ },
163
+ "node_modules/@tauri-apps/cli-linux-x64-musl": {
164
+ "version": "2.10.0",
165
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.10.0.tgz",
166
+ "integrity": "sha512-AP0KRK6bJuTpQ8kMNWvhIpKUkQJfcPFeba7QshOQZjJ8wOS6emwTN4K5g/d3AbCMo0RRdnZWwu67MlmtJyxC1Q==",
167
+ "cpu": [
168
+ "x64"
169
+ ],
170
+ "dev": true,
171
+ "license": "Apache-2.0 OR MIT",
172
+ "optional": true,
173
+ "os": [
174
+ "linux"
175
+ ],
176
+ "engines": {
177
+ "node": ">= 10"
178
+ }
179
+ },
180
+ "node_modules/@tauri-apps/cli-win32-arm64-msvc": {
181
+ "version": "2.10.0",
182
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.10.0.tgz",
183
+ "integrity": "sha512-97DXVU3dJystrq7W41IX+82JEorLNY+3+ECYxvXWqkq7DBN6FsA08x/EFGE8N/b0LTOui9X2dvpGGoeZKKV08g==",
184
+ "cpu": [
185
+ "arm64"
186
+ ],
187
+ "dev": true,
188
+ "license": "Apache-2.0 OR MIT",
189
+ "optional": true,
190
+ "os": [
191
+ "win32"
192
+ ],
193
+ "engines": {
194
+ "node": ">= 10"
195
+ }
196
+ },
197
+ "node_modules/@tauri-apps/cli-win32-ia32-msvc": {
198
+ "version": "2.10.0",
199
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.10.0.tgz",
200
+ "integrity": "sha512-EHyQ1iwrWy1CwMalEm9z2a6L5isQ121pe7FcA2xe4VWMJp+GHSDDGvbTv/OPdkt2Lyr7DAZBpZHM6nvlHXEc4A==",
201
+ "cpu": [
202
+ "ia32"
203
+ ],
204
+ "dev": true,
205
+ "license": "Apache-2.0 OR MIT",
206
+ "optional": true,
207
+ "os": [
208
+ "win32"
209
+ ],
210
+ "engines": {
211
+ "node": ">= 10"
212
+ }
213
+ },
214
+ "node_modules/@tauri-apps/cli-win32-x64-msvc": {
215
+ "version": "2.10.0",
216
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.10.0.tgz",
217
+ "integrity": "sha512-NTpyQxkpzGmU6ceWBTY2xRIEaS0ZLbVx1HE1zTA3TY/pV3+cPoPPOs+7YScr4IMzXMtOw7tLw5LEXo5oIG3qaQ==",
218
+ "cpu": [
219
+ "x64"
220
+ ],
221
+ "dev": true,
222
+ "license": "Apache-2.0 OR MIT",
223
+ "optional": true,
224
+ "os": [
225
+ "win32"
226
+ ],
227
+ "engines": {
228
+ "node": ">= 10"
229
+ }
230
+ }
231
+ }
232
+ }
@@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
2
2
  use std::path::PathBuf;
3
3
  use std::time::Duration;
4
4
 
5
- const DEFAULT_PORTS: &[u16] = &[3001, 80, 8080, 8443, 9001];
5
+ const DEFAULT_PORTS: &[u16] = &[80, 3001, 8080, 8443, 9001];
6
6
  const PROBE_TIMEOUT: Duration = Duration::from_millis(800);
7
7
 
8
8
  #[derive(Debug, Serialize, Deserialize)]
@@ -14,15 +14,57 @@ pub struct DiscoveryResult {
14
14
  #[derive(Debug, Deserialize)]
15
15
  struct A2AConfig {
16
16
  onboarding: Option<OnboardingConfig>,
17
+ agent: Option<AgentConfig>,
17
18
  }
18
19
 
19
20
  #[derive(Debug, Deserialize)]
20
21
  struct OnboardingConfig {
22
+ #[serde(alias = "serverPort")]
21
23
  server_port: Option<u16>,
22
24
  }
23
25
 
24
- /// Read port from ~/.config/openclaw/a2a-config.json
25
- pub fn read_config_port() -> Option<u16> {
26
+ #[derive(Debug, Deserialize)]
27
+ struct AgentConfig {
28
+ hostname: Option<String>,
29
+ }
30
+
31
+ fn parse_port_from_hostname(hostname: &str) -> Option<u16> {
32
+ let trimmed = hostname.trim();
33
+ if trimmed.is_empty() {
34
+ return None;
35
+ }
36
+
37
+ // Allow values like:
38
+ // - 149.28.213.47:3007
39
+ // - localhost:3007
40
+ // - http://localhost:3007
41
+ // - [::1]:3007
42
+ let without_scheme = trimmed.split("://").nth(1).unwrap_or(trimmed);
43
+ let host_segment = without_scheme.split('/').next().unwrap_or(without_scheme);
44
+
45
+ if host_segment.starts_with('[') {
46
+ let end = host_segment.find(']')?;
47
+ let remainder = &host_segment[end + 1..];
48
+ let port_str = remainder.strip_prefix(':')?;
49
+ return port_str.parse::<u16>().ok();
50
+ }
51
+
52
+ // Bare IPv6 literals contain multiple colons and no explicit port delimiter.
53
+ if host_segment.matches(':').count() > 1 {
54
+ return None;
55
+ }
56
+
57
+ let (_, port_str) = host_segment.rsplit_once(':')?;
58
+ if !port_str.chars().all(|ch| ch.is_ascii_digit()) {
59
+ return None;
60
+ }
61
+
62
+ port_str.parse::<u16>().ok()
63
+ }
64
+
65
+ /// Read likely server ports from ~/.config/openclaw/a2a-config.json.
66
+ /// We prefer explicit onboarding server_port, then agent.hostname port.
67
+ pub fn read_config_ports() -> Vec<u16> {
26
68
  let config_dir = std::env::var("A2A_CONFIG_DIR")
27
69
  .or_else(|_| std::env::var("OPENCLAW_CONFIG_DIR"))
28
70
  .map(PathBuf::from)
@@ -34,14 +76,36 @@ pub fn read_config_port() -> Option<u16> {
34
76
  });
35
77
 
36
78
  let config_path = config_dir.join("a2a-config.json");
37
- let content = std::fs::read_to_string(config_path).ok()?;
38
- let config: A2AConfig = serde_json::from_str(&content).ok()?;
39
- config.onboarding?.server_port
79
+ let content = match std::fs::read_to_string(config_path) {
80
+ Ok(data) => data,
81
+ Err(_) => return vec![],
82
+ };
83
+ let config: A2AConfig = match serde_json::from_str(&content) {
84
+ Ok(parsed) => parsed,
85
+ Err(_) => return vec![],
86
+ };
87
+
88
+ let mut ports = Vec::new();
89
+
90
+ if let Some(port) = config.onboarding.and_then(|ob| ob.server_port) {
91
+ ports.push(port);
92
+ }
93
+
94
+ if let Some(port) = config
95
+ .agent
96
+ .and_then(|agent| agent.hostname)
97
+ .and_then(|hostname| parse_port_from_hostname(&hostname))
98
+ {
99
+ if !ports.contains(&port) {
100
+ ports.push(port);
101
+ }
102
+ }
103
+
104
+ ports
40
105
  }
41
106
 
42
107
  /// Probe a single port — returns true if a2a server responds
43
108
  async fn probe_port(port: u16) -> bool {
44
- let url = format!("http://127.0.0.1:{}/api/a2a/ping", port);
45
109
  let client = reqwest::Client::builder()
46
110
  .timeout(PROBE_TIMEOUT)
47
111
  .build();
@@ -51,16 +115,36 @@ async fn probe_port(port: u16) -> bool {
51
115
  Err(_) => return false,
52
116
  };
53
117
 
54
- match client.get(&url).send().await {
55
- Ok(resp) => resp.status().is_success(),
56
- Err(_) => false,
118
+ for host in ["127.0.0.1", "localhost"] {
119
+ let url = format!("http://{}:{}/api/a2a/ping", host, port);
120
+ let ok = match client.get(&url).send().await {
121
+ Ok(resp) => {
122
+ if !resp.status().is_success() {
123
+ false
124
+ } else {
125
+ match resp.text().await {
126
+ Ok(body) => body.contains("\"pong\":true"),
127
+ Err(_) => false,
128
+ }
129
+ }
130
+ }
131
+ Err(_) => false,
132
+ };
133
+
134
+ if ok {
135
+ return true;
136
+ }
57
137
  }
138
+
139
+ false
58
140
  }
59
141
 
60
142
  /// Discover the running a2a server
61
143
  pub async fn discover_server() -> DiscoveryResult {
62
- // 1. Try config port first
63
- if let Some(port) = read_config_port() {
144
+ let config_ports = read_config_ports();
145
+
146
+ // 1. Try config-derived ports first
147
+ for &port in &config_ports {
64
148
  if probe_port(port).await {
65
149
  return DiscoveryResult {
66
150
  port: Some(port),
@@ -69,8 +153,11 @@ pub async fn discover_server() -> DiscoveryResult {
69
153
  }
70
154
  }
71
155
 
72
- // 2. Scan default ports
156
+ // 2. Scan default ports (skip those already checked from config)
73
157
  for &port in DEFAULT_PORTS {
158
+ if config_ports.contains(&port) {
159
+ continue;
160
+ }
74
161
  if probe_port(port).await {
75
162
  return DiscoveryResult {
76
163
  port: Some(port),
@@ -44,7 +44,10 @@ pub fn start_server() -> StartResult {
44
44
  }
45
45
  };
46
46
 
47
- let port = crate::discovery::read_config_port().unwrap_or(3001);
47
+ let port = crate::discovery::read_config_ports()
48
+ .first()
49
+ .copied()
50
+ .unwrap_or(3001);
48
51
  let port_str = port.to_string();
49
52
 
50
53
  let result = Command::new(&binary)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "a2acalling",
3
- "version": "0.6.52",
3
+ "version": "0.6.54",
4
4
  "description": "Agent-to-agent calling for OpenClaw - A2A agent communication",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -1,8 +1,13 @@
1
1
  /**
2
2
  * A2A Skill Installer
3
3
  *
4
- * Copies Claude Code commands and Codex AGENTS.md into a target project directory.
5
- * Idempotent: skips files that already exist with identical content.
4
+ * Copies Claude Code commands, CLAUDE.md context, and Codex AGENTS.md into a
5
+ * target project directory. Idempotent: skips files that already exist with
6
+ * identical content.
7
+ *
8
+ * CLAUDE.md is the key file — Claude Code reads it automatically, giving the
9
+ * agent full context about the a2a CLI, native app, and onboarding flow
10
+ * immediately after npm install.
6
11
  */
7
12
 
8
13
  const fs = require('fs');
@@ -11,11 +16,15 @@ const path = require('path');
11
16
  const PACKAGE_ROOT = path.join(__dirname, '..');
12
17
 
13
18
  const SKILL_FILES = [
19
+ // CLAUDE.md — gives Claude Code instant context about the a2a CLI
20
+ { src: 'CLAUDE-INSTALL.md', dest: 'CLAUDE.md', mergeKey: '# A2A Calling' },
21
+ // Claude Code slash commands
14
22
  { src: '.claude/commands/a2a-call.md', dest: '.claude/commands/a2a-call.md' },
15
23
  { src: '.claude/commands/a2a-invite.md', dest: '.claude/commands/a2a-invite.md' },
16
24
  { src: '.claude/commands/a2a-contacts.md', dest: '.claude/commands/a2a-contacts.md' },
17
25
  { src: '.claude/commands/a2a-status.md', dest: '.claude/commands/a2a-status.md' },
18
26
  { src: '.claude/commands/a2a-setup.md', dest: '.claude/commands/a2a-setup.md' },
27
+ // Codex agent instructions
19
28
  { src: '.codex/AGENTS.md', dest: '.codex/AGENTS.md' }
20
29
  ];
21
30
 
@@ -34,10 +43,36 @@ function installSkills(targetDir, options = {}) {
34
43
 
35
44
  const srcContent = fs.readFileSync(srcPath, 'utf8');
36
45
 
37
- // Check if identical file already exists
38
- if (!options.force && fs.existsSync(destPath)) {
46
+ if (fs.existsSync(destPath)) {
39
47
  const existing = fs.readFileSync(destPath, 'utf8');
40
- if (existing === srcContent) {
48
+
49
+ // Merge mode: if the file has a mergeKey, append/replace the A2A section
50
+ // in an existing file rather than overwriting it entirely.
51
+ if (file.mergeKey) {
52
+ if (existing.includes(file.mergeKey)) {
53
+ // A2A section already present — extract and compare
54
+ const sectionStart = existing.indexOf(file.mergeKey);
55
+ const existingSection = existing.slice(sectionStart);
56
+ if (!options.force && existingSection.trim() === srcContent.trim()) {
57
+ result.skipped.push(file.dest);
58
+ continue;
59
+ }
60
+ // Replace the A2A section with updated content
61
+ const before = existing.slice(0, sectionStart).trimEnd();
62
+ const merged = before ? before + '\n\n' + srcContent : srcContent;
63
+ fs.writeFileSync(destPath, merged);
64
+ result.installed.push(file.dest + ' (updated A2A section)');
65
+ } else {
66
+ // Existing CLAUDE.md without A2A section — append
67
+ const merged = existing.trimEnd() + '\n\n' + srcContent;
68
+ fs.writeFileSync(destPath, merged);
69
+ result.installed.push(file.dest + ' (appended A2A section)');
70
+ }
71
+ continue;
72
+ }
73
+
74
+ // Standard mode: skip if identical
75
+ if (!options.force && existing === srcContent) {
41
76
  result.skipped.push(file.dest);
42
77
  continue;
43
78
  }