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.
- package/.claude/commands/a2a-call.md +26 -0
- package/.claude/commands/a2a-contacts.md +31 -0
- package/.claude/commands/a2a-invite.md +33 -0
- package/.claude/commands/a2a-setup.md +30 -0
- package/.claude/commands/a2a-status.md +24 -0
- package/CLAUDE-INSTALL.md +154 -0
- package/README.md +1 -0
- package/bin/cli.js +83 -7
- package/docs/protocol.md +6 -5
- package/native/macos/index.html +24 -12
- package/native/macos/package-lock.json +232 -0
- package/native/macos/src-tauri/src/discovery.rs +100 -13
- package/native/macos/src-tauri/src/server.rs +4 -1
- package/package.json +1 -1
- package/scripts/install-skills.js +40 -5
- package/scripts/postinstall.js +153 -30
- package/src/dashboard/public/app.js +2 -0
- package/src/dashboard/public/index.html +1 -0
- package/src/lib/claude-subagent.js +100 -27
- package/src/lib/config.js +11 -0
- package/src/lib/conversation-driver.js +11 -2
- package/src/lib/disclosure.js +89 -13
- package/src/lib/runtime-adapter.js +42 -15
- package/src/lib/tokens.js +18 -0
- package/src/routes/a2a.js +4 -0
- package/src/routes/dashboard.js +9 -1
- package/src/server.js +42 -2
|
@@ -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] = &[
|
|
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
|
-
|
|
25
|
-
|
|
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)
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
63
|
-
|
|
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::
|
|
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,8 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A2A Skill Installer
|
|
3
3
|
*
|
|
4
|
-
* Copies Claude Code commands and Codex AGENTS.md into a
|
|
5
|
-
* Idempotent: skips files that already exist with
|
|
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
|
-
|
|
38
|
-
if (!options.force && fs.existsSync(destPath)) {
|
|
46
|
+
if (fs.existsSync(destPath)) {
|
|
39
47
|
const existing = fs.readFileSync(destPath, 'utf8');
|
|
40
|
-
|
|
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
|
}
|