a2acalling 0.6.14 → 0.6.16

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/bin/cli.js CHANGED
@@ -70,29 +70,28 @@ function getConvStore() {
70
70
 
71
71
  const store = new TokenStore();
72
72
 
73
+ // ── enforceOnboarding ────────────────────────────────────────────────────
74
+ // If onboarding is incomplete or the config is missing/invalid, run the
75
+ // full quickstart flow inline — verbose, with direct stdio. The agent sees
76
+ // the banner, port selection, server start, and disclosure prompt right here.
77
+ //
78
+ // This is the primary onboarding entry point for agents. npm postinstall
79
+ // silently starts the server (npm captures its output), so the first time
80
+ // the agent runs ANY `a2a` command, this function fires and gives the agent
81
+ // the full verbose walkthrough it needs to complete setup.
82
+ //
83
+ // Returns a Promise if quickstart needs to run (caller must await), or
84
+ // undefined if onboarding is already complete.
73
85
  function enforceOnboarding(command) {
74
86
  if (ONBOARDING_EXEMPT.has(command)) {
75
87
  return;
76
88
  }
77
89
 
78
90
  if (!isOnboarded()) {
79
- // Check if we're mid-onboarding (server running, awaiting disclosure)
80
- try {
81
- const cfg = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
82
- if (cfg.onboarding?.step === 'awaiting_disclosure') {
83
- console.log('\nA2A setup in progress. Disclosure topics not yet submitted.\n');
84
- console.log("Next: run `a2a quickstart --submit '<json>'` (or `a2a onboard --submit`)\n");
85
- process.exit(1);
86
- }
87
- } catch (e) {
88
- if (e.code !== 'ENOENT' && e.name !== 'SyntaxError') {
89
- console.error(`Warning: could not read config: ${e.message}`);
90
- }
91
- }
92
-
93
- console.log('\nA2A not configured yet.\n');
94
- console.log('Next: run `a2a quickstart`\n');
95
- process.exit(1);
91
+ // Run the full quickstart flow inline — verbose output, direct stdio.
92
+ // This replaces the original command; after onboarding the agent can
93
+ // re-run their intended command.
94
+ return commands.quickstart({ flags: {}, positional: [] });
96
95
  }
97
96
  }
98
97
 
@@ -265,70 +264,6 @@ function printSection(title) {
265
264
  console.log('\n━━━ ' + title + ' ━━━');
266
265
  }
267
266
 
268
- function readWorkspaceContext(baseDir = process.cwd()) {
269
- const base = baseDir || process.cwd();
270
- const workspaceFiles = {
271
- USER: { filename: 'USER.md' },
272
- SOUL: { filename: 'SOUL.md' },
273
- HEARTBEAT: { filename: 'HEARTBEAT.md' },
274
- SKILL: { filename: 'SKILL.md' },
275
- CLAUDE: { filename: 'CLAUDE.md' }
276
- };
277
-
278
- const found = {};
279
- for (const key of Object.keys(workspaceFiles)) {
280
- found[key] = fs.existsSync(path.join(base, workspaceFiles[key].filename));
281
- }
282
-
283
- const memoryDir = path.join(base, 'memory');
284
- let memoryCount = 0;
285
- if (fs.existsSync(memoryDir)) {
286
- try {
287
- memoryCount = fs.readdirSync(memoryDir).filter(item => item.endsWith('.md')).length;
288
- } catch (err) {
289
- memoryCount = 0;
290
- }
291
- }
292
- found.MEMORY = memoryCount;
293
-
294
- return {
295
- workspace: base,
296
- found,
297
- memoryCount
298
- };
299
- }
300
-
301
- function printWorkspaceScan(context) {
302
- const fileLabels = {
303
- USER: 'USER.md — identity hints',
304
- SOUL: 'SOUL.md — personality notes',
305
- HEARTBEAT: 'HEARTBEAT.md — scheduled tasks',
306
- SKILL: 'SKILL.md — capabilities',
307
- CLAUDE: 'CLAUDE.md — agent instructions'
308
- };
309
- const found = Object.entries(fileLabels)
310
- .filter(([key]) => context.found[key])
311
- .map(([, label]) => label);
312
- if (context.memoryCount > 0) found.push(`${context.memoryCount} memory file(s)`);
313
-
314
- if (found.length) {
315
- console.log(`Workspace context: ${found.join(', ')}`);
316
- } else {
317
- console.log('Workspace context: no context files found (disclosure topics will be minimal)');
318
- }
319
- }
320
-
321
- function getDisclosurePromptFiles(context) {
322
- return {
323
- 'USER.md': context.found.USER,
324
- 'SOUL.md': context.found.SOUL,
325
- 'HEARTBEAT.md': context.found.HEARTBEAT,
326
- 'SKILL.md': context.found.SKILL,
327
- 'CLAUDE.md': context.found.CLAUDE,
328
- 'memory/*.md': context.memoryCount > 0
329
- };
330
- }
331
-
332
267
  async function inspectPorts(preferredPort = null) {
333
268
  const candidates = [];
334
269
  if (preferredPort) {
@@ -1854,13 +1789,21 @@ if (!commands[command]) {
1854
1789
  process.exit(1);
1855
1790
  }
1856
1791
 
1857
- enforceOnboarding(command);
1858
-
1859
- // Handle async commands
1860
- const result = commands[command](args);
1861
- if (result instanceof Promise) {
1862
- result.catch(err => {
1792
+ // If onboarding is incomplete, enforceOnboarding runs quickstart inline
1793
+ // (verbose, full output) and returns a Promise. Otherwise returns undefined
1794
+ // and we proceed to the requested command.
1795
+ const onboardResult = enforceOnboarding(command);
1796
+ if (onboardResult instanceof Promise) {
1797
+ onboardResult.catch(err => {
1863
1798
  console.error(err.message);
1864
1799
  process.exit(1);
1865
1800
  });
1801
+ } else {
1802
+ const result = commands[command](args);
1803
+ if (result instanceof Promise) {
1804
+ result.catch(err => {
1805
+ console.error(err.message);
1806
+ process.exit(1);
1807
+ });
1808
+ }
1866
1809
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "a2acalling",
3
- "version": "0.6.14",
3
+ "version": "0.6.16",
4
4
  "description": "Agent-to-agent calling for OpenClaw - A2A agent communication",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -1,55 +1,37 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // ============================================================================
4
- // npm postinstall hook — automatic onboarding launcher
4
+ // npm postinstall hook — silent server setup
5
5
  //
6
- // OBJECTIVE: When a user (or agent) runs `npm install -g a2acalling`, this
7
- // script fires automatically and launches the full quickstart onboarding flow.
8
- // The agent should never need a second command — install IS onboarding.
6
+ // OBJECTIVE: When `npm install -g a2acalling` runs, this script silently
7
+ // starts the A2A server and saves the initial config. No output is produced
8
+ // because npm v7+ captures all lifecycle script stdio.
9
9
  //
10
- // KEY DESIGN DECISIONS:
10
+ // The VERBOSE onboarding (disclosure prompt, workspace scan instructions)
11
+ // happens when the agent runs any `a2a` command — enforceOnboarding detects
12
+ // incomplete setup and runs the full quickstart flow inline with direct stdio.
11
13
  //
12
- // 1. No TTY gate. Agents are the primary users of this package and they
13
- // run in non-interactive (piped) environments. Every prompt in the
14
- // quickstart flow auto-accepts sensible defaults when there is no TTY,
15
- // so the full onboarding completes unattended: port selection, hostname
16
- // detection, server start, and disclosure prompt output.
14
+ // This split exists because:
15
+ // - npm postinstall: stdio is piped/captured, output is invisible to agents
16
+ // - `a2a <command>`: agent runs directly, stdio is clean, output is visible
17
17
  //
18
- // 2. Bypassing npm's output capture. npm v7+ pipes lifecycle script stdout
19
- // AND stderr to an internal buffer, only showing them on failure. This
20
- // means normal console.log/console.error from postinstall is invisible.
21
- // To make onboarding output visible:
22
- //
23
- // a) Run quickstart with piped stdio, capturing all its output.
24
- // b) Save the output to a2a-onboarding.txt in the config dir (reliable
25
- // fallback — agents can always read this file).
26
- // c) Try /dev/tty (interactive terminals — bypasses npm entirely).
27
- // d) Try /proc/$PPID/fd/2 (Linux — writes directly to npm's stderr fd,
28
- // which IS the caller's stderr, bypassing npm's pipe buffer).
29
- // e) Last resort: process.stderr.write (npm may still buffer this).
30
- //
31
- // 3. Never fail the install. If quickstart can't launch (e.g. missing node
32
- // binary edge case), we print a hint and exit 0. A broken postinstall
33
- // would prevent the package from installing at all.
34
- //
35
- // 4. Skip non-global installs, CI, and Docker builds. Local `npm install`
36
- // in a project shouldn't trigger onboarding. CI and Docker are build
37
- // environments, not runtime hosts.
18
+ // So postinstall handles the parts that don't need output (server start),
19
+ // and the a2a binary handles the parts that DO need output (disclosure prompt).
38
20
  // ============================================================================
39
21
 
40
22
  if (process.env.CI || process.env.CONTINUOUS_INTEGRATION) process.exit(0);
41
23
  if (process.env.DOCKER) process.exit(0);
42
24
  if (process.env.npm_config_global !== 'true') process.exit(0);
43
25
 
44
- const fs = require('fs');
45
26
  const path = require('path');
46
27
  const { spawnSync } = require('child_process');
47
28
 
48
29
  const initCwd = process.env.INIT_CWD || process.env.HOME || process.cwd();
49
30
  const cliPath = path.join(__dirname, '..', 'bin', 'cli.js');
50
31
 
51
- // Run quickstart with piped stdio so we capture all its output.
52
- // stdin is piped (empty) so all prompts auto-accept their defaults.
32
+ // Run quickstart silently starts server, saves config, detects hostname.
33
+ // All prompts auto-accept defaults (stdin is piped with no input).
34
+ // Output is captured (not shown) because npm would swallow it anyway.
53
35
  const result = spawnSync(process.execPath, [cliPath, 'quickstart'], {
54
36
  stdio: ['pipe', 'pipe', 'pipe'],
55
37
  cwd: initCwd,
@@ -60,58 +42,8 @@ const result = spawnSync(process.execPath, [cliPath, 'quickstart'], {
60
42
  });
61
43
 
62
44
  if (result.error) {
63
- process.stderr.write('\nCould not auto-launch onboarding.\n');
64
- process.stderr.write(`Reason: ${result.error.message}\n`);
65
- process.stderr.write('\nRun manually: a2a quickstart\n\n');
66
- process.exit(0); // don't fail the install
45
+ // Don't fail the install — the agent will get onboarding when it runs `a2a`.
46
+ process.exit(0);
67
47
  }
68
48
 
69
- const output = (result.stdout || '').toString() + (result.stderr || '').toString();
70
-
71
- // ── Always save to file (reliable fallback) ──────────────────────────────
72
- // Agents can read this after install regardless of output visibility.
73
- try {
74
- const configDir = process.env.A2A_CONFIG_DIR
75
- || path.join(process.env.HOME || '/root', '.config', 'openclaw');
76
- if (fs.existsSync(configDir)) {
77
- fs.writeFileSync(path.join(configDir, 'a2a-onboarding.txt'), output);
78
- }
79
- } catch (_) {}
80
-
81
- // ── Make output visible to the caller ────────────────────────────────────
82
- // npm pipes BOTH stdout and stderr of lifecycle scripts (v7+), so writing
83
- // to our own fds goes into npm's buffer. We need to bypass npm entirely.
84
-
85
- function tryWrite(output) {
86
- // Strategy 1: /dev/tty — interactive terminals.
87
- // npm runs postinstall with piped stdio, but /dev/tty talks directly to
88
- // the user's terminal. Works for humans, not for agents.
89
- try {
90
- const fd = fs.openSync('/dev/tty', 'w');
91
- fs.writeSync(fd, output);
92
- fs.closeSync(fd);
93
- return true;
94
- } catch (_) {}
95
-
96
- // Strategy 2: /proc/$PPID/fd/2 — Linux, write to npm's stderr directly.
97
- // npm's stderr IS the caller's stderr (the agent's output stream).
98
- // This bypasses npm's pipe buffer because we open the fd independently.
99
- try {
100
- const fd = fs.openSync(`/proc/${process.ppid}/fd/2`, 'w');
101
- fs.writeSync(fd, output);
102
- fs.closeSync(fd);
103
- return true;
104
- } catch (_) {}
105
-
106
- // Strategy 3: process.stderr — last resort, npm may still buffer this.
107
- try {
108
- process.stderr.write(output);
109
- return true;
110
- } catch (_) {}
111
-
112
- return false;
113
- }
114
-
115
- tryWrite(output);
116
-
117
49
  process.exit(result.status || 0);