a2acalling 0.6.75 → 0.6.76
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/.a2a-manifest.json +2 -2
- package/.claude/worktrees/agent-a0a8dd02/.a2a-manifest.json +70 -0
- package/.claude/worktrees/agent-a0a8dd02/.c8rc.json +16 -0
- package/.claude/worktrees/agent-a0a8dd02/.claude/a2a-skill-reference.md +462 -0
- package/.claude/worktrees/agent-a0a8dd02/.claude/commands/a2a-app.md +42 -0
- package/.claude/worktrees/agent-a0a8dd02/.claude/commands/a2a-call.md +26 -0
- package/.claude/worktrees/agent-a0a8dd02/.claude/commands/a2a-contacts.md +31 -0
- package/.claude/worktrees/agent-a0a8dd02/.claude/commands/a2a-conversations.md +47 -0
- package/.claude/worktrees/agent-a0a8dd02/.claude/commands/a2a-gui.md +30 -0
- package/.claude/worktrees/agent-a0a8dd02/.claude/commands/a2a-invite.md +63 -0
- package/.claude/worktrees/agent-a0a8dd02/.claude/commands/a2a-setup.md +30 -0
- package/.claude/worktrees/agent-a0a8dd02/.claude/commands/a2a-skills.md +27 -0
- package/.claude/worktrees/agent-a0a8dd02/.claude/commands/a2a-status.md +46 -0
- package/.claude/worktrees/agent-a0a8dd02/.claude/commands/a2a-uninstall.md +36 -0
- package/.claude/worktrees/agent-a0a8dd02/.claude/commands/a2a-update.md +41 -0
- package/.claude/worktrees/agent-a0a8dd02/.node-version +1 -0
- package/.claude/worktrees/agent-a0a8dd02/ARCHITECTURE.md +135 -0
- package/.claude/worktrees/agent-a0a8dd02/CLAUDE-INSTALL.md +156 -0
- package/.claude/worktrees/agent-a0a8dd02/CONVENTIONS.md +179 -0
- package/.claude/worktrees/agent-a0a8dd02/README.md +470 -0
- package/.claude/worktrees/agent-a0a8dd02/SKILL.md +462 -0
- package/.claude/worktrees/agent-a0a8dd02/bin/cli.js +3184 -0
- package/.claude/worktrees/agent-a0a8dd02/biome.json +27 -0
- package/.claude/worktrees/agent-a0a8dd02/docs/assessments/2026-02-27-google-a2a-protocol-assessment.md +292 -0
- package/.claude/worktrees/agent-a0a8dd02/docs/plans/2026-02-15-port-fallback-warning-design.md +611 -0
- package/.claude/worktrees/agent-a0a8dd02/docs/plans/2026-02-16-a2a-callbook-macos-app.md +1660 -0
- package/.claude/worktrees/agent-a0a8dd02/docs/plans/2026-02-16-auto-updater.md +1284 -0
- package/.claude/worktrees/agent-a0a8dd02/docs/plans/2026-02-16-bugfixes-22-24.md +246 -0
- package/.claude/worktrees/agent-a0a8dd02/docs/plans/2026-02-16-e2e-test-prompt-sequence.md +3085 -0
- package/.claude/worktrees/agent-a0a8dd02/docs/plans/2026-02-16-orphan-process-fix.md +962 -0
- package/.claude/worktrees/agent-a0a8dd02/docs/plans/2026-02-17-claude-code-codex-skills.md +770 -0
- package/.claude/worktrees/agent-a0a8dd02/docs/plans/2026-03-01-a2a-68-openclaw-integration-tests.md +676 -0
- package/.claude/worktrees/agent-a0a8dd02/docs/plans/2026-03-01-a2a-77-invoke-security-tests.md +661 -0
- package/.claude/worktrees/agent-a0a8dd02/docs/prompts/e2e-test-agent.md +368 -0
- package/.claude/worktrees/agent-a0a8dd02/docs/protocol.md +440 -0
- package/.claude/worktrees/agent-a0a8dd02/docs/signing-setup.md +49 -0
- package/.claude/worktrees/agent-a0a8dd02/eslint.config.js +16 -0
- package/.claude/worktrees/agent-a0a8dd02/knip.json +17 -0
- package/.claude/worktrees/agent-a0a8dd02/native/macos/index.html +179 -0
- package/.claude/worktrees/agent-a0a8dd02/native/macos/package.json +8 -0
- package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/Cargo.lock +5875 -0
- package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/Cargo.toml +25 -0
- package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/binaries/.gitkeep +0 -0
- package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/build.rs +3 -0
- package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/capabilities/default.json +26 -0
- package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/entitlements.plist +14 -0
- package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/icons/128x128.png +0 -0
- package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/icons/128x128@2x.png +0 -0
- package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/icons/32x32.png +0 -0
- package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/icons/icon.icns +0 -0
- package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/icons/tray-connected.png +0 -0
- package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/icons/tray-disconnected.png +0 -0
- package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/src/discovery.rs +184 -0
- package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/src/health.rs +67 -0
- package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/src/lib.rs +256 -0
- package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/src/main.rs +6 -0
- package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/src/notifications.rs +180 -0
- package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/src/server.rs +306 -0
- package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/src/updater.rs +124 -0
- package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/tauri.conf.json +58 -0
- package/.claude/worktrees/agent-a0a8dd02/package.json +54 -0
- package/.claude/worktrees/agent-a0a8dd02/pkg.config.json +14 -0
- package/.claude/worktrees/agent-a0a8dd02/scripts/build-standalone.sh +106 -0
- package/.claude/worktrees/agent-a0a8dd02/scripts/cleanup.js +251 -0
- package/.claude/worktrees/agent-a0a8dd02/scripts/generate-update-manifest.sh +42 -0
- package/.claude/worktrees/agent-a0a8dd02/scripts/install-openclaw.js +986 -0
- package/.claude/worktrees/agent-a0a8dd02/scripts/install-skills.js +234 -0
- package/.claude/worktrees/agent-a0a8dd02/scripts/postinstall.js +224 -0
- package/.claude/worktrees/agent-a0a8dd02/scripts/preuninstall.js +68 -0
- package/.claude/worktrees/agent-a0a8dd02/scripts/run-e2e.sh +44 -0
- package/.claude/worktrees/agent-a0a8dd02/scripts/smoke-test-standalone.sh +101 -0
- package/.claude/worktrees/agent-a0a8dd02/scripts/sync-version.sh +28 -0
- package/.claude/worktrees/agent-a0a8dd02/scripts/verify-app-bundle.sh +34 -0
- package/.claude/worktrees/agent-a0a8dd02/src/dashboard/public/app.js +2683 -0
- package/.claude/worktrees/agent-a0a8dd02/src/dashboard/public/index.html +393 -0
- package/.claude/worktrees/agent-a0a8dd02/src/dashboard/public/style.css +1294 -0
- package/.claude/worktrees/agent-a0a8dd02/src/index.js +76 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/agent-card.js +111 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/call-monitor.js +205 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/callbook.js +366 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/claude-subagent.js +696 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/client.js +683 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/config.js +480 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/conversation-driver.js +608 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/conversations.js +830 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/crypto.js +113 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/dashboard-events.js +213 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/disclosure.js +792 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/external-ip.js +211 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/invite-host.js +223 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/local-request.js +69 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/logger.js +677 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/openclaw-integration.js +339 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/pid-file.js +103 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/port-scanner.js +83 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/prompt-template.js +355 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/runtime-adapter.js +701 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/summarizer.js +156 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/summary-formatter.js +168 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/summary-prompt.js +203 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/tokens.js +868 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/turn-timeout.js +52 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/update-checker.js +93 -0
- package/.claude/worktrees/agent-a0a8dd02/src/lib/update-manager.js +313 -0
- package/.claude/worktrees/agent-a0a8dd02/src/routes/a2a.js +1213 -0
- package/.claude/worktrees/agent-a0a8dd02/src/routes/callbook.js +142 -0
- package/.claude/worktrees/agent-a0a8dd02/src/routes/dashboard.js +1578 -0
- package/.claude/worktrees/agent-a0a8dd02/src/server.js +1179 -0
- package/.claude/worktrees/agent-a3c12538/.a2a-manifest.json +70 -0
- package/.claude/worktrees/agent-a3c12538/.c8rc.json +16 -0
- package/.claude/worktrees/agent-a3c12538/.claude/a2a-skill-reference.md +462 -0
- package/.claude/worktrees/agent-a3c12538/.claude/commands/a2a-app.md +42 -0
- package/.claude/worktrees/agent-a3c12538/.claude/commands/a2a-call.md +26 -0
- package/.claude/worktrees/agent-a3c12538/.claude/commands/a2a-contacts.md +31 -0
- package/.claude/worktrees/agent-a3c12538/.claude/commands/a2a-conversations.md +47 -0
- package/.claude/worktrees/agent-a3c12538/.claude/commands/a2a-gui.md +30 -0
- package/.claude/worktrees/agent-a3c12538/.claude/commands/a2a-invite.md +63 -0
- package/.claude/worktrees/agent-a3c12538/.claude/commands/a2a-setup.md +30 -0
- package/.claude/worktrees/agent-a3c12538/.claude/commands/a2a-skills.md +27 -0
- package/.claude/worktrees/agent-a3c12538/.claude/commands/a2a-status.md +46 -0
- package/.claude/worktrees/agent-a3c12538/.claude/commands/a2a-uninstall.md +36 -0
- package/.claude/worktrees/agent-a3c12538/.claude/commands/a2a-update.md +41 -0
- package/.claude/worktrees/agent-a3c12538/.node-version +1 -0
- package/.claude/worktrees/agent-a3c12538/ARCHITECTURE.md +135 -0
- package/.claude/worktrees/agent-a3c12538/CLAUDE-INSTALL.md +156 -0
- package/.claude/worktrees/agent-a3c12538/CONVENTIONS.md +169 -0
- package/.claude/worktrees/agent-a3c12538/README.md +470 -0
- package/.claude/worktrees/agent-a3c12538/SKILL.md +462 -0
- package/.claude/worktrees/agent-a3c12538/bin/cli.js +3184 -0
- package/.claude/worktrees/agent-a3c12538/biome.json +27 -0
- package/.claude/worktrees/agent-a3c12538/docs/app.js +30 -0
- package/.claude/worktrees/agent-a3c12538/docs/assessments/2026-02-27-google-a2a-protocol-assessment.md +292 -0
- package/.claude/worktrees/agent-a3c12538/docs/assets/icon-32.png +0 -0
- package/.claude/worktrees/agent-a3c12538/docs/assets/icon-64.png +0 -0
- package/.claude/worktrees/agent-a3c12538/docs/index.html +117 -0
- package/.claude/worktrees/agent-a3c12538/docs/plans/2026-02-15-port-fallback-warning-design.md +611 -0
- package/.claude/worktrees/agent-a3c12538/docs/plans/2026-02-16-a2a-callbook-macos-app.md +1660 -0
- package/.claude/worktrees/agent-a3c12538/docs/plans/2026-02-16-auto-updater.md +1284 -0
- package/.claude/worktrees/agent-a3c12538/docs/plans/2026-02-16-bugfixes-22-24.md +246 -0
- package/.claude/worktrees/agent-a3c12538/docs/plans/2026-02-16-e2e-test-prompt-sequence.md +3085 -0
- package/.claude/worktrees/agent-a3c12538/docs/plans/2026-02-16-orphan-process-fix.md +962 -0
- package/.claude/worktrees/agent-a3c12538/docs/plans/2026-02-17-claude-code-codex-skills.md +770 -0
- package/.claude/worktrees/agent-a3c12538/docs/plans/2026-03-01-a2a-68-openclaw-integration-tests.md +676 -0
- package/.claude/worktrees/agent-a3c12538/docs/plans/2026-03-01-a2a-77-invoke-security-tests.md +661 -0
- package/.claude/worktrees/agent-a3c12538/docs/prompts/e2e-test-agent.md +368 -0
- package/.claude/worktrees/agent-a3c12538/docs/protocol.md +440 -0
- package/.claude/worktrees/agent-a3c12538/docs/signing-setup.md +49 -0
- package/.claude/worktrees/agent-a3c12538/docs/style.css +209 -0
- package/.claude/worktrees/agent-a3c12538/eslint.config.js +16 -0
- package/.claude/worktrees/agent-a3c12538/knip.json +17 -0
- package/.claude/worktrees/agent-a3c12538/native/macos/index.html +179 -0
- package/.claude/worktrees/agent-a3c12538/native/macos/package.json +8 -0
- package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/Cargo.lock +5875 -0
- package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/Cargo.toml +24 -0
- package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/binaries/.gitkeep +0 -0
- package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/build.rs +3 -0
- package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/capabilities/default.json +26 -0
- package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/entitlements.plist +14 -0
- package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/icons/128x128.png +0 -0
- package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/icons/128x128@2x.png +0 -0
- package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/icons/32x32.png +0 -0
- package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/icons/icon.icns +0 -0
- package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/icons/tray-connected.png +0 -0
- package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/icons/tray-disconnected.png +0 -0
- package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/src/discovery.rs +184 -0
- package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/src/health.rs +67 -0
- package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/src/lib.rs +226 -0
- package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/src/main.rs +6 -0
- package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/src/notifications.rs +180 -0
- package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/src/server.rs +306 -0
- package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/tauri.conf.json +50 -0
- package/.claude/worktrees/agent-a3c12538/package.json +54 -0
- package/.claude/worktrees/agent-a3c12538/pkg.config.json +14 -0
- package/.claude/worktrees/agent-a3c12538/scripts/build-standalone.sh +106 -0
- package/.claude/worktrees/agent-a3c12538/scripts/cleanup.js +251 -0
- package/.claude/worktrees/agent-a3c12538/scripts/install-openclaw.js +986 -0
- package/.claude/worktrees/agent-a3c12538/scripts/install-skills.js +234 -0
- package/.claude/worktrees/agent-a3c12538/scripts/postinstall.js +224 -0
- package/.claude/worktrees/agent-a3c12538/scripts/preuninstall.js +68 -0
- package/.claude/worktrees/agent-a3c12538/scripts/run-e2e.sh +44 -0
- package/.claude/worktrees/agent-a3c12538/scripts/smoke-test-standalone.sh +101 -0
- package/.claude/worktrees/agent-a3c12538/scripts/sync-version.sh +28 -0
- package/.claude/worktrees/agent-a3c12538/scripts/verify-app-bundle.sh +34 -0
- package/.claude/worktrees/agent-a3c12538/src/dashboard/public/app.js +2649 -0
- package/.claude/worktrees/agent-a3c12538/src/dashboard/public/index.html +386 -0
- package/.claude/worktrees/agent-a3c12538/src/dashboard/public/style.css +1274 -0
- package/.claude/worktrees/agent-a3c12538/src/index.js +76 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/agent-card.js +111 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/call-monitor.js +205 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/callbook.js +366 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/claude-subagent.js +696 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/client.js +683 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/config.js +480 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/conversation-driver.js +608 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/conversations.js +830 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/crypto.js +113 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/dashboard-events.js +213 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/disclosure.js +792 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/external-ip.js +211 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/invite-host.js +223 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/local-request.js +69 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/logger.js +677 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/openclaw-integration.js +339 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/pid-file.js +103 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/port-scanner.js +83 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/prompt-template.js +355 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/runtime-adapter.js +701 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/summarizer.js +156 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/summary-formatter.js +168 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/summary-prompt.js +203 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/tokens.js +868 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/turn-timeout.js +52 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/update-checker.js +93 -0
- package/.claude/worktrees/agent-a3c12538/src/lib/update-manager.js +313 -0
- package/.claude/worktrees/agent-a3c12538/src/routes/a2a.js +1213 -0
- package/.claude/worktrees/agent-a3c12538/src/routes/callbook.js +142 -0
- package/.claude/worktrees/agent-a3c12538/src/routes/dashboard.js +1578 -0
- package/.claude/worktrees/agent-a3c12538/src/server.js +1179 -0
- package/.claude/worktrees/agent-a5b87d75/.a2a-manifest.json +70 -0
- package/.claude/worktrees/agent-a5b87d75/.c8rc.json +16 -0
- package/.claude/worktrees/agent-a5b87d75/.claude/a2a-skill-reference.md +462 -0
- package/.claude/worktrees/agent-a5b87d75/.claude/commands/a2a-app.md +42 -0
- package/.claude/worktrees/agent-a5b87d75/.claude/commands/a2a-call.md +26 -0
- package/.claude/worktrees/agent-a5b87d75/.claude/commands/a2a-contacts.md +31 -0
- package/.claude/worktrees/agent-a5b87d75/.claude/commands/a2a-conversations.md +47 -0
- package/.claude/worktrees/agent-a5b87d75/.claude/commands/a2a-gui.md +30 -0
- package/.claude/worktrees/agent-a5b87d75/.claude/commands/a2a-invite.md +63 -0
- package/.claude/worktrees/agent-a5b87d75/.claude/commands/a2a-setup.md +30 -0
- package/.claude/worktrees/agent-a5b87d75/.claude/commands/a2a-skills.md +27 -0
- package/.claude/worktrees/agent-a5b87d75/.claude/commands/a2a-status.md +46 -0
- package/.claude/worktrees/agent-a5b87d75/.claude/commands/a2a-uninstall.md +36 -0
- package/.claude/worktrees/agent-a5b87d75/.claude/commands/a2a-update.md +41 -0
- package/.claude/worktrees/agent-a5b87d75/.node-version +1 -0
- package/.claude/worktrees/agent-a5b87d75/ARCHITECTURE.md +135 -0
- package/.claude/worktrees/agent-a5b87d75/CLAUDE-INSTALL.md +156 -0
- package/.claude/worktrees/agent-a5b87d75/CONVENTIONS.md +208 -0
- package/.claude/worktrees/agent-a5b87d75/README.md +470 -0
- package/.claude/worktrees/agent-a5b87d75/SKILL.md +462 -0
- package/.claude/worktrees/agent-a5b87d75/bin/cli.js +3184 -0
- package/.claude/worktrees/agent-a5b87d75/biome.json +27 -0
- package/.claude/worktrees/agent-a5b87d75/docs/app.js +30 -0
- package/.claude/worktrees/agent-a5b87d75/docs/assessments/2026-02-27-google-a2a-protocol-assessment.md +292 -0
- package/.claude/worktrees/agent-a5b87d75/docs/assets/icon-32.png +0 -0
- package/.claude/worktrees/agent-a5b87d75/docs/assets/icon-64.png +0 -0
- package/.claude/worktrees/agent-a5b87d75/docs/index.html +117 -0
- package/.claude/worktrees/agent-a5b87d75/docs/plans/2026-02-15-port-fallback-warning-design.md +611 -0
- package/.claude/worktrees/agent-a5b87d75/docs/plans/2026-02-16-a2a-callbook-macos-app.md +1660 -0
- package/.claude/worktrees/agent-a5b87d75/docs/plans/2026-02-16-auto-updater.md +1284 -0
- package/.claude/worktrees/agent-a5b87d75/docs/plans/2026-02-16-bugfixes-22-24.md +246 -0
- package/.claude/worktrees/agent-a5b87d75/docs/plans/2026-02-16-e2e-test-prompt-sequence.md +3085 -0
- package/.claude/worktrees/agent-a5b87d75/docs/plans/2026-02-16-orphan-process-fix.md +962 -0
- package/.claude/worktrees/agent-a5b87d75/docs/plans/2026-02-17-claude-code-codex-skills.md +770 -0
- package/.claude/worktrees/agent-a5b87d75/docs/plans/2026-03-01-a2a-68-openclaw-integration-tests.md +676 -0
- package/.claude/worktrees/agent-a5b87d75/docs/plans/2026-03-01-a2a-77-invoke-security-tests.md +661 -0
- package/.claude/worktrees/agent-a5b87d75/docs/prompts/e2e-test-agent.md +368 -0
- package/.claude/worktrees/agent-a5b87d75/docs/protocol.md +440 -0
- package/.claude/worktrees/agent-a5b87d75/docs/signing-setup.md +49 -0
- package/.claude/worktrees/agent-a5b87d75/docs/style.css +209 -0
- package/.claude/worktrees/agent-a5b87d75/eslint.config.js +16 -0
- package/.claude/worktrees/agent-a5b87d75/knip.json +17 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/index.html +182 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/package.json +8 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/Cargo.lock +5875 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/Cargo.toml +25 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/binaries/.gitkeep +0 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/build.rs +3 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/capabilities/default.json +26 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/dmg-background.png +0 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/dmg-background@2x.png +0 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/entitlements.plist +14 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/icons/128x128.png +0 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/icons/128x128@2x.png +0 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/icons/256x256.png +0 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/icons/32x32.png +0 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/icons/512x512.png +0 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/icons/512x512@2x.png +0 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/icons/icon.icns +0 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/icons/tray-connected.png +0 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/icons/tray-disconnected.png +0 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/src/discovery.rs +221 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/src/health.rs +67 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/src/lib.rs +256 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/src/main.rs +6 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/src/notifications.rs +180 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/src/server.rs +306 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/src/updater.rs +124 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/tauri.conf.json +68 -0
- package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/volume-icon.icns +0 -0
- package/.claude/worktrees/agent-a5b87d75/package.json +55 -0
- package/.claude/worktrees/agent-a5b87d75/pkg.config.json +14 -0
- package/.claude/worktrees/agent-a5b87d75/scripts/build-standalone.sh +106 -0
- package/.claude/worktrees/agent-a5b87d75/scripts/cleanup.js +251 -0
- package/.claude/worktrees/agent-a5b87d75/scripts/generate-update-manifest.sh +42 -0
- package/.claude/worktrees/agent-a5b87d75/scripts/install-openclaw.js +986 -0
- package/.claude/worktrees/agent-a5b87d75/scripts/install-skills.js +234 -0
- package/.claude/worktrees/agent-a5b87d75/scripts/postinstall.js +224 -0
- package/.claude/worktrees/agent-a5b87d75/scripts/preuninstall.js +68 -0
- package/.claude/worktrees/agent-a5b87d75/scripts/run-e2e.sh +52 -0
- package/.claude/worktrees/agent-a5b87d75/scripts/smoke-test-standalone.sh +101 -0
- package/.claude/worktrees/agent-a5b87d75/scripts/sync-version.sh +28 -0
- package/.claude/worktrees/agent-a5b87d75/scripts/verify-app-bundle.sh +34 -0
- package/.claude/worktrees/agent-a5b87d75/src/dashboard/public/app.js +2683 -0
- package/.claude/worktrees/agent-a5b87d75/src/dashboard/public/index.html +393 -0
- package/.claude/worktrees/agent-a5b87d75/src/dashboard/public/onboarding.css +269 -0
- package/.claude/worktrees/agent-a5b87d75/src/dashboard/public/onboarding.html +96 -0
- package/.claude/worktrees/agent-a5b87d75/src/dashboard/public/onboarding.js +277 -0
- package/.claude/worktrees/agent-a5b87d75/src/dashboard/public/style.css +1294 -0
- package/.claude/worktrees/agent-a5b87d75/src/index.js +76 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/agent-card.js +111 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/call-monitor.js +205 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/callbook.js +366 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/claude-subagent.js +696 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/client.js +683 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/config.js +480 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/conversation-driver.js +608 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/conversations.js +830 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/crypto.js +113 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/dashboard-events.js +213 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/disclosure.js +792 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/external-ip.js +211 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/invite-host.js +223 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/local-request.js +69 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/logger.js +677 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/openclaw-integration.js +339 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/pid-file.js +103 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/port-scanner.js +83 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/prompt-template.js +355 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/runtime-adapter.js +701 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/summarizer.js +156 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/summary-formatter.js +168 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/summary-prompt.js +203 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/tokens.js +868 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/turn-timeout.js +52 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/update-checker.js +93 -0
- package/.claude/worktrees/agent-a5b87d75/src/lib/update-manager.js +313 -0
- package/.claude/worktrees/agent-a5b87d75/src/routes/a2a.js +1213 -0
- package/.claude/worktrees/agent-a5b87d75/src/routes/callbook.js +142 -0
- package/.claude/worktrees/agent-a5b87d75/src/routes/dashboard.js +1688 -0
- package/.claude/worktrees/agent-a5b87d75/src/server.js +1179 -0
- package/.claude/worktrees/agent-acefedbd/.a2a-manifest.json +70 -0
- package/.claude/worktrees/agent-acefedbd/.c8rc.json +16 -0
- package/.claude/worktrees/agent-acefedbd/.claude/a2a-skill-reference.md +462 -0
- package/.claude/worktrees/agent-acefedbd/.claude/commands/a2a-app.md +42 -0
- package/.claude/worktrees/agent-acefedbd/.claude/commands/a2a-call.md +26 -0
- package/.claude/worktrees/agent-acefedbd/.claude/commands/a2a-contacts.md +31 -0
- package/.claude/worktrees/agent-acefedbd/.claude/commands/a2a-conversations.md +47 -0
- package/.claude/worktrees/agent-acefedbd/.claude/commands/a2a-gui.md +30 -0
- package/.claude/worktrees/agent-acefedbd/.claude/commands/a2a-invite.md +63 -0
- package/.claude/worktrees/agent-acefedbd/.claude/commands/a2a-setup.md +30 -0
- package/.claude/worktrees/agent-acefedbd/.claude/commands/a2a-skills.md +27 -0
- package/.claude/worktrees/agent-acefedbd/.claude/commands/a2a-status.md +46 -0
- package/.claude/worktrees/agent-acefedbd/.claude/commands/a2a-uninstall.md +36 -0
- package/.claude/worktrees/agent-acefedbd/.claude/commands/a2a-update.md +41 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.a2a-manifest.json +70 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.c8rc.json +16 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/a2a-skill-reference.md +462 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/commands/a2a-app.md +42 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/commands/a2a-call.md +26 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/commands/a2a-contacts.md +31 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/commands/a2a-conversations.md +47 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/commands/a2a-gui.md +30 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/commands/a2a-invite.md +63 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/commands/a2a-setup.md +30 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/commands/a2a-skills.md +27 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/commands/a2a-status.md +46 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/commands/a2a-uninstall.md +36 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/commands/a2a-update.md +41 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.node-version +1 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/ARCHITECTURE.md +135 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/CLAUDE-INSTALL.md +156 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/CONVENTIONS.md +179 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/README.md +470 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/SKILL.md +462 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/bin/cli.js +3184 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/biome.json +27 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/assessments/2026-02-27-google-a2a-protocol-assessment.md +292 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/plans/2026-02-15-port-fallback-warning-design.md +611 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/plans/2026-02-16-a2a-callbook-macos-app.md +1660 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/plans/2026-02-16-auto-updater.md +1284 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/plans/2026-02-16-bugfixes-22-24.md +246 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/plans/2026-02-16-e2e-test-prompt-sequence.md +3085 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/plans/2026-02-16-orphan-process-fix.md +962 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/plans/2026-02-17-claude-code-codex-skills.md +770 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/plans/2026-03-01-a2a-68-openclaw-integration-tests.md +676 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/plans/2026-03-01-a2a-77-invoke-security-tests.md +661 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/prompts/e2e-test-agent.md +368 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/protocol.md +440 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/signing-setup.md +49 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/eslint.config.js +16 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/knip.json +17 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/index.html +179 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/package.json +8 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/Cargo.lock +5875 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/Cargo.toml +25 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/binaries/.gitkeep +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/build.rs +3 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/capabilities/default.json +26 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/entitlements.plist +14 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/icons/128x128.png +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/icons/128x128@2x.png +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/icons/32x32.png +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/icons/icon.icns +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/icons/tray-connected.png +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/icons/tray-disconnected.png +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/src/discovery.rs +184 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/src/health.rs +67 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/src/lib.rs +255 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/src/main.rs +6 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/src/notifications.rs +180 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/src/server.rs +306 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/src/updater.rs +117 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/tauri.conf.json +58 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/package.json +54 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/pkg.config.json +14 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/scripts/build-standalone.sh +106 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/scripts/cleanup.js +251 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/scripts/generate-update-manifest.sh +42 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/scripts/install-openclaw.js +986 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/scripts/install-skills.js +234 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/scripts/postinstall.js +224 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/scripts/preuninstall.js +68 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/scripts/run-e2e.sh +44 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/scripts/smoke-test-standalone.sh +101 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/scripts/sync-version.sh +28 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/scripts/verify-app-bundle.sh +34 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/dashboard/public/app.js +2683 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/dashboard/public/index.html +394 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/dashboard/public/style.css +1294 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/index.js +76 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/agent-card.js +111 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/call-monitor.js +205 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/callbook.js +366 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/claude-subagent.js +696 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/client.js +683 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/config.js +480 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/conversation-driver.js +608 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/conversations.js +830 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/crypto.js +113 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/dashboard-events.js +213 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/disclosure.js +792 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/external-ip.js +211 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/invite-host.js +223 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/local-request.js +69 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/logger.js +677 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/openclaw-integration.js +339 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/pid-file.js +103 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/port-scanner.js +83 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/prompt-template.js +355 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/runtime-adapter.js +701 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/summarizer.js +156 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/summary-formatter.js +168 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/summary-prompt.js +203 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/tokens.js +868 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/turn-timeout.js +52 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/update-checker.js +93 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/update-manager.js +313 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/routes/a2a.js +1213 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/routes/callbook.js +142 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/routes/dashboard.js +1578 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/server.js +1179 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.a2a-manifest.json +70 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.c8rc.json +16 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/a2a-skill-reference.md +462 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/commands/a2a-app.md +42 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/commands/a2a-call.md +26 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/commands/a2a-contacts.md +31 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/commands/a2a-conversations.md +47 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/commands/a2a-gui.md +30 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/commands/a2a-invite.md +63 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/commands/a2a-setup.md +30 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/commands/a2a-skills.md +27 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/commands/a2a-status.md +46 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/commands/a2a-uninstall.md +36 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/commands/a2a-update.md +41 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.node-version +1 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/ARCHITECTURE.md +135 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/CLAUDE-INSTALL.md +156 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/CONVENTIONS.md +178 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/README.md +470 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/SKILL.md +462 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/bin/cli.js +3184 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/biome.json +27 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/assessments/2026-02-27-google-a2a-protocol-assessment.md +292 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/plans/2026-02-15-port-fallback-warning-design.md +611 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/plans/2026-02-16-a2a-callbook-macos-app.md +1660 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/plans/2026-02-16-auto-updater.md +1284 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/plans/2026-02-16-bugfixes-22-24.md +246 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/plans/2026-02-16-e2e-test-prompt-sequence.md +3085 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/plans/2026-02-16-orphan-process-fix.md +962 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/plans/2026-02-17-claude-code-codex-skills.md +770 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/plans/2026-03-01-a2a-68-openclaw-integration-tests.md +676 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/plans/2026-03-01-a2a-77-invoke-security-tests.md +661 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/prompts/e2e-test-agent.md +368 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/protocol.md +440 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/signing-setup.md +49 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/eslint.config.js +16 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/knip.json +17 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/index.html +179 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/package.json +8 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/Cargo.lock +5875 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/Cargo.toml +24 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/binaries/.gitkeep +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/build.rs +3 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/capabilities/default.json +26 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/dmg-background.png +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/dmg-background@2x.png +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/entitlements.plist +14 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/icons/128x128.png +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/icons/128x128@2x.png +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/icons/256x256.png +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/icons/32x32.png +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/icons/512x512.png +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/icons/512x512@2x.png +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/icons/icon.icns +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/icons/tray-connected.png +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/icons/tray-disconnected.png +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/src/discovery.rs +184 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/src/health.rs +67 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/src/lib.rs +226 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/src/main.rs +6 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/src/notifications.rs +180 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/src/server.rs +306 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/tauri.conf.json +60 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/volume-icon.icns +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/package.json +54 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/pkg.config.json +14 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/scripts/build-standalone.sh +106 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/scripts/cleanup.js +251 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/scripts/install-openclaw.js +986 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/scripts/install-skills.js +234 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/scripts/postinstall.js +224 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/scripts/preuninstall.js +68 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/scripts/run-e2e.sh +44 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/scripts/smoke-test-standalone.sh +101 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/scripts/sync-version.sh +28 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/scripts/verify-app-bundle.sh +34 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/dashboard/public/app.js +2649 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/dashboard/public/index.html +386 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/dashboard/public/style.css +1274 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/index.js +76 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/agent-card.js +111 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/call-monitor.js +205 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/callbook.js +366 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/claude-subagent.js +696 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/client.js +683 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/config.js +480 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/conversation-driver.js +608 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/conversations.js +830 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/crypto.js +113 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/dashboard-events.js +213 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/disclosure.js +792 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/external-ip.js +211 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/invite-host.js +223 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/local-request.js +69 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/logger.js +677 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/openclaw-integration.js +339 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/pid-file.js +103 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/port-scanner.js +83 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/prompt-template.js +355 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/runtime-adapter.js +701 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/summarizer.js +156 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/summary-formatter.js +168 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/summary-prompt.js +203 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/tokens.js +868 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/turn-timeout.js +52 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/update-checker.js +93 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/update-manager.js +313 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/routes/a2a.js +1213 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/routes/callbook.js +142 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/routes/dashboard.js +1578 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/server.js +1179 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.a2a-manifest.json +70 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.c8rc.json +16 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/a2a-skill-reference.md +462 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/commands/a2a-app.md +42 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/commands/a2a-call.md +26 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/commands/a2a-contacts.md +31 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/commands/a2a-conversations.md +47 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/commands/a2a-gui.md +30 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/commands/a2a-invite.md +63 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/commands/a2a-setup.md +30 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/commands/a2a-skills.md +27 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/commands/a2a-status.md +46 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/commands/a2a-uninstall.md +36 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/commands/a2a-update.md +41 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.node-version +1 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/ARCHITECTURE.md +135 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/CLAUDE-INSTALL.md +156 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/CONVENTIONS.md +169 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/README.md +470 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/SKILL.md +462 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/bin/cli.js +3184 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/biome.json +27 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/app.js +160 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/assessments/2026-02-27-google-a2a-protocol-assessment.md +292 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/assets/icon-32.png +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/assets/icon-64.png +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/index.html +166 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/plans/2026-02-15-port-fallback-warning-design.md +611 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/plans/2026-02-16-a2a-callbook-macos-app.md +1660 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/plans/2026-02-16-auto-updater.md +1284 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/plans/2026-02-16-bugfixes-22-24.md +246 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/plans/2026-02-16-e2e-test-prompt-sequence.md +3085 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/plans/2026-02-16-orphan-process-fix.md +962 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/plans/2026-02-17-claude-code-codex-skills.md +770 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/plans/2026-03-01-a2a-68-openclaw-integration-tests.md +676 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/plans/2026-03-01-a2a-77-invoke-security-tests.md +661 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/prompts/e2e-test-agent.md +368 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/protocol.md +440 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/signing-setup.md +49 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/style.css +457 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/eslint.config.js +16 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/knip.json +17 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/index.html +179 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/package.json +8 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/Cargo.lock +5875 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/Cargo.toml +24 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/binaries/.gitkeep +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/build.rs +3 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/capabilities/default.json +26 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/entitlements.plist +14 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/icons/128x128.png +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/icons/128x128@2x.png +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/icons/32x32.png +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/icons/icon.icns +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/icons/tray-connected.png +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/icons/tray-disconnected.png +0 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/src/discovery.rs +184 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/src/health.rs +67 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/src/lib.rs +226 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/src/main.rs +6 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/src/notifications.rs +180 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/src/server.rs +306 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/tauri.conf.json +50 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/package.json +54 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/pkg.config.json +14 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/scripts/build-standalone.sh +106 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/scripts/cleanup.js +251 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/scripts/install-openclaw.js +986 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/scripts/install-skills.js +234 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/scripts/postinstall.js +224 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/scripts/preuninstall.js +68 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/scripts/run-e2e.sh +44 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/scripts/smoke-test-standalone.sh +101 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/scripts/sync-version.sh +28 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/scripts/verify-app-bundle.sh +34 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/dashboard/public/app.js +2649 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/dashboard/public/index.html +386 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/dashboard/public/style.css +1274 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/index.js +76 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/agent-card.js +111 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/call-monitor.js +205 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/callbook.js +366 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/claude-subagent.js +696 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/client.js +683 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/config.js +480 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/conversation-driver.js +608 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/conversations.js +830 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/crypto.js +113 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/dashboard-events.js +213 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/disclosure.js +792 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/external-ip.js +211 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/invite-host.js +223 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/local-request.js +69 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/logger.js +677 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/openclaw-integration.js +339 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/pid-file.js +103 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/port-scanner.js +83 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/prompt-template.js +355 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/runtime-adapter.js +701 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/summarizer.js +156 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/summary-formatter.js +168 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/summary-prompt.js +203 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/tokens.js +868 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/turn-timeout.js +52 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/update-checker.js +93 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/update-manager.js +313 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/routes/a2a.js +1213 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/routes/callbook.js +142 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/routes/dashboard.js +1578 -0
- package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/server.js +1179 -0
- package/.claude/worktrees/agent-acefedbd/.node-version +1 -0
- package/.claude/worktrees/agent-acefedbd/ARCHITECTURE.md +135 -0
- package/.claude/worktrees/agent-acefedbd/CLAUDE-INSTALL.md +156 -0
- package/.claude/worktrees/agent-acefedbd/CONVENTIONS.md +177 -0
- package/.claude/worktrees/agent-acefedbd/README.md +470 -0
- package/.claude/worktrees/agent-acefedbd/SKILL.md +462 -0
- package/.claude/worktrees/agent-acefedbd/bin/cli.js +3184 -0
- package/.claude/worktrees/agent-acefedbd/biome.json +27 -0
- package/.claude/worktrees/agent-acefedbd/docs/assessments/2026-02-27-google-a2a-protocol-assessment.md +292 -0
- package/.claude/worktrees/agent-acefedbd/docs/plans/2026-02-15-port-fallback-warning-design.md +611 -0
- package/.claude/worktrees/agent-acefedbd/docs/plans/2026-02-16-a2a-callbook-macos-app.md +1660 -0
- package/.claude/worktrees/agent-acefedbd/docs/plans/2026-02-16-auto-updater.md +1284 -0
- package/.claude/worktrees/agent-acefedbd/docs/plans/2026-02-16-bugfixes-22-24.md +246 -0
- package/.claude/worktrees/agent-acefedbd/docs/plans/2026-02-16-e2e-test-prompt-sequence.md +3085 -0
- package/.claude/worktrees/agent-acefedbd/docs/plans/2026-02-16-orphan-process-fix.md +962 -0
- package/.claude/worktrees/agent-acefedbd/docs/plans/2026-02-17-claude-code-codex-skills.md +770 -0
- package/.claude/worktrees/agent-acefedbd/docs/plans/2026-03-01-a2a-68-openclaw-integration-tests.md +676 -0
- package/.claude/worktrees/agent-acefedbd/docs/plans/2026-03-01-a2a-77-invoke-security-tests.md +661 -0
- package/.claude/worktrees/agent-acefedbd/docs/prompts/e2e-test-agent.md +368 -0
- package/.claude/worktrees/agent-acefedbd/docs/protocol.md +440 -0
- package/.claude/worktrees/agent-acefedbd/docs/signing-setup.md +49 -0
- package/.claude/worktrees/agent-acefedbd/eslint.config.js +16 -0
- package/.claude/worktrees/agent-acefedbd/knip.json +17 -0
- package/.claude/worktrees/agent-acefedbd/native/macos/index.html +182 -0
- package/.claude/worktrees/agent-acefedbd/native/macos/package.json +8 -0
- package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/Cargo.lock +5875 -0
- package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/Cargo.toml +24 -0
- package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/binaries/.gitkeep +0 -0
- package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/build.rs +3 -0
- package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/capabilities/default.json +26 -0
- package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/entitlements.plist +14 -0
- package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/icons/128x128.png +0 -0
- package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/icons/128x128@2x.png +0 -0
- package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/icons/32x32.png +0 -0
- package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/icons/icon.icns +0 -0
- package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/icons/tray-connected.png +0 -0
- package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/icons/tray-disconnected.png +0 -0
- package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/src/discovery.rs +221 -0
- package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/src/health.rs +67 -0
- package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/src/lib.rs +226 -0
- package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/src/main.rs +6 -0
- package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/src/notifications.rs +180 -0
- package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/src/server.rs +306 -0
- package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/tauri.conf.json +50 -0
- package/.claude/worktrees/agent-acefedbd/package.json +54 -0
- package/.claude/worktrees/agent-acefedbd/pkg.config.json +14 -0
- package/.claude/worktrees/agent-acefedbd/scripts/build-standalone.sh +106 -0
- package/.claude/worktrees/agent-acefedbd/scripts/cleanup.js +251 -0
- package/.claude/worktrees/agent-acefedbd/scripts/install-openclaw.js +986 -0
- package/.claude/worktrees/agent-acefedbd/scripts/install-skills.js +234 -0
- package/.claude/worktrees/agent-acefedbd/scripts/postinstall.js +224 -0
- package/.claude/worktrees/agent-acefedbd/scripts/preuninstall.js +68 -0
- package/.claude/worktrees/agent-acefedbd/scripts/run-e2e.sh +44 -0
- package/.claude/worktrees/agent-acefedbd/scripts/smoke-test-standalone.sh +101 -0
- package/.claude/worktrees/agent-acefedbd/scripts/sync-version.sh +28 -0
- package/.claude/worktrees/agent-acefedbd/scripts/verify-app-bundle.sh +34 -0
- package/.claude/worktrees/agent-acefedbd/src/dashboard/public/app.js +2649 -0
- package/.claude/worktrees/agent-acefedbd/src/dashboard/public/index.html +386 -0
- package/.claude/worktrees/agent-acefedbd/src/dashboard/public/onboarding.css +269 -0
- package/.claude/worktrees/agent-acefedbd/src/dashboard/public/onboarding.html +96 -0
- package/.claude/worktrees/agent-acefedbd/src/dashboard/public/onboarding.js +277 -0
- package/.claude/worktrees/agent-acefedbd/src/dashboard/public/style.css +1274 -0
- package/.claude/worktrees/agent-acefedbd/src/index.js +76 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/agent-card.js +111 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/call-monitor.js +205 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/callbook.js +366 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/claude-subagent.js +696 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/client.js +683 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/config.js +480 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/conversation-driver.js +608 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/conversations.js +830 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/crypto.js +113 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/dashboard-events.js +213 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/disclosure.js +792 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/external-ip.js +211 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/invite-host.js +223 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/local-request.js +69 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/logger.js +677 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/openclaw-integration.js +339 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/pid-file.js +103 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/port-scanner.js +83 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/prompt-template.js +355 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/runtime-adapter.js +701 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/summarizer.js +156 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/summary-formatter.js +168 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/summary-prompt.js +203 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/tokens.js +868 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/turn-timeout.js +52 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/update-checker.js +93 -0
- package/.claude/worktrees/agent-acefedbd/src/lib/update-manager.js +313 -0
- package/.claude/worktrees/agent-acefedbd/src/routes/a2a.js +1213 -0
- package/.claude/worktrees/agent-acefedbd/src/routes/callbook.js +142 -0
- package/.claude/worktrees/agent-acefedbd/src/routes/dashboard.js +1688 -0
- package/.claude/worktrees/agent-acefedbd/src/server.js +1179 -0
- package/.claude/worktrees/agent-ad420869/.a2a-manifest.json +70 -0
- package/.claude/worktrees/agent-ad420869/.c8rc.json +16 -0
- package/.claude/worktrees/agent-ad420869/.claude/a2a-skill-reference.md +462 -0
- package/.claude/worktrees/agent-ad420869/.claude/commands/a2a-app.md +42 -0
- package/.claude/worktrees/agent-ad420869/.claude/commands/a2a-call.md +26 -0
- package/.claude/worktrees/agent-ad420869/.claude/commands/a2a-contacts.md +31 -0
- package/.claude/worktrees/agent-ad420869/.claude/commands/a2a-conversations.md +47 -0
- package/.claude/worktrees/agent-ad420869/.claude/commands/a2a-gui.md +30 -0
- package/.claude/worktrees/agent-ad420869/.claude/commands/a2a-invite.md +63 -0
- package/.claude/worktrees/agent-ad420869/.claude/commands/a2a-setup.md +30 -0
- package/.claude/worktrees/agent-ad420869/.claude/commands/a2a-skills.md +27 -0
- package/.claude/worktrees/agent-ad420869/.claude/commands/a2a-status.md +46 -0
- package/.claude/worktrees/agent-ad420869/.claude/commands/a2a-uninstall.md +36 -0
- package/.claude/worktrees/agent-ad420869/.claude/commands/a2a-update.md +41 -0
- package/.claude/worktrees/agent-ad420869/.node-version +1 -0
- package/.claude/worktrees/agent-ad420869/ARCHITECTURE.md +135 -0
- package/.claude/worktrees/agent-ad420869/CLAUDE-INSTALL.md +156 -0
- package/.claude/worktrees/agent-ad420869/CONVENTIONS.md +178 -0
- package/.claude/worktrees/agent-ad420869/README.md +470 -0
- package/.claude/worktrees/agent-ad420869/SKILL.md +462 -0
- package/.claude/worktrees/agent-ad420869/bin/cli.js +3184 -0
- package/.claude/worktrees/agent-ad420869/biome.json +27 -0
- package/.claude/worktrees/agent-ad420869/docs/assessments/2026-02-27-google-a2a-protocol-assessment.md +292 -0
- package/.claude/worktrees/agent-ad420869/docs/plans/2026-02-15-port-fallback-warning-design.md +611 -0
- package/.claude/worktrees/agent-ad420869/docs/plans/2026-02-16-a2a-callbook-macos-app.md +1660 -0
- package/.claude/worktrees/agent-ad420869/docs/plans/2026-02-16-auto-updater.md +1284 -0
- package/.claude/worktrees/agent-ad420869/docs/plans/2026-02-16-bugfixes-22-24.md +246 -0
- package/.claude/worktrees/agent-ad420869/docs/plans/2026-02-16-e2e-test-prompt-sequence.md +3085 -0
- package/.claude/worktrees/agent-ad420869/docs/plans/2026-02-16-orphan-process-fix.md +962 -0
- package/.claude/worktrees/agent-ad420869/docs/plans/2026-02-17-claude-code-codex-skills.md +770 -0
- package/.claude/worktrees/agent-ad420869/docs/plans/2026-03-01-a2a-68-openclaw-integration-tests.md +676 -0
- package/.claude/worktrees/agent-ad420869/docs/plans/2026-03-01-a2a-77-invoke-security-tests.md +661 -0
- package/.claude/worktrees/agent-ad420869/docs/prompts/e2e-test-agent.md +368 -0
- package/.claude/worktrees/agent-ad420869/docs/protocol.md +440 -0
- package/.claude/worktrees/agent-ad420869/docs/signing-setup.md +49 -0
- package/.claude/worktrees/agent-ad420869/eslint.config.js +16 -0
- package/.claude/worktrees/agent-ad420869/knip.json +17 -0
- package/.claude/worktrees/agent-ad420869/native/macos/index.html +182 -0
- package/.claude/worktrees/agent-ad420869/native/macos/package.json +8 -0
- package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/Cargo.lock +5875 -0
- package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/Cargo.toml +24 -0
- package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/binaries/.gitkeep +0 -0
- package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/build.rs +3 -0
- package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/capabilities/default.json +26 -0
- package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/entitlements.plist +14 -0
- package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/icons/128x128.png +0 -0
- package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/icons/128x128@2x.png +0 -0
- package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/icons/32x32.png +0 -0
- package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/icons/icon.icns +0 -0
- package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/icons/tray-connected.png +0 -0
- package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/icons/tray-disconnected.png +0 -0
- package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/src/discovery.rs +221 -0
- package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/src/health.rs +67 -0
- package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/src/lib.rs +226 -0
- package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/src/main.rs +6 -0
- package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/src/notifications.rs +180 -0
- package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/src/server.rs +306 -0
- package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/tauri.conf.json +50 -0
- package/.claude/worktrees/agent-ad420869/package.json +54 -0
- package/.claude/worktrees/agent-ad420869/pkg.config.json +14 -0
- package/.claude/worktrees/agent-ad420869/scripts/build-standalone.sh +106 -0
- package/.claude/worktrees/agent-ad420869/scripts/cleanup.js +251 -0
- package/.claude/worktrees/agent-ad420869/scripts/install-openclaw.js +986 -0
- package/.claude/worktrees/agent-ad420869/scripts/install-skills.js +234 -0
- package/.claude/worktrees/agent-ad420869/scripts/postinstall.js +224 -0
- package/.claude/worktrees/agent-ad420869/scripts/preuninstall.js +68 -0
- package/.claude/worktrees/agent-ad420869/scripts/run-e2e.sh +44 -0
- package/.claude/worktrees/agent-ad420869/scripts/smoke-test-standalone.sh +101 -0
- package/.claude/worktrees/agent-ad420869/scripts/sync-version.sh +28 -0
- package/.claude/worktrees/agent-ad420869/scripts/verify-app-bundle.sh +34 -0
- package/.claude/worktrees/agent-ad420869/src/dashboard/public/app.js +2683 -0
- package/.claude/worktrees/agent-ad420869/src/dashboard/public/index.html +394 -0
- package/.claude/worktrees/agent-ad420869/src/dashboard/public/onboarding.css +269 -0
- package/.claude/worktrees/agent-ad420869/src/dashboard/public/onboarding.html +96 -0
- package/.claude/worktrees/agent-ad420869/src/dashboard/public/onboarding.js +277 -0
- package/.claude/worktrees/agent-ad420869/src/dashboard/public/style.css +1294 -0
- package/.claude/worktrees/agent-ad420869/src/index.js +76 -0
- package/.claude/worktrees/agent-ad420869/src/lib/agent-card.js +111 -0
- package/.claude/worktrees/agent-ad420869/src/lib/call-monitor.js +205 -0
- package/.claude/worktrees/agent-ad420869/src/lib/callbook.js +366 -0
- package/.claude/worktrees/agent-ad420869/src/lib/claude-subagent.js +696 -0
- package/.claude/worktrees/agent-ad420869/src/lib/client.js +683 -0
- package/.claude/worktrees/agent-ad420869/src/lib/config.js +480 -0
- package/.claude/worktrees/agent-ad420869/src/lib/conversation-driver.js +608 -0
- package/.claude/worktrees/agent-ad420869/src/lib/conversations.js +830 -0
- package/.claude/worktrees/agent-ad420869/src/lib/crypto.js +113 -0
- package/.claude/worktrees/agent-ad420869/src/lib/dashboard-events.js +213 -0
- package/.claude/worktrees/agent-ad420869/src/lib/disclosure.js +792 -0
- package/.claude/worktrees/agent-ad420869/src/lib/external-ip.js +211 -0
- package/.claude/worktrees/agent-ad420869/src/lib/invite-host.js +223 -0
- package/.claude/worktrees/agent-ad420869/src/lib/local-request.js +69 -0
- package/.claude/worktrees/agent-ad420869/src/lib/logger.js +677 -0
- package/.claude/worktrees/agent-ad420869/src/lib/openclaw-integration.js +339 -0
- package/.claude/worktrees/agent-ad420869/src/lib/pid-file.js +103 -0
- package/.claude/worktrees/agent-ad420869/src/lib/port-scanner.js +83 -0
- package/.claude/worktrees/agent-ad420869/src/lib/prompt-template.js +355 -0
- package/.claude/worktrees/agent-ad420869/src/lib/runtime-adapter.js +701 -0
- package/.claude/worktrees/agent-ad420869/src/lib/summarizer.js +156 -0
- package/.claude/worktrees/agent-ad420869/src/lib/summary-formatter.js +168 -0
- package/.claude/worktrees/agent-ad420869/src/lib/summary-prompt.js +203 -0
- package/.claude/worktrees/agent-ad420869/src/lib/tokens.js +868 -0
- package/.claude/worktrees/agent-ad420869/src/lib/turn-timeout.js +52 -0
- package/.claude/worktrees/agent-ad420869/src/lib/update-checker.js +93 -0
- package/.claude/worktrees/agent-ad420869/src/lib/update-manager.js +313 -0
- package/.claude/worktrees/agent-ad420869/src/routes/a2a.js +1213 -0
- package/.claude/worktrees/agent-ad420869/src/routes/callbook.js +142 -0
- package/.claude/worktrees/agent-ad420869/src/routes/dashboard.js +1688 -0
- package/.claude/worktrees/agent-ad420869/src/server.js +1179 -0
- package/.claude/worktrees/agent-af1f3b59/.c8rc.json +16 -0
- package/.claude/worktrees/agent-af1f3b59/.claude/commands/a2a-app.md +42 -0
- package/.claude/worktrees/agent-af1f3b59/.claude/commands/a2a-call.md +26 -0
- package/.claude/worktrees/agent-af1f3b59/.claude/commands/a2a-contacts.md +31 -0
- package/.claude/worktrees/agent-af1f3b59/.claude/commands/a2a-conversations.md +47 -0
- package/.claude/worktrees/agent-af1f3b59/.claude/commands/a2a-gui.md +30 -0
- package/.claude/worktrees/agent-af1f3b59/.claude/commands/a2a-invite.md +63 -0
- package/.claude/worktrees/agent-af1f3b59/.claude/commands/a2a-setup.md +30 -0
- package/.claude/worktrees/agent-af1f3b59/.claude/commands/a2a-skills.md +27 -0
- package/.claude/worktrees/agent-af1f3b59/.claude/commands/a2a-status.md +46 -0
- package/.claude/worktrees/agent-af1f3b59/.claude/commands/a2a-uninstall.md +36 -0
- package/.claude/worktrees/agent-af1f3b59/.claude/commands/a2a-update.md +41 -0
- package/.claude/worktrees/agent-af1f3b59/.node-version +1 -0
- package/.claude/worktrees/agent-af1f3b59/ARCHITECTURE.md +135 -0
- package/.claude/worktrees/agent-af1f3b59/CLAUDE-INSTALL.md +156 -0
- package/.claude/worktrees/agent-af1f3b59/CONVENTIONS.md +178 -0
- package/.claude/worktrees/agent-af1f3b59/README.md +470 -0
- package/.claude/worktrees/agent-af1f3b59/SKILL.md +462 -0
- package/.claude/worktrees/agent-af1f3b59/bin/cli.js +3184 -0
- package/.claude/worktrees/agent-af1f3b59/biome.json +27 -0
- package/.claude/worktrees/agent-af1f3b59/docs/assessments/2026-02-27-google-a2a-protocol-assessment.md +292 -0
- package/.claude/worktrees/agent-af1f3b59/docs/plans/2026-02-15-port-fallback-warning-design.md +611 -0
- package/.claude/worktrees/agent-af1f3b59/docs/plans/2026-02-16-a2a-callbook-macos-app.md +1660 -0
- package/.claude/worktrees/agent-af1f3b59/docs/plans/2026-02-16-auto-updater.md +1284 -0
- package/.claude/worktrees/agent-af1f3b59/docs/plans/2026-02-16-bugfixes-22-24.md +246 -0
- package/.claude/worktrees/agent-af1f3b59/docs/plans/2026-02-16-e2e-test-prompt-sequence.md +3085 -0
- package/.claude/worktrees/agent-af1f3b59/docs/plans/2026-02-16-orphan-process-fix.md +962 -0
- package/.claude/worktrees/agent-af1f3b59/docs/plans/2026-02-17-claude-code-codex-skills.md +770 -0
- package/.claude/worktrees/agent-af1f3b59/docs/plans/2026-03-01-a2a-68-openclaw-integration-tests.md +676 -0
- package/.claude/worktrees/agent-af1f3b59/docs/plans/2026-03-01-a2a-77-invoke-security-tests.md +661 -0
- package/.claude/worktrees/agent-af1f3b59/docs/prompts/e2e-test-agent.md +368 -0
- package/.claude/worktrees/agent-af1f3b59/docs/protocol.md +440 -0
- package/.claude/worktrees/agent-af1f3b59/docs/signing-setup.md +49 -0
- package/.claude/worktrees/agent-af1f3b59/eslint.config.js +16 -0
- package/.claude/worktrees/agent-af1f3b59/knip.json +17 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/index.html +179 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/package.json +8 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/Cargo.lock +5875 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/Cargo.toml +24 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/binaries/.gitkeep +0 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/build.rs +3 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/capabilities/default.json +26 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/dmg-background.png +0 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/dmg-background@2x.png +0 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/entitlements.plist +14 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/icons/128x128.png +0 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/icons/128x128@2x.png +0 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/icons/256x256.png +0 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/icons/32x32.png +0 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/icons/512x512.png +0 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/icons/512x512@2x.png +0 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/icons/icon.icns +0 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/icons/tray-connected.png +0 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/icons/tray-disconnected.png +0 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/src/discovery.rs +184 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/src/health.rs +67 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/src/lib.rs +226 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/src/main.rs +6 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/src/notifications.rs +180 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/src/server.rs +306 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/tauri.conf.json +60 -0
- package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/volume-icon.icns +0 -0
- package/.claude/worktrees/agent-af1f3b59/package.json +54 -0
- package/.claude/worktrees/agent-af1f3b59/pkg.config.json +14 -0
- package/.claude/worktrees/agent-af1f3b59/scripts/build-standalone.sh +106 -0
- package/.claude/worktrees/agent-af1f3b59/scripts/cleanup.js +251 -0
- package/.claude/worktrees/agent-af1f3b59/scripts/install-openclaw.js +986 -0
- package/.claude/worktrees/agent-af1f3b59/scripts/install-skills.js +234 -0
- package/.claude/worktrees/agent-af1f3b59/scripts/postinstall.js +224 -0
- package/.claude/worktrees/agent-af1f3b59/scripts/preuninstall.js +68 -0
- package/.claude/worktrees/agent-af1f3b59/scripts/run-e2e.sh +44 -0
- package/.claude/worktrees/agent-af1f3b59/scripts/smoke-test-standalone.sh +101 -0
- package/.claude/worktrees/agent-af1f3b59/scripts/sync-version.sh +28 -0
- package/.claude/worktrees/agent-af1f3b59/scripts/verify-app-bundle.sh +34 -0
- package/.claude/worktrees/agent-af1f3b59/src/dashboard/public/app.js +2649 -0
- package/.claude/worktrees/agent-af1f3b59/src/dashboard/public/index.html +386 -0
- package/.claude/worktrees/agent-af1f3b59/src/dashboard/public/style.css +1274 -0
- package/.claude/worktrees/agent-af1f3b59/src/index.js +76 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/agent-card.js +111 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/call-monitor.js +205 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/callbook.js +366 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/claude-subagent.js +696 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/client.js +683 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/config.js +480 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/conversation-driver.js +608 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/conversations.js +830 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/crypto.js +113 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/dashboard-events.js +213 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/disclosure.js +792 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/external-ip.js +211 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/invite-host.js +223 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/local-request.js +69 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/logger.js +677 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/openclaw-integration.js +339 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/pid-file.js +103 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/port-scanner.js +83 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/prompt-template.js +355 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/runtime-adapter.js +701 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/summarizer.js +156 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/summary-formatter.js +168 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/summary-prompt.js +203 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/tokens.js +868 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/turn-timeout.js +52 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/update-checker.js +93 -0
- package/.claude/worktrees/agent-af1f3b59/src/lib/update-manager.js +313 -0
- package/.claude/worktrees/agent-af1f3b59/src/routes/a2a.js +1213 -0
- package/.claude/worktrees/agent-af1f3b59/src/routes/callbook.js +142 -0
- package/.claude/worktrees/agent-af1f3b59/src/routes/dashboard.js +1578 -0
- package/.claude/worktrees/agent-af1f3b59/src/server.js +1179 -0
- package/CONVENTIONS.md +39 -0
- package/docs/app.js +30 -0
- package/docs/assets/icon-32.png +0 -0
- package/docs/assets/icon-64.png +0 -0
- package/docs/index.html +117 -0
- package/docs/plans/2026-03-06-a2a-100-tauri-auto-updater.md +519 -0
- package/docs/plans/2026-03-06-a2a-101-dmg-installer-polish.md +229 -0
- package/docs/plans/2026-03-06-a2a-102-landing-page.md +611 -0
- package/docs/plans/2026-03-06-a2a-103-standalone-e2e-tests.md +810 -0
- package/docs/plans/2026-03-06-a2a-99-native-onboarding-wizard.md +1261 -0
- package/docs/style.css +209 -0
- package/native/macos/index.html +7 -4
- package/native/macos/src-tauri/Cargo.toml +1 -0
- package/native/macos/src-tauri/dmg-background.png +0 -0
- package/native/macos/src-tauri/dmg-background@2x.png +0 -0
- package/native/macos/src-tauri/icons/256x256.png +0 -0
- package/native/macos/src-tauri/icons/512x512.png +0 -0
- package/native/macos/src-tauri/icons/512x512@2x.png +0 -0
- package/native/macos/src-tauri/src/discovery.rs +37 -0
- package/native/macos/src-tauri/src/lib.rs +32 -2
- package/native/macos/src-tauri/src/updater.rs +124 -0
- package/native/macos/src-tauri/tauri.conf.json +21 -3
- package/native/macos/src-tauri/volume-icon.icns +0 -0
- package/package.json +2 -1
- package/scripts/generate-update-manifest.sh +42 -0
- package/scripts/run-e2e.sh +8 -0
- package/src/dashboard/public/app.js +34 -0
- package/src/dashboard/public/index.html +7 -0
- package/src/dashboard/public/onboarding.css +269 -0
- package/src/dashboard/public/onboarding.html +96 -0
- package/src/dashboard/public/onboarding.js +277 -0
- package/src/dashboard/public/style.css +20 -0
- package/src/routes/dashboard.js +110 -0
|
@@ -0,0 +1,3184 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* A2A Calling CLI
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* a2a create [options] Create an A2A token
|
|
7
|
+
* a2a list List active tokens
|
|
8
|
+
* a2a revoke <id> Revoke a token
|
|
9
|
+
* a2a add <url> [name] Add a contact (alias of "contacts add")
|
|
10
|
+
* a2a remotes List contacts (alias of "contacts")
|
|
11
|
+
* a2a call <url> <msg> Call a contact (or invite URL)
|
|
12
|
+
* a2a ping <url> Ping an invite URL
|
|
13
|
+
* a2a gui Open the local dashboard GUI in a browser
|
|
14
|
+
* a2a app <action> Manage native macOS app (status/install/uninstall)
|
|
15
|
+
* a2a setup Auto setup (gateway-aware dashboard install)
|
|
16
|
+
* a2a uninstall Stop server and remove local A2A config
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const os = require('os');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
const crypto = require('crypto');
|
|
23
|
+
const { spawn } = require('child_process');
|
|
24
|
+
const { TokenStore } = require('../src/lib/tokens');
|
|
25
|
+
const { A2AClient } = require('../src/lib/client');
|
|
26
|
+
|
|
27
|
+
const CONFIG_DIR = process.env.A2A_CONFIG_DIR || process.env.OPENCLAW_CONFIG_DIR || path.join(os.homedir(), '.config', 'openclaw');
|
|
28
|
+
const CONFIG_PATH = path.join(CONFIG_DIR, 'a2a-config.json');
|
|
29
|
+
const ONBOARDING_EXEMPT = new Set([
|
|
30
|
+
'quickstart',
|
|
31
|
+
'help',
|
|
32
|
+
'version',
|
|
33
|
+
'status',
|
|
34
|
+
'update',
|
|
35
|
+
'uninstall',
|
|
36
|
+
'onboard',
|
|
37
|
+
'gui',
|
|
38
|
+
'dashboard',
|
|
39
|
+
'server',
|
|
40
|
+
'setup',
|
|
41
|
+
'app',
|
|
42
|
+
'install',
|
|
43
|
+
'skills'
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
function isOnboarded() {
|
|
47
|
+
try {
|
|
48
|
+
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
|
|
49
|
+
return config.onboarding?.version === 2 && config.onboarding?.step === 'complete';
|
|
50
|
+
} catch (err) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Lazy load conversation store (requires better-sqlite3)
|
|
56
|
+
let convStore = null;
|
|
57
|
+
function getConvStore() {
|
|
58
|
+
if (convStore === false) return null; // Already tried and failed
|
|
59
|
+
if (!convStore) {
|
|
60
|
+
try {
|
|
61
|
+
const { ConversationStore } = require('../src/lib/conversations');
|
|
62
|
+
convStore = new ConversationStore();
|
|
63
|
+
if (!convStore.isAvailable()) {
|
|
64
|
+
console.error(`[a2a] ${convStore.getError()}`);
|
|
65
|
+
convStore = false;
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
} catch (err) {
|
|
69
|
+
convStore = false;
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return convStore;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const store = new TokenStore();
|
|
77
|
+
|
|
78
|
+
// Commands that should hard-fail with a clear error when not onboarded,
|
|
79
|
+
// rather than falling through to the interactive quickstart flow.
|
|
80
|
+
// These are outbound operations often invoked by agents/automation.
|
|
81
|
+
const ONBOARDING_HARD_FAIL = new Set([
|
|
82
|
+
'call', 'ping'
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
// ── enforceOnboarding ────────────────────────────────────────────────────
|
|
86
|
+
// If onboarding is incomplete or the config is missing/invalid, run the
|
|
87
|
+
// full quickstart flow inline — verbose, with direct stdio. The agent sees
|
|
88
|
+
// the banner, port selection, server start, and disclosure prompt right here.
|
|
89
|
+
//
|
|
90
|
+
// This is the primary onboarding entry point for agents. npm postinstall
|
|
91
|
+
// silently starts the server (npm captures its output), so the first time
|
|
92
|
+
// the agent runs ANY `a2a` command, this function fires and gives the agent
|
|
93
|
+
// the full verbose walkthrough it needs to complete setup.
|
|
94
|
+
//
|
|
95
|
+
// Returns a Promise if quickstart needs to run (caller must await), or
|
|
96
|
+
// undefined if onboarding is already complete.
|
|
97
|
+
function enforceOnboarding(command) {
|
|
98
|
+
if (ONBOARDING_EXEMPT.has(command)) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!isOnboarded()) {
|
|
103
|
+
// For outbound commands (call, ping, status), fail immediately with
|
|
104
|
+
// a clear error instead of dumping onboarding prompts. This prevents
|
|
105
|
+
// calling agents from receiving walls of setup instructions.
|
|
106
|
+
if (ONBOARDING_HARD_FAIL.has(command)) {
|
|
107
|
+
console.error('❌ Onboarding not complete. Run `a2a quickstart` first to set up your agent.');
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Run the full quickstart flow inline — verbose output, direct stdio.
|
|
112
|
+
// This replaces the original command; after onboarding the agent can
|
|
113
|
+
// re-run their intended command.
|
|
114
|
+
return commands.quickstart({ flags: {}, positional: [] });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Format relative time
|
|
119
|
+
function formatTimeAgo(date) {
|
|
120
|
+
const seconds = Math.floor((new Date() - date) / 1000);
|
|
121
|
+
if (seconds < 60) return 'just now';
|
|
122
|
+
const minutes = Math.floor(seconds / 60);
|
|
123
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
124
|
+
const hours = Math.floor(minutes / 60);
|
|
125
|
+
if (hours < 24) return `${hours}h ago`;
|
|
126
|
+
const days = Math.floor(hours / 24);
|
|
127
|
+
if (days < 7) return `${days}d ago`;
|
|
128
|
+
return date.toLocaleDateString();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function openInBrowser(url) {
|
|
132
|
+
const platform = process.platform;
|
|
133
|
+
let cmd = null;
|
|
134
|
+
let args = [];
|
|
135
|
+
|
|
136
|
+
if (platform === 'darwin') {
|
|
137
|
+
cmd = 'open';
|
|
138
|
+
args = [url];
|
|
139
|
+
} else if (platform === 'win32') {
|
|
140
|
+
cmd = 'cmd';
|
|
141
|
+
args = ['/c', 'start', '', url];
|
|
142
|
+
} else {
|
|
143
|
+
cmd = 'xdg-open';
|
|
144
|
+
args = [url];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
const child = spawn(cmd, args, { stdio: 'ignore', detached: true });
|
|
149
|
+
child.unref();
|
|
150
|
+
return { attempted: true, command: [cmd, ...args].join(' ') };
|
|
151
|
+
} catch (err) {
|
|
152
|
+
return { attempted: false, error: err.message };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function findNativeApp() {
|
|
157
|
+
if (os.platform() !== 'darwin') return null;
|
|
158
|
+
|
|
159
|
+
const candidates = [
|
|
160
|
+
path.join(os.homedir(), 'Applications', 'A2A Callbook.app'),
|
|
161
|
+
'/Applications/A2A Callbook.app',
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
for (const appPath of candidates) {
|
|
165
|
+
try {
|
|
166
|
+
if (fs.existsSync(appPath)) return appPath;
|
|
167
|
+
} catch (_) {}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function getNativeAppPaths() {
|
|
174
|
+
return {
|
|
175
|
+
appDir: path.join(os.homedir(), 'Applications'),
|
|
176
|
+
appPath: path.join(os.homedir(), 'Applications', 'A2A Callbook.app')
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function parseInstalledNativeAppVersion(appPath) {
|
|
181
|
+
if (!appPath) return null;
|
|
182
|
+
const plistPath = path.join(appPath, 'Contents', 'Info.plist');
|
|
183
|
+
if (!fs.existsSync(plistPath)) return null;
|
|
184
|
+
try {
|
|
185
|
+
const plist = fs.readFileSync(plistPath, 'utf8');
|
|
186
|
+
const m = plist.match(/<key>CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/);
|
|
187
|
+
return m && m[1] ? m[1].trim() : null;
|
|
188
|
+
} catch (_) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function installNativeMacApp(options = {}) {
|
|
194
|
+
if (os.platform() !== 'darwin') {
|
|
195
|
+
return { success: false, skipped: 'not_macos' };
|
|
196
|
+
}
|
|
197
|
+
const quiet = Boolean(options.quiet);
|
|
198
|
+
const force = Boolean(options.force);
|
|
199
|
+
const version = require('../package.json').version;
|
|
200
|
+
const { appDir, appPath } = getNativeAppPaths();
|
|
201
|
+
const installedVersion = parseInstalledNativeAppVersion(appPath);
|
|
202
|
+
if (!force && installedVersion === version) {
|
|
203
|
+
return { success: true, installed: false, version, appPath, reason: 'already_current' };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const tarUrl = `https://github.com/onthegonow/a2a_calling/releases/download/v${version}/A2A-Callbook-${version}.app.tar.gz`;
|
|
207
|
+
const tmpFile = path.join(os.tmpdir(), `a2a-callbook-${version}.app.tar.gz`);
|
|
208
|
+
const { execFileSync } = require('child_process');
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
fs.mkdirSync(appDir, { recursive: true });
|
|
212
|
+
execFileSync('curl', ['-fL', '-o', tmpFile, tarUrl], { timeout: 120000, stdio: quiet ? 'ignore' : 'inherit' });
|
|
213
|
+
if (!fs.existsSync(tmpFile) || fs.statSync(tmpFile).size < 1000) {
|
|
214
|
+
return { success: false, error: 'download_failed' };
|
|
215
|
+
}
|
|
216
|
+
if (fs.existsSync(appPath)) {
|
|
217
|
+
fs.rmSync(appPath, { recursive: true, force: true });
|
|
218
|
+
}
|
|
219
|
+
execFileSync('tar', ['-xzf', tmpFile, '-C', appDir], { timeout: 60000, stdio: quiet ? 'ignore' : 'inherit' });
|
|
220
|
+
try { fs.unlinkSync(tmpFile); } catch (_) {}
|
|
221
|
+
return { success: true, installed: true, version, appPath };
|
|
222
|
+
} catch (err) {
|
|
223
|
+
try { fs.unlinkSync(tmpFile); } catch (_) {}
|
|
224
|
+
return { success: false, error: err.message || 'install_failed' };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function uninstallNativeMacApp() {
|
|
229
|
+
if (os.platform() !== 'darwin') {
|
|
230
|
+
return { success: false, skipped: 'not_macos' };
|
|
231
|
+
}
|
|
232
|
+
const candidates = [
|
|
233
|
+
path.join(os.homedir(), 'Applications', 'A2A Callbook.app'),
|
|
234
|
+
'/Applications/A2A Callbook.app'
|
|
235
|
+
];
|
|
236
|
+
const existing = candidates.filter((candidate) => {
|
|
237
|
+
try {
|
|
238
|
+
return fs.existsSync(candidate);
|
|
239
|
+
} catch (_) {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
if (existing.length === 0) {
|
|
244
|
+
return { success: true, removed: false, appPath: candidates[0] };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const removed = [];
|
|
248
|
+
const failed = [];
|
|
249
|
+
for (const appPath of existing) {
|
|
250
|
+
try {
|
|
251
|
+
fs.rmSync(appPath, { recursive: true, force: true });
|
|
252
|
+
removed.push(appPath);
|
|
253
|
+
} catch (err) {
|
|
254
|
+
failed.push({ appPath, error: err && err.message ? err.message : 'uninstall_failed' });
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (failed.length > 0) {
|
|
258
|
+
return {
|
|
259
|
+
success: false,
|
|
260
|
+
error: failed.map((f) => `${f.appPath}: ${f.error}`).join('; '),
|
|
261
|
+
appPath: removed[0] || existing[0]
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
return { success: true, removed: true, appPath: removed[0] || existing[0] };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async function findLocalServerPort(preferredPorts = []) {
|
|
268
|
+
const http = require('http');
|
|
269
|
+
|
|
270
|
+
const candidates = [];
|
|
271
|
+
const seen = new Set();
|
|
272
|
+
for (const port of preferredPorts) {
|
|
273
|
+
const n = Number.parseInt(String(port), 10);
|
|
274
|
+
if (!Number.isFinite(n) || n <= 0 || n > 65535) continue;
|
|
275
|
+
if (seen.has(n)) continue;
|
|
276
|
+
seen.add(n);
|
|
277
|
+
candidates.push(n);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const defaultPorts = [3001, 80, 8080, 8443, 9001];
|
|
281
|
+
for (const port of defaultPorts) {
|
|
282
|
+
if (seen.has(port)) continue;
|
|
283
|
+
seen.add(port);
|
|
284
|
+
candidates.push(port);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const probe = (port) => new Promise(resolve => {
|
|
288
|
+
const req = http.request({
|
|
289
|
+
hostname: '127.0.0.1',
|
|
290
|
+
port,
|
|
291
|
+
path: '/api/a2a/ping',
|
|
292
|
+
method: 'GET',
|
|
293
|
+
timeout: 800
|
|
294
|
+
}, (res) => {
|
|
295
|
+
res.resume();
|
|
296
|
+
resolve(res.statusCode === 200);
|
|
297
|
+
});
|
|
298
|
+
req.on('error', () => resolve(false));
|
|
299
|
+
req.on('timeout', () => { req.destroy(); resolve(false); });
|
|
300
|
+
req.end();
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
for (const port of candidates) {
|
|
304
|
+
// eslint-disable-next-line no-await-in-loop
|
|
305
|
+
const ok = await probe(port);
|
|
306
|
+
if (ok) return port;
|
|
307
|
+
}
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Parse arguments
|
|
312
|
+
function parseArgs(argv) {
|
|
313
|
+
const args = { _: [], flags: {} };
|
|
314
|
+
let i = 2;
|
|
315
|
+
while (i < argv.length) {
|
|
316
|
+
if (argv[i].startsWith('--')) {
|
|
317
|
+
const key = argv[i].slice(2);
|
|
318
|
+
const val = argv[i + 1] && !argv[i + 1].startsWith('-') ? argv[++i] : true;
|
|
319
|
+
args.flags[key] = val;
|
|
320
|
+
} else if (argv[i].startsWith('-') && argv[i].length === 2) {
|
|
321
|
+
const key = argv[i].slice(1);
|
|
322
|
+
const val = argv[i + 1] && !argv[i + 1].startsWith('-') ? argv[++i] : true;
|
|
323
|
+
args.flags[key] = val;
|
|
324
|
+
} else {
|
|
325
|
+
args._.push(argv[i]);
|
|
326
|
+
}
|
|
327
|
+
i++;
|
|
328
|
+
}
|
|
329
|
+
return args;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
async function promptYesNo(question) {
|
|
333
|
+
const q = String(question || '');
|
|
334
|
+
// Support both bracket and paren styles: [Y/n], (y/N), etc.
|
|
335
|
+
// Convention: uppercase letter is the default when user presses Enter.
|
|
336
|
+
const defaultValue = q.includes('y/N')
|
|
337
|
+
? false
|
|
338
|
+
: q.includes('Y/n')
|
|
339
|
+
? true
|
|
340
|
+
: true;
|
|
341
|
+
|
|
342
|
+
if (!isInteractiveShell()) {
|
|
343
|
+
return defaultValue;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return await new Promise(resolve => {
|
|
347
|
+
const rl = require('readline').createInterface({ input: process.stdin, output: process.stdout });
|
|
348
|
+
rl.question(question, (answer) => {
|
|
349
|
+
rl.close();
|
|
350
|
+
const normalized = String(answer || '').trim().toLowerCase();
|
|
351
|
+
if (!normalized) return resolve(defaultValue);
|
|
352
|
+
resolve(normalized === 'y' || normalized === 'yes');
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function isInteractiveShell() {
|
|
358
|
+
return Boolean(process.stdin && process.stdout && process.stdin.isTTY && process.stdout.isTTY);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
async function promptText(question, defaultValue = '') {
|
|
362
|
+
if (!isInteractiveShell()) {
|
|
363
|
+
return defaultValue;
|
|
364
|
+
}
|
|
365
|
+
return await new Promise(resolve => {
|
|
366
|
+
const rl = require('readline').createInterface({ input: process.stdin, output: process.stdout });
|
|
367
|
+
rl.question(question, (answer) => {
|
|
368
|
+
rl.close();
|
|
369
|
+
const cleaned = String(answer || '').trim();
|
|
370
|
+
resolve(cleaned || defaultValue);
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function parsePort(raw, fallback = null) {
|
|
376
|
+
const parsed = Number.parseInt(String(raw || '').trim(), 10);
|
|
377
|
+
if (Number.isFinite(parsed) && parsed > 0 && parsed <= 65535) {
|
|
378
|
+
return parsed;
|
|
379
|
+
}
|
|
380
|
+
return fallback;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function printStepHeader(label) {
|
|
384
|
+
const clean = String(label || '').trim();
|
|
385
|
+
const innerWidth = Math.max(62, clean.length + 12);
|
|
386
|
+
const padding = Math.max(0, innerWidth - clean.length);
|
|
387
|
+
const left = Math.floor(padding / 2);
|
|
388
|
+
const right = Math.max(0, padding - left);
|
|
389
|
+
console.log('\n' + '╔' + '═'.repeat(innerWidth) + '╗');
|
|
390
|
+
console.log(`║${' '.repeat(left)}${clean}${' '.repeat(right)}║`);
|
|
391
|
+
console.log('╚' + '═'.repeat(innerWidth) + '╝');
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function printSection(title) {
|
|
395
|
+
console.log('\n━━━ ' + title + ' ━━━');
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
async function inspectPorts(preferredPort = null) {
|
|
399
|
+
const candidates = [];
|
|
400
|
+
if (preferredPort) {
|
|
401
|
+
candidates.push(preferredPort);
|
|
402
|
+
}
|
|
403
|
+
candidates.push(80);
|
|
404
|
+
for (let p = 3001; p < 3021; p += 1) {
|
|
405
|
+
if (!candidates.includes(p)) candidates.push(p);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const { tryBindPort } = require('../src/lib/port-scanner');
|
|
409
|
+
const results = [];
|
|
410
|
+
for (const port of candidates) {
|
|
411
|
+
const r = await tryBindPort(port);
|
|
412
|
+
results.push({
|
|
413
|
+
port,
|
|
414
|
+
available: Boolean(r.ok),
|
|
415
|
+
blocked: !r.ok && r.code === 'EACCES',
|
|
416
|
+
code: r.code || null
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
return results;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function summarizePortResults(portResults) {
|
|
423
|
+
return portResults.map(item => {
|
|
424
|
+
if (item.available) return `Port ${item.port}: available ✓`;
|
|
425
|
+
if (item.blocked) return `Port ${item.port}: requires elevated privileges`;
|
|
426
|
+
return `Port ${item.port}: in use`;
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Identify what process is using a given port.
|
|
432
|
+
* Returns { pid, name } or null if detection fails.
|
|
433
|
+
*/
|
|
434
|
+
function identifyPortProcess(port) {
|
|
435
|
+
const p = Number(port);
|
|
436
|
+
if (!Number.isFinite(p) || p <= 0) return null;
|
|
437
|
+
const { execSync } = require('child_process');
|
|
438
|
+
// Try lsof first (most common on Linux/macOS)
|
|
439
|
+
try {
|
|
440
|
+
const out = execSync(`lsof -i :${p} -sTCP:LISTEN -t 2>/dev/null`, { encoding: 'utf8', timeout: 5000 }).trim();
|
|
441
|
+
if (out) {
|
|
442
|
+
const pid = out.split('\n')[0].trim();
|
|
443
|
+
let name = 'unknown';
|
|
444
|
+
try {
|
|
445
|
+
name = execSync(`ps -p ${pid} -o comm= 2>/dev/null`, { encoding: 'utf8', timeout: 3000 }).trim();
|
|
446
|
+
} catch (e) { /* best-effort */ }
|
|
447
|
+
return { pid: Number(pid), name };
|
|
448
|
+
}
|
|
449
|
+
} catch (e) { /* lsof not available or failed */ }
|
|
450
|
+
|
|
451
|
+
// Fallback: ss (Linux)
|
|
452
|
+
try {
|
|
453
|
+
const out = execSync(`ss -tlnp 'sport = :${p}' 2>/dev/null`, { encoding: 'utf8', timeout: 5000 });
|
|
454
|
+
const pidMatch = out.match(/pid=(\d+)/);
|
|
455
|
+
const nameMatch = out.match(/\("([^"]+)"/);
|
|
456
|
+
if (pidMatch) {
|
|
457
|
+
return { pid: Number(pidMatch[1]), name: nameMatch ? nameMatch[1] : 'unknown' };
|
|
458
|
+
}
|
|
459
|
+
} catch (e) { /* ss not available */ }
|
|
460
|
+
|
|
461
|
+
return null;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* When port 80 is unavailable, prompt the user with fallback options.
|
|
466
|
+
* Returns { strategy: 'kill' | 'proxy' | 'continue', port: number }
|
|
467
|
+
*
|
|
468
|
+
* Non-interactive: auto-returns 'continue' with a printed warning.
|
|
469
|
+
*/
|
|
470
|
+
async function promptPortFallbackStrategy(fallbackPort, interactive) {
|
|
471
|
+
const processInfo = identifyPortProcess(80);
|
|
472
|
+
|
|
473
|
+
console.log('\n ┌─────────────────────────────────────────────────────────────────┐');
|
|
474
|
+
console.log(' │ ⚠ PORT 80 IS UNAVAILABLE │');
|
|
475
|
+
console.log(' └─────────────────────────────────────────────────────────────────┘');
|
|
476
|
+
console.log('');
|
|
477
|
+
if (processInfo) {
|
|
478
|
+
console.log(` Port 80 is held by: ${processInfo.name} (PID ${processInfo.pid})`);
|
|
479
|
+
} else {
|
|
480
|
+
console.log(' Port 80 is in use by another process (could not identify).');
|
|
481
|
+
}
|
|
482
|
+
console.log('');
|
|
483
|
+
console.log(' Why this matters:');
|
|
484
|
+
console.log(' - Port 80 is the default HTTP port — no firewall config needed');
|
|
485
|
+
console.log(` - Fallback port ${fallbackPort} may be blocked by the caller's firewall`);
|
|
486
|
+
console.log(` - If the server restarts on a different port, all invite URLs break`);
|
|
487
|
+
console.log(` - Invite URLs with non-standard ports look like: a2a://host:${fallbackPort}/token`);
|
|
488
|
+
console.log('');
|
|
489
|
+
|
|
490
|
+
if (!interactive) {
|
|
491
|
+
console.log(` Non-interactive mode: continuing on port ${fallbackPort}.`);
|
|
492
|
+
console.log(' Set up a reverse proxy (port 80 → ' + fallbackPort + ') for production use.\n');
|
|
493
|
+
return { strategy: 'continue', port: fallbackPort };
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
console.log(' Options:');
|
|
497
|
+
if (processInfo) {
|
|
498
|
+
console.log(` 1) Kill ${processInfo.name} (PID ${processInfo.pid}) and use port 80`);
|
|
499
|
+
} else {
|
|
500
|
+
console.log(' 1) Kill the process on port 80 and retry');
|
|
501
|
+
}
|
|
502
|
+
console.log(` 2) Set up a reverse proxy (port 80 → ${fallbackPort})`);
|
|
503
|
+
console.log(` 3) Continue on port ${fallbackPort} (not recommended for production)`);
|
|
504
|
+
console.log('');
|
|
505
|
+
|
|
506
|
+
const choice = await promptText(' Choose [1/2/3]: ', '2');
|
|
507
|
+
const normalized = String(choice).trim();
|
|
508
|
+
|
|
509
|
+
if (normalized === '1') {
|
|
510
|
+
return { strategy: 'kill', port: 80, processInfo };
|
|
511
|
+
} else if (normalized === '3') {
|
|
512
|
+
console.log(`\n ⚠ Continuing on port ${fallbackPort}.`);
|
|
513
|
+
console.log(` Invite URLs will include :${fallbackPort} and may not be reachable externally.`);
|
|
514
|
+
console.log(' You can set up a reverse proxy later with: a2a config --help\n');
|
|
515
|
+
return { strategy: 'continue', port: fallbackPort };
|
|
516
|
+
} else {
|
|
517
|
+
// Default: reverse proxy (option 2)
|
|
518
|
+
return { strategy: 'proxy', port: fallbackPort };
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Attempt to kill the process on a given port.
|
|
524
|
+
* Returns true if kill succeeded and port is now available.
|
|
525
|
+
*/
|
|
526
|
+
async function killPortProcess(processInfo) {
|
|
527
|
+
if (!processInfo || !Number.isFinite(processInfo.pid)) return false;
|
|
528
|
+
const { execSync } = require('child_process');
|
|
529
|
+
try {
|
|
530
|
+
console.log(` Killing ${processInfo.name} (PID ${processInfo.pid})...`);
|
|
531
|
+
execSync(`kill ${processInfo.pid}`, { timeout: 5000 });
|
|
532
|
+
// Wait briefly for the port to free up
|
|
533
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
534
|
+
const { tryBindPort } = require('../src/lib/port-scanner');
|
|
535
|
+
const result = await tryBindPort(80);
|
|
536
|
+
if (result.ok) {
|
|
537
|
+
console.log(' ✅ Port 80 is now available.');
|
|
538
|
+
return true;
|
|
539
|
+
}
|
|
540
|
+
console.log(' Port 80 is still in use after kill. The process may require sudo to stop.');
|
|
541
|
+
return false;
|
|
542
|
+
} catch (e) {
|
|
543
|
+
console.log(` Could not kill process: ${e.message}`);
|
|
544
|
+
console.log(' You may need to run: sudo kill ' + processInfo.pid);
|
|
545
|
+
return false;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Detect installed web servers and generate reverse proxy config.
|
|
551
|
+
* Returns { hasNginx, hasCaddy, nginxConfig, caddyConfig }.
|
|
552
|
+
*/
|
|
553
|
+
function generateProxyConfig(backendPort) {
|
|
554
|
+
const { spawnSync } = require('child_process');
|
|
555
|
+
const hasNginx = spawnSync('which', ['nginx'], { encoding: 'utf8' }).status === 0;
|
|
556
|
+
const hasCaddy = spawnSync('which', ['caddy'], { encoding: 'utf8' }).status === 0;
|
|
557
|
+
|
|
558
|
+
const nginxConfig = [
|
|
559
|
+
'# ══════════════════════════════════════════════════════════════',
|
|
560
|
+
'# A2A (Agent-to-Agent) Protocol Proxy',
|
|
561
|
+
'# ══════════════════════════════════════════════════════════════',
|
|
562
|
+
'# Routes federation requests from port 80 to the local',
|
|
563
|
+
`# A2A server on port ${backendPort}.`,
|
|
564
|
+
'#',
|
|
565
|
+
'# Protocol: https://github.com/onthegonow/a2a_calling',
|
|
566
|
+
'# All requests to /api/a2a/* are agent-to-agent API calls.',
|
|
567
|
+
'# ══════════════════════════════════════════════════════════════',
|
|
568
|
+
'location /api/a2a/ {',
|
|
569
|
+
` proxy_pass http://127.0.0.1:${backendPort}/api/a2a/;`,
|
|
570
|
+
' proxy_http_version 1.1;',
|
|
571
|
+
' proxy_set_header Host $host;',
|
|
572
|
+
' proxy_set_header X-Real-IP $remote_addr;',
|
|
573
|
+
' proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;',
|
|
574
|
+
' proxy_set_header X-Forwarded-Proto $scheme;',
|
|
575
|
+
'}'
|
|
576
|
+
].join('\n');
|
|
577
|
+
|
|
578
|
+
const caddyConfig = [
|
|
579
|
+
'# A2A (Agent-to-Agent) Protocol Proxy',
|
|
580
|
+
`# Routes federation requests to local A2A server on port ${backendPort}`,
|
|
581
|
+
'# Protocol: https://github.com/onthegonow/a2a_calling',
|
|
582
|
+
'handle /api/a2a/* {',
|
|
583
|
+
` reverse_proxy 127.0.0.1:${backendPort}`,
|
|
584
|
+
'}'
|
|
585
|
+
].join('\n');
|
|
586
|
+
|
|
587
|
+
return { hasNginx, hasCaddy, nginxConfig, caddyConfig };
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
function extractNameFromPersonality(notes) {
|
|
591
|
+
if (!notes || typeof notes !== 'string') return null;
|
|
592
|
+
const patterns = [
|
|
593
|
+
/(?:I'm|I am|My name is|Name:|Owner:)\s+([A-Z][a-z]+(?:\s+[A-Z][a-z]+)?)/,
|
|
594
|
+
/^([A-Z][a-z]+(?:\s+[A-Z][a-z]+)?)\s+(?:is|here|speaking)/
|
|
595
|
+
];
|
|
596
|
+
for (const p of patterns) {
|
|
597
|
+
const m = notes.match(p);
|
|
598
|
+
if (m && m[1]) return m[1].trim();
|
|
599
|
+
}
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
async function handleDisclosureSubmit(args, commandLabel = 'onboard') {
|
|
604
|
+
const submitRaw = args.flags.submit;
|
|
605
|
+
if (!submitRaw) return false;
|
|
606
|
+
|
|
607
|
+
const { A2AConfig } = require('../src/lib/config');
|
|
608
|
+
const {
|
|
609
|
+
validateDisclosureSubmission,
|
|
610
|
+
saveManifest,
|
|
611
|
+
MANIFEST_FILE
|
|
612
|
+
} = require('../src/lib/disclosure');
|
|
613
|
+
|
|
614
|
+
const config = new A2AConfig();
|
|
615
|
+
const submitCommand = commandLabel === 'quickstart'
|
|
616
|
+
? 'a2a quickstart --submit'
|
|
617
|
+
: 'a2a onboard --submit';
|
|
618
|
+
|
|
619
|
+
let parsed;
|
|
620
|
+
try {
|
|
621
|
+
parsed = JSON.parse(String(submitRaw));
|
|
622
|
+
} catch (e) {
|
|
623
|
+
console.error('\nInvalid JSON in --submit flag.');
|
|
624
|
+
console.error(` Parse error: ${e.message}\n`);
|
|
625
|
+
process.exit(1);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
const result = validateDisclosureSubmission(parsed);
|
|
629
|
+
if (!result.valid) {
|
|
630
|
+
console.error('\nDisclosure submission validation failed:\n');
|
|
631
|
+
result.errors.forEach(err => console.error(` - ${err}`));
|
|
632
|
+
console.error(`\nFix the errors above and resubmit with: ${submitCommand} '<json>'\n`);
|
|
633
|
+
process.exit(1);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
saveManifest(result.manifest);
|
|
637
|
+
console.log('\nStep 3 of 4: Disclosure manifest saved.');
|
|
638
|
+
console.log(` Manifest: ${MANIFEST_FILE}`);
|
|
639
|
+
|
|
640
|
+
// Sync tier config from manifest
|
|
641
|
+
const manifest = result.manifest;
|
|
642
|
+
|
|
643
|
+
// Helper to extract topic names
|
|
644
|
+
function getTierTopics(tierData) {
|
|
645
|
+
if (!tierData || !Array.isArray(tierData.topics)) return [];
|
|
646
|
+
return tierData.topics.map(t => String(t && t.topic || '').trim()).filter(Boolean);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// Helper to extract allowed tools per tier from the disclosure manifest.
|
|
650
|
+
function getTierTools(tierData) {
|
|
651
|
+
if (!tierData || !Array.isArray(tierData.allowed_tools)) return [];
|
|
652
|
+
const seen = new Set();
|
|
653
|
+
const out = [];
|
|
654
|
+
for (const tool of tierData.allowed_tools) {
|
|
655
|
+
const cleaned = String(tool || '').trim();
|
|
656
|
+
if (!cleaned) continue;
|
|
657
|
+
const key = cleaned.toLowerCase();
|
|
658
|
+
if (seen.has(key)) continue;
|
|
659
|
+
seen.add(key);
|
|
660
|
+
out.push(cleaned);
|
|
661
|
+
}
|
|
662
|
+
return out;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
const tiersData = manifest.tiers || {};
|
|
666
|
+
|
|
667
|
+
// Derive goals from disclosure objectives (used in tier config and token creation)
|
|
668
|
+
const disclosureObjectives = (tiersData.public?.objectives || [])
|
|
669
|
+
.map(o => typeof o === 'string' ? o : (o && o.objective || ''))
|
|
670
|
+
.map(s => s.trim().toLowerCase().replace(/\s+/g, '-').slice(0, 60))
|
|
671
|
+
.filter(Boolean);
|
|
672
|
+
|
|
673
|
+
const tokenGoals = disclosureObjectives.length > 0
|
|
674
|
+
? [...new Set(disclosureObjectives)].slice(0, 5)
|
|
675
|
+
: ['grow-network', 'find-collaborators', 'build-in-public'];
|
|
676
|
+
|
|
677
|
+
try {
|
|
678
|
+
const publicTools = getTierTools(tiersData.public);
|
|
679
|
+
const friendsTools = [...publicTools, ...getTierTools(tiersData.friends)];
|
|
680
|
+
const familyTools = [...friendsTools, ...getTierTools(tiersData.family)];
|
|
681
|
+
|
|
682
|
+
const publicTierPatch = {
|
|
683
|
+
topics: getTierTopics(tiersData.public),
|
|
684
|
+
goals: tokenGoals
|
|
685
|
+
};
|
|
686
|
+
if (publicTools.length > 0) publicTierPatch.allowed_tools = publicTools;
|
|
687
|
+
|
|
688
|
+
const friendsTierPatch = {
|
|
689
|
+
topics: [...getTierTopics(tiersData.public), ...getTierTopics(tiersData.friends)]
|
|
690
|
+
};
|
|
691
|
+
if (friendsTools.length > 0) friendsTierPatch.allowed_tools = friendsTools;
|
|
692
|
+
|
|
693
|
+
const familyTierPatch = {
|
|
694
|
+
topics: [...getTierTopics(tiersData.public), ...getTierTopics(tiersData.friends), ...getTierTopics(tiersData.family)]
|
|
695
|
+
};
|
|
696
|
+
if (familyTools.length > 0) familyTierPatch.allowed_tools = familyTools;
|
|
697
|
+
|
|
698
|
+
config.setTier('public', publicTierPatch);
|
|
699
|
+
config.setTier('friends', friendsTierPatch);
|
|
700
|
+
config.setTier('family', familyTierPatch);
|
|
701
|
+
} catch (err) {
|
|
702
|
+
console.error(` Warning: could not sync tier config: ${err.message}`);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// If already onboarded, this is a topic update — no invite generation needed
|
|
706
|
+
if (config.isOnboarded()) {
|
|
707
|
+
console.log('\nDisclosure topics updated. Your agent will use these on the next inbound call.\n');
|
|
708
|
+
return true;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
console.log('\nStep 4 of 4: Generating your first invite...\n');
|
|
712
|
+
|
|
713
|
+
// Extract identity from disclosure submission (parsed = raw JSON, result = validated)
|
|
714
|
+
const ownerName = parsed.owner_name
|
|
715
|
+
|| extractNameFromPersonality(result.manifest?.personality_notes)
|
|
716
|
+
|| process.env.USER
|
|
717
|
+
|| 'Agent Owner';
|
|
718
|
+
|
|
719
|
+
const agentName = args.flags.name
|
|
720
|
+
|| parsed.agent_name
|
|
721
|
+
|| config.getAgent().name
|
|
722
|
+
|| process.env.A2A_AGENT_NAME
|
|
723
|
+
|| `${ownerName}'s Agent`;
|
|
724
|
+
|
|
725
|
+
// Save identity to config
|
|
726
|
+
config.setAgent({ name: agentName, owner_name: ownerName });
|
|
727
|
+
|
|
728
|
+
const hostname = config.getAgent().hostname || process.env.A2A_HOSTNAME || 'localhost';
|
|
729
|
+
|
|
730
|
+
const publicTopics = getTierTopics(tiersData.public);
|
|
731
|
+
const publicTools = getTierTools(tiersData.public);
|
|
732
|
+
|
|
733
|
+
const { token } = store.create({
|
|
734
|
+
name: agentName,
|
|
735
|
+
owner: ownerName,
|
|
736
|
+
permissions: 'public',
|
|
737
|
+
disclosure: 'minimal',
|
|
738
|
+
expires: 'never',
|
|
739
|
+
maxCalls: null,
|
|
740
|
+
allowedTopics: publicTopics,
|
|
741
|
+
allowedGoals: tokenGoals,
|
|
742
|
+
allowedTools: publicTools.length > 0 ? publicTools : null,
|
|
743
|
+
notify: 'all'
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
const inviteUrl = `a2a://${hostname}/${token}`;
|
|
747
|
+
console.log(` Invite URL: ${inviteUrl}`);
|
|
748
|
+
console.log(' Share this invite to let other agents call you.\n');
|
|
749
|
+
|
|
750
|
+
config.completeOnboarding();
|
|
751
|
+
console.log('Onboarding complete.\n');
|
|
752
|
+
console.log(` Config: ${CONFIG_PATH}`);
|
|
753
|
+
console.log(` Disclosure: ${MANIFEST_FILE}`);
|
|
754
|
+
console.log(` Invite: ${inviteUrl}\n`);
|
|
755
|
+
|
|
756
|
+
// Native app install should be part of the onboarding tail on macOS so users
|
|
757
|
+
// don't end up launching the app before a2a setup is complete.
|
|
758
|
+
if (os.platform() === 'darwin' && !findNativeApp()) {
|
|
759
|
+
if (isInteractiveShell()) {
|
|
760
|
+
const installNow = await promptYesNo('Install the native macOS app? [Y/n] ');
|
|
761
|
+
if (installNow) {
|
|
762
|
+
const result = installNativeMacApp({ force: false, quiet: false });
|
|
763
|
+
if (result.success) {
|
|
764
|
+
if (result.reason === 'already_current') {
|
|
765
|
+
console.log(`Native app already installed at current version (${result.version}).`);
|
|
766
|
+
console.log(`Path: ${result.appPath}\n`);
|
|
767
|
+
} else {
|
|
768
|
+
console.log(`Native app installed (v${result.version}).`);
|
|
769
|
+
console.log(`Path: ${result.appPath}\n`);
|
|
770
|
+
}
|
|
771
|
+
} else {
|
|
772
|
+
console.warn(`Native app install failed: ${result.error || 'unknown error'}`);
|
|
773
|
+
console.warn('You can retry with: a2a app install\n');
|
|
774
|
+
}
|
|
775
|
+
} else {
|
|
776
|
+
console.log('You can install the native app later with: a2a app install\n');
|
|
777
|
+
}
|
|
778
|
+
} else {
|
|
779
|
+
console.log('Install the native macOS app with: a2a app install\n');
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
return true;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
async function resolveInviteHostname() {
|
|
787
|
+
const { resolveInviteHost } = require('../src/lib/invite-host');
|
|
788
|
+
|
|
789
|
+
try {
|
|
790
|
+
const { A2AConfig } = require('../src/lib/config');
|
|
791
|
+
const config = new A2AConfig();
|
|
792
|
+
const agent = config.getAgent() || {};
|
|
793
|
+
const onboarding = config.getAll().onboarding || {};
|
|
794
|
+
|
|
795
|
+
// If hostname is set without a port (e.g., "149.28.213.47"), assume port 80
|
|
796
|
+
// (user configured reverse proxy or direct bind to 80)
|
|
797
|
+
// If hostname has a port (e.g., "149.28.213.47:3007"), use that port
|
|
798
|
+
// If no hostname set, use server_port from onboarding
|
|
799
|
+
const hostname = agent.hostname || '';
|
|
800
|
+
const hasExplicitPort = hostname.includes(':') && !hostname.startsWith('[');
|
|
801
|
+
|
|
802
|
+
let defaultPort;
|
|
803
|
+
if (hasExplicitPort) {
|
|
804
|
+
defaultPort = null; // Will be parsed from hostname
|
|
805
|
+
} else if (hostname && !hostname.includes('localhost')) {
|
|
806
|
+
// External hostname without port = assume port 80 (reverse proxy or direct)
|
|
807
|
+
defaultPort = 80;
|
|
808
|
+
} else {
|
|
809
|
+
// Local or no hostname - use actual server port
|
|
810
|
+
defaultPort = onboarding.server_port || process.env.PORT || process.env.A2A_PORT || 80;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
const resolved = await resolveInviteHost({
|
|
814
|
+
config,
|
|
815
|
+
defaultPort
|
|
816
|
+
});
|
|
817
|
+
return resolved;
|
|
818
|
+
} catch (err) {
|
|
819
|
+
return resolveInviteHost({
|
|
820
|
+
fallbackHost: process.env.OPENCLAW_HOSTNAME || process.env.HOSTNAME || 'localhost',
|
|
821
|
+
defaultPort: process.env.PORT || process.env.A2A_PORT || 80
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// Commands
|
|
827
|
+
const commands = {
|
|
828
|
+
create: async (args) => {
|
|
829
|
+
const { A2AConfig } = require('../src/lib/config');
|
|
830
|
+
const { loadManifest, getTopicsForTier } = require('../src/lib/disclosure');
|
|
831
|
+
const config = new A2AConfig();
|
|
832
|
+
|
|
833
|
+
// Parse max-calls: number, 'unlimited', or default (unlimited)
|
|
834
|
+
let maxCalls = null; // Default: unlimited
|
|
835
|
+
if (args.flags['max-calls']) {
|
|
836
|
+
if (args.flags['max-calls'] === 'unlimited') {
|
|
837
|
+
maxCalls = null;
|
|
838
|
+
} else {
|
|
839
|
+
maxCalls = parseInt(args.flags['max-calls']) || null;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// Get tier from --tier or --permissions flag
|
|
844
|
+
const tier = args.flags.tier || args.flags.t || args.flags.permissions || args.flags.p || 'public';
|
|
845
|
+
const configTier = config.getTiers?.()[tier] || {};
|
|
846
|
+
|
|
847
|
+
// Get owner from flag or config
|
|
848
|
+
const configAgent = config.getAgent() || {};
|
|
849
|
+
const ownerName = args.flags.owner || args.flags.o || configAgent.owner || configAgent.name || null;
|
|
850
|
+
|
|
851
|
+
// Get topics from disclosure manifest based on tier (with inheritance)
|
|
852
|
+
const tierTopics = getTopicsForTier(tier);
|
|
853
|
+
|
|
854
|
+
// Parse custom topics if provided, otherwise use tier topics
|
|
855
|
+
let allowedTopics;
|
|
856
|
+
if (args.flags.topics) {
|
|
857
|
+
allowedTopics = args.flags.topics.split(',').map(t => t.trim());
|
|
858
|
+
} else if (tierTopics.topics && tierTopics.topics.length > 0) {
|
|
859
|
+
allowedTopics = tierTopics.topics.map(t => t.topic || t);
|
|
860
|
+
} else {
|
|
861
|
+
allowedTopics = null;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// Get objectives from disclosure
|
|
865
|
+
const objectives = tierTopics.objectives || [];
|
|
866
|
+
const allowedTools = args.flags.tools
|
|
867
|
+
? String(args.flags.tools).split(',').map(t => t.trim()).filter(Boolean)
|
|
868
|
+
: (Array.isArray(configTier.allowed_tools) ? configTier.allowed_tools : null);
|
|
869
|
+
const timeoutMsRaw = args.flags['timeout-ms'] || args.flags.timeout_ms;
|
|
870
|
+
const timeoutMs = timeoutMsRaw ? Number.parseInt(String(timeoutMsRaw), 10) : null;
|
|
871
|
+
|
|
872
|
+
const { token, record } = store.create({
|
|
873
|
+
name: args.flags.name || args.flags.n || 'unnamed',
|
|
874
|
+
owner: ownerName,
|
|
875
|
+
expires: args.flags.expires || args.flags.e || 'never',
|
|
876
|
+
permissions: tier,
|
|
877
|
+
notify: args.flags.notify || 'all',
|
|
878
|
+
maxCalls,
|
|
879
|
+
allowedTopics,
|
|
880
|
+
allowedGoals: objectives.map(o => o.objective || o),
|
|
881
|
+
allowedTools,
|
|
882
|
+
timeoutMs
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
const resolvedHost = await resolveInviteHostname();
|
|
886
|
+
const hostname = resolvedHost.host;
|
|
887
|
+
const inviteUrl = `a2a://${hostname}/${token}`;
|
|
888
|
+
|
|
889
|
+
const expiresText = record.expires_at
|
|
890
|
+
? new Date(record.expires_at).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })
|
|
891
|
+
: 'never';
|
|
892
|
+
|
|
893
|
+
if (resolvedHost.warnings && resolvedHost.warnings.length) {
|
|
894
|
+
for (const w of resolvedHost.warnings) {
|
|
895
|
+
console.warn(`\n⚠️ ${w}`);
|
|
896
|
+
}
|
|
897
|
+
console.warn('');
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
// Auto-link to contact if specified
|
|
901
|
+
const linkContact = args.flags.link || args.flags.l;
|
|
902
|
+
if (linkContact) {
|
|
903
|
+
const linkResult = store.linkTokenToContact(linkContact, record.id);
|
|
904
|
+
if (linkResult.success) {
|
|
905
|
+
console.log(`✅ Token created & linked to ${linkContact}\n`);
|
|
906
|
+
} else {
|
|
907
|
+
console.log(`✅ Token created (link failed: ${linkResult.error})\n`);
|
|
908
|
+
}
|
|
909
|
+
} else {
|
|
910
|
+
console.log(`✅ A2A token created\n`);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
console.log(`Name: ${record.name}`);
|
|
914
|
+
if (record.owner) console.log(`Owner: ${record.owner}`);
|
|
915
|
+
console.log(`Expires: ${record.expires_at || 'never'}`);
|
|
916
|
+
console.log(`Tier: ${record.tier}`);
|
|
917
|
+
console.log(`Topics: ${record.allowed_topics.join(', ')}`);
|
|
918
|
+
if (Array.isArray(record.allowed_tools) && record.allowed_tools.length > 0) {
|
|
919
|
+
console.log(`Tools: ${record.allowed_tools.join(', ')}`);
|
|
920
|
+
}
|
|
921
|
+
console.log(`Notify: ${record.notify}`);
|
|
922
|
+
console.log(`Max calls: ${record.max_calls || 'unlimited'}`);
|
|
923
|
+
if (record.timeout_ms) console.log(`Turn timeout: ${record.timeout_ms}ms`);
|
|
924
|
+
if (linkContact) console.log(`Linked to: ${linkContact}`);
|
|
925
|
+
console.log(`\nTo revoke: a2a revoke ${record.id}`);
|
|
926
|
+
console.log(`\n${'─'.repeat(50)}`);
|
|
927
|
+
console.log(`📋 SHAREABLE INVITE (copy everything below):`);
|
|
928
|
+
console.log(`${'─'.repeat(50)}\n`);
|
|
929
|
+
|
|
930
|
+
// Get agent name from config (reuse configAgent from earlier)
|
|
931
|
+
const myAgentName = configAgent.name || 'my agent';
|
|
932
|
+
const ownerText = record.owner;
|
|
933
|
+
|
|
934
|
+
// Format topics as bullet list
|
|
935
|
+
const topicsArray = record.allowed_topics || [];
|
|
936
|
+
const topicsList = topicsArray.length > 0
|
|
937
|
+
? topicsArray.map(t => ` • ${t}`).join('\n')
|
|
938
|
+
: '';
|
|
939
|
+
|
|
940
|
+
// Format objectives as bullet list
|
|
941
|
+
const goalsArray = record.allowed_goals || [];
|
|
942
|
+
const goalsList = goalsArray.length > 0
|
|
943
|
+
? goalsArray.map(g => ` • ${g}`).join('\n')
|
|
944
|
+
: '';
|
|
945
|
+
|
|
946
|
+
// Build invite header
|
|
947
|
+
const inviteHeader = ownerText
|
|
948
|
+
? `**${ownerText}** invites you to connect with their agent **${myAgentName}**`
|
|
949
|
+
: `You're invited to connect with **${myAgentName}**`;
|
|
950
|
+
|
|
951
|
+
const invite = `🤝 **Agent-to-Agent Invite**
|
|
952
|
+
|
|
953
|
+
${inviteHeader}
|
|
954
|
+
|
|
955
|
+
\`\`\`
|
|
956
|
+
${inviteUrl}
|
|
957
|
+
\`\`\`
|
|
958
|
+
${topicsList ? `\n💬 **Topics:**\n${topicsList}\n` : ''}${goalsList ? `\n🎯 **Goals:**\n${goalsList}\n` : ''}${expiresText !== 'never' ? `⏰ Expires: ${expiresText}\n` : ''}
|
|
959
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
960
|
+
|
|
961
|
+
📦 **Getting Started**
|
|
962
|
+
|
|
963
|
+
**1️⃣ Install**
|
|
964
|
+
\`\`\`
|
|
965
|
+
npm install -g a2acalling
|
|
966
|
+
\`\`\`
|
|
967
|
+
|
|
968
|
+
**2️⃣ Quick Setup** _(first time only)_
|
|
969
|
+
\`\`\`
|
|
970
|
+
a2a quickstart
|
|
971
|
+
\`\`\`
|
|
972
|
+
|
|
973
|
+
**3️⃣ Add Contact**
|
|
974
|
+
\`\`\`
|
|
975
|
+
a2a add "${inviteUrl}" "${ownerText || 'friend'}"
|
|
976
|
+
\`\`\`
|
|
977
|
+
|
|
978
|
+
**4️⃣ Say Hello!**
|
|
979
|
+
\`\`\`
|
|
980
|
+
a2a call "${ownerText || 'friend'}" "Hello! My owner asked me to reach out."
|
|
981
|
+
\`\`\`
|
|
982
|
+
|
|
983
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
984
|
+
|
|
985
|
+
⚡ **One-liner** _(already set up?)_
|
|
986
|
+
\`\`\`
|
|
987
|
+
a2a add "${inviteUrl}" "${ownerText || 'friend'}" && a2a call "${ownerText || 'friend'}" "Hello!"
|
|
988
|
+
\`\`\`
|
|
989
|
+
|
|
990
|
+
🔗 Docs: https://github.com/onthegonow/a2a_calling`;
|
|
991
|
+
|
|
992
|
+
console.log(invite);
|
|
993
|
+
console.log(`\n${'─'.repeat(50)}`);
|
|
994
|
+
},
|
|
995
|
+
|
|
996
|
+
list: () => {
|
|
997
|
+
const tokens = store.list();
|
|
998
|
+
if (tokens.length === 0) {
|
|
999
|
+
console.log('No active A2A tokens.');
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
console.log('Active A2A tokens:\n');
|
|
1004
|
+
for (const t of tokens) {
|
|
1005
|
+
const expired = t.expires_at && new Date(t.expires_at) < new Date();
|
|
1006
|
+
const status = expired ? '⚠️ EXPIRED' : '✅ Active';
|
|
1007
|
+
const tier = t.tier || 'public';
|
|
1008
|
+
const topics = t.allowed_topics || ['chat'];
|
|
1009
|
+
console.log(`${status} ${t.id}`);
|
|
1010
|
+
console.log(` Name: ${t.name}`);
|
|
1011
|
+
console.log(` Tier: ${tier} → ${topics.join(', ')}`);
|
|
1012
|
+
console.log(` Expires: ${t.expires_at || 'never'}`);
|
|
1013
|
+
console.log(` Calls: ${t.calls_made}${t.max_calls ? '/' + t.max_calls : ''}`);
|
|
1014
|
+
console.log();
|
|
1015
|
+
}
|
|
1016
|
+
},
|
|
1017
|
+
|
|
1018
|
+
revoke: (args) => {
|
|
1019
|
+
const id = args._[1];
|
|
1020
|
+
if (!id) {
|
|
1021
|
+
console.error('Usage: a2a revoke <token_id>');
|
|
1022
|
+
process.exit(1);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
const result = store.revoke(id);
|
|
1026
|
+
if (!result.success) {
|
|
1027
|
+
console.error(`Token not found: ${id}`);
|
|
1028
|
+
process.exit(1);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
console.log(`✅ Token revoked: ${result.record.name} (${result.record.id})`);
|
|
1032
|
+
},
|
|
1033
|
+
|
|
1034
|
+
add: (args) => {
|
|
1035
|
+
const url = args._[1];
|
|
1036
|
+
const name = args._[2] || args.flags.name;
|
|
1037
|
+
|
|
1038
|
+
if (!url) {
|
|
1039
|
+
console.error('Usage: a2a add <invite_url> [name]');
|
|
1040
|
+
process.exit(1);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
try {
|
|
1044
|
+
const result = store.addContact(url, { name });
|
|
1045
|
+
if (!result.success) {
|
|
1046
|
+
console.log(`Contact already registered: ${result.existing.name}`);
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
console.log(`✅ Contact added: ${result.contact.name} (${result.contact.host})`);
|
|
1050
|
+
} catch (err) {
|
|
1051
|
+
console.error(err.message);
|
|
1052
|
+
process.exit(1);
|
|
1053
|
+
}
|
|
1054
|
+
},
|
|
1055
|
+
|
|
1056
|
+
remotes: () => {
|
|
1057
|
+
// Alias for contacts
|
|
1058
|
+
commands.contacts({ _: ['contacts'], flags: {} });
|
|
1059
|
+
},
|
|
1060
|
+
|
|
1061
|
+
contacts: (args) => {
|
|
1062
|
+
const subcommand = args._[1];
|
|
1063
|
+
|
|
1064
|
+
// Sub-commands
|
|
1065
|
+
if (subcommand === 'add') return commands['contacts:add'](args);
|
|
1066
|
+
if (subcommand === 'show') return commands['contacts:show'](args);
|
|
1067
|
+
if (subcommand === 'edit') return commands['contacts:edit'](args);
|
|
1068
|
+
if (subcommand === 'ping') return commands['contacts:ping'](args);
|
|
1069
|
+
if (subcommand === 'link') return commands['contacts:link'](args);
|
|
1070
|
+
if (subcommand === 'rm' || subcommand === 'remove') return commands['contacts:rm'](args);
|
|
1071
|
+
|
|
1072
|
+
// Default: list contacts
|
|
1073
|
+
const contacts = store.listContacts();
|
|
1074
|
+
if (contacts.length === 0) {
|
|
1075
|
+
console.log('📇 No contacts yet.\n');
|
|
1076
|
+
console.log('Add one with: a2a contacts add <invite_url>');
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
console.log(`📇 Agent Contacts (${contacts.length})\n`);
|
|
1081
|
+
for (const r of contacts) {
|
|
1082
|
+
const statusIcon = r.status === 'online' ? '🟢' : r.status === 'offline' ? '🔴' : '⚪';
|
|
1083
|
+
const ownerText = r.owner ? ` — ${r.owner}` : '';
|
|
1084
|
+
|
|
1085
|
+
// Permission badge from linked token (what YOU gave THEM)
|
|
1086
|
+
let permBadge = '';
|
|
1087
|
+
if (r.linked_token) {
|
|
1088
|
+
const tier = r.linked_token.tier || 'public';
|
|
1089
|
+
permBadge = tier === 'family' ? ' ⚡' : tier === 'friends' ? ' 🔧' : ' 🌐';
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
console.log(`${statusIcon} ${r.name}${ownerText}${permBadge}`);
|
|
1093
|
+
if (r.tags && r.tags.length > 0) {
|
|
1094
|
+
console.log(` 🏷️ ${r.tags.join(', ')}`);
|
|
1095
|
+
}
|
|
1096
|
+
if (r.last_seen) {
|
|
1097
|
+
const ago = formatTimeAgo(new Date(r.last_seen));
|
|
1098
|
+
console.log(` 📍 Last seen: ${ago}`);
|
|
1099
|
+
}
|
|
1100
|
+
console.log();
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
console.log('Legend: 🌐 public 🔧 friends ⚡ family');
|
|
1104
|
+
},
|
|
1105
|
+
|
|
1106
|
+
'contacts:add': async (args) => {
|
|
1107
|
+
const url = args._[2];
|
|
1108
|
+
if (!url) {
|
|
1109
|
+
console.error('Usage: a2a contacts add <invite_url> [options]');
|
|
1110
|
+
console.error('Options:');
|
|
1111
|
+
console.error(' --name, -n Agent name');
|
|
1112
|
+
console.error(' --owner, -o Owner name');
|
|
1113
|
+
console.error(' --server-name Server label (optional)');
|
|
1114
|
+
console.error(' --notes Notes about this contact');
|
|
1115
|
+
console.error(' --tags Comma-separated tags');
|
|
1116
|
+
console.error(' --link Link to token ID you gave them');
|
|
1117
|
+
console.error(' --public-key Ed25519 public key (base64, or "fetch" to get from /status)');
|
|
1118
|
+
process.exit(1);
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
const options = {
|
|
1122
|
+
name: args.flags.name || args.flags.n,
|
|
1123
|
+
owner: args.flags.owner || args.flags.o,
|
|
1124
|
+
server_name: args.flags['server-name'] || args.flags.server_name || args.flags.serverName || null,
|
|
1125
|
+
notes: args.flags.notes,
|
|
1126
|
+
tags: args.flags.tags ? args.flags.tags.split(',').map(t => t.trim()) : [],
|
|
1127
|
+
linkedTokenId: args.flags.link || null
|
|
1128
|
+
};
|
|
1129
|
+
|
|
1130
|
+
// A2A-52: fetch or accept public key for identity verification
|
|
1131
|
+
const pubKeyFlag = args.flags['public-key'] || args.flags.public_key || args.flags.publicKey;
|
|
1132
|
+
if (pubKeyFlag === 'fetch' || pubKeyFlag === true) {
|
|
1133
|
+
try {
|
|
1134
|
+
const client = new A2AClient({});
|
|
1135
|
+
const statusResult = await client.status(url);
|
|
1136
|
+
if (statusResult.public_key) {
|
|
1137
|
+
options.public_key = statusResult.public_key;
|
|
1138
|
+
const { fingerprint: fpFunc } = require('../src/lib/crypto');
|
|
1139
|
+
console.log(` Fetched public key: ${fpFunc(statusResult.public_key)}`);
|
|
1140
|
+
}
|
|
1141
|
+
} catch (fetchErr) {
|
|
1142
|
+
console.error(` Warning: could not fetch public key from /status: ${fetchErr.message}`);
|
|
1143
|
+
}
|
|
1144
|
+
} else if (pubKeyFlag && typeof pubKeyFlag === 'string') {
|
|
1145
|
+
options.public_key = pubKeyFlag;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
try {
|
|
1149
|
+
const result = store.addContact(url, options);
|
|
1150
|
+
if (!result.success) {
|
|
1151
|
+
console.log(`Contact already exists: ${result.existing.name}`);
|
|
1152
|
+
return;
|
|
1153
|
+
}
|
|
1154
|
+
console.log(`✅ Contact added: ${result.contact.name}`);
|
|
1155
|
+
if (result.contact.owner) console.log(` Owner: ${result.contact.owner}`);
|
|
1156
|
+
if (result.contact.server_name) console.log(` Server: ${result.contact.server_name}`);
|
|
1157
|
+
console.log(` Host: ${result.contact.host}`);
|
|
1158
|
+
if (options.linkedTokenId) {
|
|
1159
|
+
console.log(` Linked to token: ${options.linkedTokenId}`);
|
|
1160
|
+
} else {
|
|
1161
|
+
console.log(`\n💡 Link a token: a2a contacts link ${result.contact.name} <token_id>`);
|
|
1162
|
+
}
|
|
1163
|
+
} catch (err) {
|
|
1164
|
+
console.error(err.message);
|
|
1165
|
+
process.exit(1);
|
|
1166
|
+
}
|
|
1167
|
+
},
|
|
1168
|
+
|
|
1169
|
+
'contacts:show': (args) => {
|
|
1170
|
+
const name = args._[2];
|
|
1171
|
+
if (!name) {
|
|
1172
|
+
console.error('Usage: a2a contacts show <name>');
|
|
1173
|
+
process.exit(1);
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
// Get contact with linked token info
|
|
1177
|
+
const contacts = store.listContacts();
|
|
1178
|
+
const remote = contacts.find(r => r.name === name || r.id === name);
|
|
1179
|
+
if (!remote) {
|
|
1180
|
+
console.error(`Contact not found: ${name}`);
|
|
1181
|
+
process.exit(1);
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
const statusIcon = remote.status === 'online' ? '🟢' : remote.status === 'offline' ? '🔴' : '⚪';
|
|
1185
|
+
|
|
1186
|
+
console.log(`\n${'═'.repeat(50)}`);
|
|
1187
|
+
console.log(`${statusIcon} ${remote.name}`);
|
|
1188
|
+
console.log(`${'═'.repeat(50)}\n`);
|
|
1189
|
+
|
|
1190
|
+
if (remote.owner) console.log(`👤 Owner: ${remote.owner}`);
|
|
1191
|
+
console.log(`🌐 Host: ${remote.host}`);
|
|
1192
|
+
|
|
1193
|
+
// Show linked token (permissions you gave them)
|
|
1194
|
+
if (remote.linked_token) {
|
|
1195
|
+
const t = remote.linked_token;
|
|
1196
|
+
const tier = t.tier || 'public';
|
|
1197
|
+
const topics = t.allowed_topics || ['chat'];
|
|
1198
|
+
const tierIcon = tier === 'family' ? '⚡' : tier === 'friends' ? '🔧' : '🌐';
|
|
1199
|
+
console.log(`🔐 Your token to them: ${t.id}`);
|
|
1200
|
+
console.log(` Tier: ${tierIcon} ${tier}`);
|
|
1201
|
+
console.log(` Topics: ${topics.join(', ')}`);
|
|
1202
|
+
console.log(` Calls: ${t.calls_made}${t.max_calls ? '/' + t.max_calls : ''}`);
|
|
1203
|
+
if (t.revoked) console.log(` ⚠️ REVOKED`);
|
|
1204
|
+
} else {
|
|
1205
|
+
console.log(`🔐 No linked token (you haven't given them access yet)`);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
// A2A-52: show cryptographic identity verification status
|
|
1209
|
+
if (remote.public_key) {
|
|
1210
|
+
const { fingerprint: fpFunc } = require('../src/lib/crypto');
|
|
1211
|
+
console.log(`🔑 Identity: verified`);
|
|
1212
|
+
console.log(` Fingerprint: ${fpFunc(remote.public_key)}`);
|
|
1213
|
+
} else {
|
|
1214
|
+
console.log(`🔑 Identity: unverified (no public key pinned)`);
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
if (remote.tags && remote.tags.length > 0) {
|
|
1218
|
+
console.log(`🏷️ Tags: ${remote.tags.join(', ')}`);
|
|
1219
|
+
}
|
|
1220
|
+
if (remote.notes) {
|
|
1221
|
+
console.log(`📝 Notes: ${remote.notes}`);
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
console.log(`\n📅 Added: ${new Date(remote.added_at).toLocaleDateString()}`);
|
|
1225
|
+
if (remote.last_seen) {
|
|
1226
|
+
console.log(`📍 Last seen: ${formatTimeAgo(new Date(remote.last_seen))}`);
|
|
1227
|
+
}
|
|
1228
|
+
if (remote.last_check) {
|
|
1229
|
+
console.log(`🔄 Last check: ${formatTimeAgo(new Date(remote.last_check))}`);
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
console.log(`\n${'─'.repeat(50)}`);
|
|
1233
|
+
console.log(`Quick actions:`);
|
|
1234
|
+
console.log(` a2a contacts ping ${name}`);
|
|
1235
|
+
console.log(` a2a call ${name} "Hello!"`);
|
|
1236
|
+
if (!remote.linked_token) {
|
|
1237
|
+
console.log(` a2a contacts link ${name} <token_id>`);
|
|
1238
|
+
}
|
|
1239
|
+
console.log(`${'─'.repeat(50)}\n`);
|
|
1240
|
+
},
|
|
1241
|
+
|
|
1242
|
+
'contacts:edit': (args) => {
|
|
1243
|
+
const name = args._[2];
|
|
1244
|
+
if (!name) {
|
|
1245
|
+
console.error('Usage: a2a contacts edit <name> [options]');
|
|
1246
|
+
console.error('Options:');
|
|
1247
|
+
console.error(' --name New name');
|
|
1248
|
+
console.error(' --owner Owner name');
|
|
1249
|
+
console.error(' --server-name Server label');
|
|
1250
|
+
console.error(' --notes Notes');
|
|
1251
|
+
console.error(' --tags Comma-separated tags');
|
|
1252
|
+
process.exit(1);
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
const updates = {};
|
|
1256
|
+
if (args.flags.name) updates.name = args.flags.name;
|
|
1257
|
+
if (args.flags.owner) updates.owner = args.flags.owner;
|
|
1258
|
+
if (args.flags['server-name'] || args.flags.server_name || args.flags.serverName) updates.server_name = args.flags['server-name'] || args.flags.server_name || args.flags.serverName;
|
|
1259
|
+
if (args.flags.notes) updates.notes = args.flags.notes;
|
|
1260
|
+
if (args.flags.tags) updates.tags = args.flags.tags.split(',').map(t => t.trim());
|
|
1261
|
+
|
|
1262
|
+
if (Object.keys(updates).length === 0) {
|
|
1263
|
+
console.error('No updates specified. Use --name, --owner, --notes, or --tags');
|
|
1264
|
+
process.exit(1);
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
const result = store.updateContact(name, updates);
|
|
1268
|
+
if (!result.success) {
|
|
1269
|
+
console.error(`Contact not found: ${name}`);
|
|
1270
|
+
process.exit(1);
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
console.log(`✅ Contact updated: ${(result.contact || result.remote).name}`);
|
|
1274
|
+
},
|
|
1275
|
+
|
|
1276
|
+
'contacts:link': (args) => {
|
|
1277
|
+
const contactName = args._[2];
|
|
1278
|
+
const tokenId = args._[3];
|
|
1279
|
+
|
|
1280
|
+
if (!contactName || !tokenId) {
|
|
1281
|
+
console.error('Usage: a2a contacts link <contact_name> <token_id>');
|
|
1282
|
+
console.error('\nLinks a token you created to a contact, showing what access they have.');
|
|
1283
|
+
console.error('\nExample:');
|
|
1284
|
+
console.error(' a2a contacts link Alice tok_abc123');
|
|
1285
|
+
process.exit(1);
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
const result = store.linkTokenToContact(contactName, tokenId);
|
|
1289
|
+
if (!result.success) {
|
|
1290
|
+
if (result.error === 'contact_not_found') {
|
|
1291
|
+
console.error(`Contact not found: ${contactName}`);
|
|
1292
|
+
} else if (result.error === 'token_not_found') {
|
|
1293
|
+
console.error(`Token not found: ${tokenId}`);
|
|
1294
|
+
}
|
|
1295
|
+
process.exit(1);
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
const permLabel = result.token.tier === 'family' ? '⚡ family' :
|
|
1299
|
+
result.token.tier === 'friends' ? '🔧 friends' : '🌐 public';
|
|
1300
|
+
|
|
1301
|
+
console.log(`✅ Linked token to contact`);
|
|
1302
|
+
console.log(` Contact: ${result.contact?.name || result.remote.name}`);
|
|
1303
|
+
console.log(` Token: ${result.token.id} (${result.token.name})`);
|
|
1304
|
+
console.log(` Permissions: ${permLabel}`);
|
|
1305
|
+
},
|
|
1306
|
+
|
|
1307
|
+
'contacts:ping': async (args) => {
|
|
1308
|
+
const name = args._[2];
|
|
1309
|
+
if (!name) {
|
|
1310
|
+
console.error('Usage: a2a contacts ping <name>');
|
|
1311
|
+
process.exit(1);
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
const remote = store.getContact(name);
|
|
1315
|
+
if (!remote) {
|
|
1316
|
+
console.error(`Contact not found: ${name}`);
|
|
1317
|
+
process.exit(1);
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
const client = new A2AClient({});
|
|
1321
|
+
const url = `a2a://${remote.host}/${remote.token}`;
|
|
1322
|
+
|
|
1323
|
+
console.log(`🔍 Pinging ${remote.name}...`);
|
|
1324
|
+
|
|
1325
|
+
try {
|
|
1326
|
+
const result = await client.ping(url);
|
|
1327
|
+
store.updateContactStatus(name, 'online');
|
|
1328
|
+
console.log(`🟢 ${remote.name} is online`);
|
|
1329
|
+
console.log(` Agent: ${result.name}`);
|
|
1330
|
+
console.log(` Version: ${result.version}`);
|
|
1331
|
+
|
|
1332
|
+
// A2A-52: also fetch /status to refresh public key
|
|
1333
|
+
try {
|
|
1334
|
+
const statusResult = await client.status(url);
|
|
1335
|
+
if (statusResult.public_key) {
|
|
1336
|
+
const { fingerprint: fpFunc } = require('../src/lib/crypto');
|
|
1337
|
+
if (remote.public_key && remote.public_key !== statusResult.public_key) {
|
|
1338
|
+
console.log(` ⚠️ Public key changed!`);
|
|
1339
|
+
console.log(` Old: ${fpFunc(remote.public_key)}`);
|
|
1340
|
+
console.log(` New: ${fpFunc(statusResult.public_key)}`);
|
|
1341
|
+
}
|
|
1342
|
+
store.updateContact(name, { public_key: statusResult.public_key });
|
|
1343
|
+
console.log(` 🔑 Fingerprint: ${fpFunc(statusResult.public_key)}`);
|
|
1344
|
+
}
|
|
1345
|
+
} catch (_) {
|
|
1346
|
+
// /status fetch is best-effort during ping
|
|
1347
|
+
}
|
|
1348
|
+
} catch (err) {
|
|
1349
|
+
store.updateContactStatus(name, 'offline', err.message);
|
|
1350
|
+
console.log(`🔴 ${remote.name} is offline`);
|
|
1351
|
+
console.log(` Error: ${err.message}`);
|
|
1352
|
+
}
|
|
1353
|
+
},
|
|
1354
|
+
|
|
1355
|
+
'contacts:rm': (args) => {
|
|
1356
|
+
const name = args._[2];
|
|
1357
|
+
if (!name) {
|
|
1358
|
+
console.error('Usage: a2a contacts rm <name>');
|
|
1359
|
+
process.exit(1);
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
const result = store.removeContact(name);
|
|
1363
|
+
if (!result.success) {
|
|
1364
|
+
console.error(`Contact not found: ${name}`);
|
|
1365
|
+
process.exit(1);
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
console.log(`✅ Contact removed: ${(result.contact || result.remote).name}`);
|
|
1369
|
+
},
|
|
1370
|
+
|
|
1371
|
+
// ========== CONVERSATIONS ==========
|
|
1372
|
+
|
|
1373
|
+
conversations: (args) => {
|
|
1374
|
+
const subcommand = args._[1];
|
|
1375
|
+
|
|
1376
|
+
if (subcommand === 'show') return commands['conversations:show'](args);
|
|
1377
|
+
if (subcommand === 'end') return commands['conversations:end'](args);
|
|
1378
|
+
|
|
1379
|
+
// Default: list conversations
|
|
1380
|
+
const cs = getConvStore();
|
|
1381
|
+
if (!cs) {
|
|
1382
|
+
console.log('💬 Conversation storage not available.');
|
|
1383
|
+
console.log('Install: npm install better-sqlite3');
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
const { contact, status, limit = 20 } = args.flags;
|
|
1388
|
+
const conversations = cs.listConversations({
|
|
1389
|
+
contactId: contact,
|
|
1390
|
+
status,
|
|
1391
|
+
limit: parseInt(limit),
|
|
1392
|
+
includeMessages: true,
|
|
1393
|
+
messageLimit: 1
|
|
1394
|
+
});
|
|
1395
|
+
|
|
1396
|
+
if (conversations.length === 0) {
|
|
1397
|
+
console.log('💬 No conversations yet.');
|
|
1398
|
+
return;
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
console.log(`💬 Conversations (${conversations.length})\n`);
|
|
1402
|
+
for (const conv of conversations) {
|
|
1403
|
+
const statusIcon = conv.status === 'concluded' ? '✅' : conv.status === 'timeout' ? '⏱️' : '💬';
|
|
1404
|
+
const timeAgo = formatTimeAgo(new Date(conv.last_message_at));
|
|
1405
|
+
const preview = conv.messages?.[0]?.content?.slice(0, 50) || '';
|
|
1406
|
+
|
|
1407
|
+
console.log(`${statusIcon} ${conv.id}`);
|
|
1408
|
+
console.log(` Contact: ${conv.contact_name || conv.contact_id || 'unknown'}`);
|
|
1409
|
+
console.log(` Messages: ${conv.message_count} | ${timeAgo}`);
|
|
1410
|
+
if (conv.summary) {
|
|
1411
|
+
console.log(` Summary: ${conv.summary.slice(0, 80)}...`);
|
|
1412
|
+
} else if (preview) {
|
|
1413
|
+
console.log(` Preview: "${preview}..."`);
|
|
1414
|
+
}
|
|
1415
|
+
if (conv.owner_relevance) {
|
|
1416
|
+
console.log(` Relevance: ${conv.owner_relevance}`);
|
|
1417
|
+
}
|
|
1418
|
+
console.log();
|
|
1419
|
+
}
|
|
1420
|
+
},
|
|
1421
|
+
|
|
1422
|
+
'conversations:show': (args) => {
|
|
1423
|
+
const convId = args._[2];
|
|
1424
|
+
if (!convId) {
|
|
1425
|
+
console.error('Usage: a2a conversations show <conversation_id>');
|
|
1426
|
+
process.exit(1);
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
const cs = getConvStore();
|
|
1430
|
+
if (!cs) {
|
|
1431
|
+
console.error('Conversation storage not available. Install: npm install better-sqlite3');
|
|
1432
|
+
process.exit(1);
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
const context = cs.getConversationContext(convId, args.flags.messages || 20);
|
|
1436
|
+
if (!context) {
|
|
1437
|
+
console.error(`Conversation not found: ${convId}`);
|
|
1438
|
+
process.exit(1);
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
console.log(`\n${'═'.repeat(60)}`);
|
|
1442
|
+
console.log(`💬 ${context.id}`);
|
|
1443
|
+
console.log(`${'═'.repeat(60)}\n`);
|
|
1444
|
+
|
|
1445
|
+
console.log(`👤 Contact: ${context.contact || 'unknown'}`);
|
|
1446
|
+
console.log(`📊 Status: ${context.status}`);
|
|
1447
|
+
console.log(`📝 Messages: ${context.messageCount}`);
|
|
1448
|
+
console.log(`📅 Started: ${new Date(context.startedAt).toLocaleString()}`);
|
|
1449
|
+
if (context.endedAt) {
|
|
1450
|
+
console.log(`🏁 Ended: ${new Date(context.endedAt).toLocaleString()}`);
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
if (context.summary) {
|
|
1454
|
+
console.log(`\n${'─'.repeat(60)}`);
|
|
1455
|
+
console.log(`📋 Summary:\n${context.summary}`);
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
if (context.ownerContext) {
|
|
1459
|
+
console.log(`\n${'─'.repeat(60)}`);
|
|
1460
|
+
console.log(`🔒 Owner Context (private):`);
|
|
1461
|
+
console.log(` Relevance: ${context.ownerContext.relevance || 'unknown'}`);
|
|
1462
|
+
if (context.ownerContext.summary) {
|
|
1463
|
+
console.log(` Summary: ${context.ownerContext.summary}`);
|
|
1464
|
+
}
|
|
1465
|
+
if (context.ownerContext.goalsTouched?.length) {
|
|
1466
|
+
console.log(` Goals: ${context.ownerContext.goalsTouched.join(', ')}`);
|
|
1467
|
+
}
|
|
1468
|
+
if (context.ownerContext.actionItems?.length) {
|
|
1469
|
+
console.log(` Actions: ${context.ownerContext.actionItems.join(', ')}`);
|
|
1470
|
+
}
|
|
1471
|
+
if (context.ownerContext.followUp) {
|
|
1472
|
+
console.log(` Follow-up: ${context.ownerContext.followUp}`);
|
|
1473
|
+
}
|
|
1474
|
+
if (context.ownerContext.notes) {
|
|
1475
|
+
console.log(` Notes: ${context.ownerContext.notes}`);
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
console.log(`\n${'─'.repeat(60)}`);
|
|
1480
|
+
console.log(`Recent messages:`);
|
|
1481
|
+
console.log(`${'─'.repeat(60)}`);
|
|
1482
|
+
for (const msg of context.recentMessages) {
|
|
1483
|
+
const role = msg.direction === 'inbound' ? '← In' : '→ Out';
|
|
1484
|
+
const time = new Date(msg.timestamp).toLocaleTimeString();
|
|
1485
|
+
console.log(`\n[${time}] ${role}:`);
|
|
1486
|
+
console.log(msg.content);
|
|
1487
|
+
}
|
|
1488
|
+
console.log(`\n${'═'.repeat(60)}\n`);
|
|
1489
|
+
},
|
|
1490
|
+
|
|
1491
|
+
'conversations:end': async (args) => {
|
|
1492
|
+
const convId = args._[2];
|
|
1493
|
+
if (!convId) {
|
|
1494
|
+
console.error('Usage: a2a conversations end <conversation_id>');
|
|
1495
|
+
process.exit(1);
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
const cs = getConvStore();
|
|
1499
|
+
if (!cs) {
|
|
1500
|
+
console.error('Conversation storage not available');
|
|
1501
|
+
process.exit(1);
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
// For now, conclude without LLM summarizer
|
|
1505
|
+
const result = await cs.concludeConversation(convId, {});
|
|
1506
|
+
|
|
1507
|
+
if (!result.success) {
|
|
1508
|
+
console.error(`Failed to end conversation: ${result.error}`);
|
|
1509
|
+
process.exit(1);
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
console.log(`✅ Conversation concluded: ${convId}`);
|
|
1513
|
+
if (result.summary) {
|
|
1514
|
+
console.log(`📋 Summary: ${result.summary}`);
|
|
1515
|
+
}
|
|
1516
|
+
},
|
|
1517
|
+
|
|
1518
|
+
call: async (args) => {
|
|
1519
|
+
let target = args._[1];
|
|
1520
|
+
const message = args._.slice(2).join(' ') || args.flags.message || args.flags.m;
|
|
1521
|
+
|
|
1522
|
+
if (!target || !message) {
|
|
1523
|
+
console.error('Usage: a2a call <contact_or_url> <message>');
|
|
1524
|
+
console.error(' --single Single-turn call (one message, one response)');
|
|
1525
|
+
console.error(' --min-turns N Minimum turns before close (default: 8)');
|
|
1526
|
+
console.error(' --max-turns N Maximum turns (default: 25)');
|
|
1527
|
+
process.exit(1);
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
// Check if target is a contact name (not a URL)
|
|
1531
|
+
let url = target;
|
|
1532
|
+
let contactName = null;
|
|
1533
|
+
if (!target.startsWith('a2a://')) {
|
|
1534
|
+
const remote = store.getContact(target);
|
|
1535
|
+
if (remote) {
|
|
1536
|
+
url = `a2a://${remote.host}/${remote.token}`;
|
|
1537
|
+
contactName = remote.name;
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
const single = Boolean(args.flags.single);
|
|
1542
|
+
const callerName = args.flags.name || 'CLI User';
|
|
1543
|
+
|
|
1544
|
+
if (!single) {
|
|
1545
|
+
// Multi-turn conversation via ConversationDriver
|
|
1546
|
+
const { ConversationDriver } = require('../src/lib/conversation-driver');
|
|
1547
|
+
const { createRuntimeAdapter } = require('../src/lib/runtime-adapter');
|
|
1548
|
+
const { loadManifest } = require('../src/lib/disclosure');
|
|
1549
|
+
|
|
1550
|
+
const workspaceDir = process.env.A2A_WORKSPACE || process.env.OPENCLAW_WORKSPACE || process.cwd();
|
|
1551
|
+
const agentContext = {
|
|
1552
|
+
name: process.env.A2A_AGENT_NAME || process.env.AGENT_NAME || 'a2a-agent',
|
|
1553
|
+
owner: process.env.A2A_OWNER_NAME || process.env.USER || 'Agent Owner'
|
|
1554
|
+
};
|
|
1555
|
+
|
|
1556
|
+
const runtime = createRuntimeAdapter({ workspaceDir, agentContext });
|
|
1557
|
+
const cs = getConvStore();
|
|
1558
|
+
const disclosure = loadManifest();
|
|
1559
|
+
|
|
1560
|
+
const minTurns = parseInt(args.flags['min-turns']) || 8;
|
|
1561
|
+
const maxTurns = parseInt(args.flags['max-turns']) || 25;
|
|
1562
|
+
|
|
1563
|
+
// Build owner context from config for summarizer
|
|
1564
|
+
let ownerContext = {};
|
|
1565
|
+
let configTurnTimeoutMs = null;
|
|
1566
|
+
try {
|
|
1567
|
+
const { A2AConfig } = require('../src/lib/config');
|
|
1568
|
+
const config = new A2AConfig();
|
|
1569
|
+
const configAll = config.getAll();
|
|
1570
|
+
const tierGoals = configAll.tiers?.public?.goals || [];
|
|
1571
|
+
configTurnTimeoutMs = configAll.defaults?.turnTimeoutMs
|
|
1572
|
+
|| configAll.defaults?.turn_timeout_ms
|
|
1573
|
+
|| null;
|
|
1574
|
+
ownerContext = {
|
|
1575
|
+
goals: tierGoals,
|
|
1576
|
+
agentName: agentContext.name,
|
|
1577
|
+
ownerName: agentContext.owner
|
|
1578
|
+
};
|
|
1579
|
+
} catch (err) {
|
|
1580
|
+
// Best effort
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
// A2A-52: load keypair for request signing in multi-turn calls
|
|
1584
|
+
const _multiKeypair = config.getKeypair();
|
|
1585
|
+
const driver = new ConversationDriver({
|
|
1586
|
+
runtime,
|
|
1587
|
+
agentContext,
|
|
1588
|
+
caller: { name: callerName },
|
|
1589
|
+
endpoint: url,
|
|
1590
|
+
convStore: cs,
|
|
1591
|
+
disclosure,
|
|
1592
|
+
minTurns,
|
|
1593
|
+
maxTurns,
|
|
1594
|
+
configTurnTimeoutMs,
|
|
1595
|
+
ownerContext,
|
|
1596
|
+
privateKey: _multiKeypair ? _multiKeypair.privateKey : null,
|
|
1597
|
+
publicKey: _multiKeypair ? _multiKeypair.publicKey : null,
|
|
1598
|
+
onTurn: (info) => {
|
|
1599
|
+
const preview = info.messagePreview.length >= 80
|
|
1600
|
+
? info.messagePreview + '...'
|
|
1601
|
+
: info.messagePreview;
|
|
1602
|
+
console.log(` Turn ${info.turn} | ${info.phase} | overlap: ${info.overlapScore.toFixed(2)} | ${preview}`);
|
|
1603
|
+
}
|
|
1604
|
+
});
|
|
1605
|
+
|
|
1606
|
+
console.log(`📞 Starting multi-turn conversation with ${contactName || url}...`);
|
|
1607
|
+
console.log(` Min turns: ${minTurns} | Max turns: ${maxTurns}\n`);
|
|
1608
|
+
|
|
1609
|
+
try {
|
|
1610
|
+
const result = await driver.run(message);
|
|
1611
|
+
|
|
1612
|
+
if (contactName) {
|
|
1613
|
+
store.updateContactStatus(contactName, 'online');
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
console.log(`\n✅ Conversation complete`);
|
|
1617
|
+
console.log(` Turns: ${result.turnCount}`);
|
|
1618
|
+
console.log(` Phase: ${result.collabState.phase}`);
|
|
1619
|
+
console.log(` Overlap: ${result.collabState.overlapScore.toFixed(2)}`);
|
|
1620
|
+
if (result.collabState.candidateCollaborations.length > 0) {
|
|
1621
|
+
console.log(` Collaborations: ${result.collabState.candidateCollaborations.join(', ')}`);
|
|
1622
|
+
}
|
|
1623
|
+
console.log(` Conversation ID: ${result.conversationId}`);
|
|
1624
|
+
if (result.summary) {
|
|
1625
|
+
console.log(`\n📋 Summary:\n${result.summary}`);
|
|
1626
|
+
}
|
|
1627
|
+
} catch (err) {
|
|
1628
|
+
if (contactName) {
|
|
1629
|
+
store.updateContactStatus(contactName, 'offline', err.message);
|
|
1630
|
+
}
|
|
1631
|
+
console.error(`❌ Multi-turn call failed: ${err.message}`);
|
|
1632
|
+
process.exit(1);
|
|
1633
|
+
}
|
|
1634
|
+
return;
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
// Single-shot call (existing behavior)
|
|
1638
|
+
// A2A-52: load keypair for request signing
|
|
1639
|
+
const _callKeypair = config.getKeypair();
|
|
1640
|
+
const client = new A2AClient({
|
|
1641
|
+
caller: { name: callerName },
|
|
1642
|
+
privateKey: _callKeypair ? _callKeypair.privateKey : null,
|
|
1643
|
+
publicKey: _callKeypair ? _callKeypair.publicKey : null
|
|
1644
|
+
});
|
|
1645
|
+
|
|
1646
|
+
try {
|
|
1647
|
+
console.log(`📞 Calling ${contactName || url}...`);
|
|
1648
|
+
const response = await client.call(url, message);
|
|
1649
|
+
|
|
1650
|
+
// Update contact status on success
|
|
1651
|
+
if (contactName) {
|
|
1652
|
+
store.updateContactStatus(contactName, 'online');
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
// Persist conversation locally
|
|
1656
|
+
const cs = getConvStore();
|
|
1657
|
+
if (cs) {
|
|
1658
|
+
try {
|
|
1659
|
+
// Use remote conversation ID if provided, otherwise generate a local one
|
|
1660
|
+
const convId = response.conversation_id || `conv_${Date.now()}_${crypto.randomBytes(4).toString('hex')}`;
|
|
1661
|
+
|
|
1662
|
+
const convResult = cs.startConversation({
|
|
1663
|
+
id: convId,
|
|
1664
|
+
contactId: contactName || null,
|
|
1665
|
+
contactName: contactName || null,
|
|
1666
|
+
direction: 'outbound'
|
|
1667
|
+
});
|
|
1668
|
+
if (convResult.success === false) {
|
|
1669
|
+
console.error(`⚠️ Failed to save conversation: ${convResult.error}`);
|
|
1670
|
+
} else {
|
|
1671
|
+
const outMsg = cs.addMessage(convId, {
|
|
1672
|
+
direction: 'outbound',
|
|
1673
|
+
role: 'user',
|
|
1674
|
+
content: message
|
|
1675
|
+
});
|
|
1676
|
+
if (outMsg.success === false) {
|
|
1677
|
+
console.error(`⚠️ Failed to save outbound message: ${outMsg.error}`);
|
|
1678
|
+
}
|
|
1679
|
+
if (response.response) {
|
|
1680
|
+
const inMsg = cs.addMessage(convId, {
|
|
1681
|
+
direction: 'inbound',
|
|
1682
|
+
role: 'assistant',
|
|
1683
|
+
content: response.response
|
|
1684
|
+
});
|
|
1685
|
+
if (inMsg.success === false) {
|
|
1686
|
+
console.error(`⚠️ Failed to save inbound message: ${inMsg.error}`);
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
// Update response to include conversation ID for display
|
|
1690
|
+
if (!response.conversation_id) {
|
|
1691
|
+
response.conversation_id = convId;
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
} catch (err) {
|
|
1695
|
+
console.error(`⚠️ Error persisting conversation: ${err.message}`);
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
console.log(`\n✅ Response:\n`);
|
|
1700
|
+
console.log(response.response);
|
|
1701
|
+
if (response.conversation_id) {
|
|
1702
|
+
console.log(`\n📝 Conversation ID: ${response.conversation_id}`);
|
|
1703
|
+
}
|
|
1704
|
+
} catch (err) {
|
|
1705
|
+
// Update contact status on failure
|
|
1706
|
+
if (contactName) {
|
|
1707
|
+
store.updateContactStatus(contactName, 'offline', err.message);
|
|
1708
|
+
}
|
|
1709
|
+
console.error(`❌ Call failed: ${err.message}`);
|
|
1710
|
+
process.exit(1);
|
|
1711
|
+
}
|
|
1712
|
+
},
|
|
1713
|
+
|
|
1714
|
+
ping: async (args) => {
|
|
1715
|
+
const url = args._[1];
|
|
1716
|
+
if (!url) {
|
|
1717
|
+
console.error('Usage: a2a ping <invite_url>');
|
|
1718
|
+
process.exit(1);
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
const client = new A2AClient();
|
|
1722
|
+
const result = await client.ping(url);
|
|
1723
|
+
|
|
1724
|
+
if (result.pong) {
|
|
1725
|
+
console.log(`✅ Agent reachable at ${url}`);
|
|
1726
|
+
if (result.timestamp) {
|
|
1727
|
+
console.log(` Timestamp: ${result.timestamp}`);
|
|
1728
|
+
}
|
|
1729
|
+
} else {
|
|
1730
|
+
console.log(`❌ Agent not reachable at ${url}`);
|
|
1731
|
+
process.exit(1);
|
|
1732
|
+
}
|
|
1733
|
+
},
|
|
1734
|
+
|
|
1735
|
+
gui: async (args) => {
|
|
1736
|
+
// GUI is always safe to open even before onboarding.
|
|
1737
|
+
const tab = (args.flags.tab || args.flags.t || '').trim().toLowerCase();
|
|
1738
|
+
// A2A-41: 'settings' remains as backward-compat alias for 'permissions'
|
|
1739
|
+
const tabAliases = { settings: 'permissions' };
|
|
1740
|
+
const resolvedTab = tabAliases[tab] || tab;
|
|
1741
|
+
const allowedTabs = new Set(['contacts', 'calls', 'logs', 'permissions', 'invites']);
|
|
1742
|
+
const hash = allowedTabs.has(resolvedTab) ? `#${resolvedTab}` : '';
|
|
1743
|
+
|
|
1744
|
+
const urlFlag = args.flags.url;
|
|
1745
|
+
if (urlFlag) {
|
|
1746
|
+
const url = String(urlFlag);
|
|
1747
|
+
console.log(`Dashboard URL: ${url}`);
|
|
1748
|
+
const opened = openInBrowser(url);
|
|
1749
|
+
if (opened.attempted) {
|
|
1750
|
+
console.log(`Opening browser via: ${opened.command}`);
|
|
1751
|
+
} else {
|
|
1752
|
+
console.log('Could not auto-open browser.');
|
|
1753
|
+
}
|
|
1754
|
+
return;
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
// Prefer native app on macOS (--browser flag forces browser)
|
|
1758
|
+
if (!args.flags.browser) {
|
|
1759
|
+
const nativeApp = findNativeApp();
|
|
1760
|
+
if (nativeApp) {
|
|
1761
|
+
console.log('Opening A2A Callbook native app...');
|
|
1762
|
+
const result = openInBrowser(nativeApp);
|
|
1763
|
+
if (result.attempted) {
|
|
1764
|
+
return;
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
const preferred = [];
|
|
1770
|
+
if (args.flags.port || args.flags.p) preferred.push(args.flags.port || args.flags.p);
|
|
1771
|
+
if (process.env.A2A_PORT) preferred.push(process.env.A2A_PORT);
|
|
1772
|
+
if (process.env.PORT) preferred.push(process.env.PORT);
|
|
1773
|
+
|
|
1774
|
+
const port = await findLocalServerPort(preferred);
|
|
1775
|
+
if (!port) {
|
|
1776
|
+
console.log('Dashboard is not reachable on common ports.');
|
|
1777
|
+
console.log('Start the server (example):');
|
|
1778
|
+
console.log(' A2A_HOSTNAME="localhost:3001" a2a server --port 3001');
|
|
1779
|
+
console.log('Then open:');
|
|
1780
|
+
console.log(' http://127.0.0.1:3001/dashboard/');
|
|
1781
|
+
return;
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
const url = `http://127.0.0.1:${port}/dashboard/${hash}`;
|
|
1785
|
+
console.log(`Dashboard URL: ${url}`);
|
|
1786
|
+
const opened = openInBrowser(url);
|
|
1787
|
+
if (opened.attempted) {
|
|
1788
|
+
console.log(`Opening browser via: ${opened.command}`);
|
|
1789
|
+
} else {
|
|
1790
|
+
console.log('Could not auto-open browser; open the URL above manually.');
|
|
1791
|
+
}
|
|
1792
|
+
},
|
|
1793
|
+
|
|
1794
|
+
dashboard: (args) => {
|
|
1795
|
+
// Alias for gui
|
|
1796
|
+
return commands.gui(args);
|
|
1797
|
+
},
|
|
1798
|
+
|
|
1799
|
+
status: async (args) => {
|
|
1800
|
+
const url = args._[1];
|
|
1801
|
+
|
|
1802
|
+
// If a URL is provided, check that remote agent's status
|
|
1803
|
+
if (url) {
|
|
1804
|
+
const client = new A2AClient();
|
|
1805
|
+
try {
|
|
1806
|
+
const status = await client.status(url);
|
|
1807
|
+
console.log(`A2A status for ${url}:\n`);
|
|
1808
|
+
console.log(JSON.stringify(status, null, 2));
|
|
1809
|
+
} catch (err) {
|
|
1810
|
+
console.error(`❌ Failed to get status: ${err.message}`);
|
|
1811
|
+
process.exit(1);
|
|
1812
|
+
}
|
|
1813
|
+
return;
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
// No URL — show local server status
|
|
1817
|
+
const { A2AConfig } = require('../src/lib/config');
|
|
1818
|
+
const config = new A2AConfig();
|
|
1819
|
+
const onboarding = config.getOnboarding();
|
|
1820
|
+
const agent = config.getAgent();
|
|
1821
|
+
|
|
1822
|
+
console.log('A2A Local Status\n');
|
|
1823
|
+
|
|
1824
|
+
// Onboarding state
|
|
1825
|
+
const onboarded = onboarding.version === 2 && onboarding.step === 'complete';
|
|
1826
|
+
console.log(` Onboarding: ${onboarded ? '✅ Complete' : `⚠️ ${onboarding.step || 'not started'} (run: a2a quickstart)`}`);
|
|
1827
|
+
console.log(` Agent name: ${agent.name || '(not set)'}`);
|
|
1828
|
+
console.log(` Hostname: ${agent.hostname || '(not set)'}`);
|
|
1829
|
+
|
|
1830
|
+
// Check if server is running
|
|
1831
|
+
const preferred = [];
|
|
1832
|
+
if (onboarding.server_port) preferred.push(onboarding.server_port);
|
|
1833
|
+
const port = await findLocalServerPort(preferred);
|
|
1834
|
+
if (port) {
|
|
1835
|
+
console.log(` Server: ✅ Running on port ${port}`);
|
|
1836
|
+
|
|
1837
|
+
// Fetch dashboard status for more detail
|
|
1838
|
+
const http = require('http');
|
|
1839
|
+
try {
|
|
1840
|
+
const statusData = await new Promise((resolve, reject) => {
|
|
1841
|
+
const req = http.request({
|
|
1842
|
+
hostname: '127.0.0.1', port,
|
|
1843
|
+
path: '/api/a2a/dashboard/status',
|
|
1844
|
+
method: 'GET', timeout: 2000
|
|
1845
|
+
}, (res) => {
|
|
1846
|
+
let body = '';
|
|
1847
|
+
res.on('data', c => body += c);
|
|
1848
|
+
res.on('end', () => {
|
|
1849
|
+
try { resolve(JSON.parse(body)); } catch (e) { reject(e); }
|
|
1850
|
+
});
|
|
1851
|
+
});
|
|
1852
|
+
req.on('error', reject);
|
|
1853
|
+
req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); });
|
|
1854
|
+
req.end();
|
|
1855
|
+
});
|
|
1856
|
+
|
|
1857
|
+
if (statusData.agent) {
|
|
1858
|
+
if (statusData.agent.owner_name) console.log(` Owner: ${statusData.agent.owner_name}`);
|
|
1859
|
+
}
|
|
1860
|
+
if (statusData.invite_host) {
|
|
1861
|
+
const ih = typeof statusData.invite_host === 'object' ? statusData.invite_host.host : statusData.invite_host;
|
|
1862
|
+
if (ih) console.log(` Invite host: ${ih}`);
|
|
1863
|
+
}
|
|
1864
|
+
if (statusData.warnings && statusData.warnings.length) {
|
|
1865
|
+
console.log('');
|
|
1866
|
+
for (const w of statusData.warnings) {
|
|
1867
|
+
console.log(` ⚠️ ${w}`);
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
} catch (_) {
|
|
1871
|
+
// Dashboard status unavailable — that's fine, we already showed port
|
|
1872
|
+
}
|
|
1873
|
+
} else {
|
|
1874
|
+
console.log(' Server: ❌ Not running');
|
|
1875
|
+
console.log(' Start with: a2a server --port 3001');
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
console.log(`\n Tip: a2a status <invite_url> to check a remote agent`);
|
|
1879
|
+
},
|
|
1880
|
+
|
|
1881
|
+
config: (args) => {
|
|
1882
|
+
const { A2AConfig } = require('../src/lib/config');
|
|
1883
|
+
const config = new A2AConfig();
|
|
1884
|
+
|
|
1885
|
+
const hostname = args.flags.hostname || args.flags.h;
|
|
1886
|
+
const port = args.flags.port || args.flags.p;
|
|
1887
|
+
const show = args.flags.show || args.flags.s || (!hostname && !port);
|
|
1888
|
+
|
|
1889
|
+
if (show) {
|
|
1890
|
+
const agent = config.getAgent();
|
|
1891
|
+
console.log('A2A Configuration:\n');
|
|
1892
|
+
console.log(` Hostname: ${agent.hostname || '(not set)'}`);
|
|
1893
|
+
console.log(` Name: ${agent.name || '(not set)'}`);
|
|
1894
|
+
console.log(` Description: ${agent.description || '(not set)'}`);
|
|
1895
|
+
const onboarding = config.getAll().onboarding || {};
|
|
1896
|
+
console.log(` Server port: ${onboarding.server_port || '(not running)'}`);
|
|
1897
|
+
console.log(` Onboarding step: ${onboarding.step || 'not started'}`);
|
|
1898
|
+
return;
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
const updates = {};
|
|
1902
|
+
if (hostname) {
|
|
1903
|
+
// Remove port from hostname if it's :80 (default)
|
|
1904
|
+
const cleanHostname = hostname.replace(/:80$/, '');
|
|
1905
|
+
updates.hostname = cleanHostname;
|
|
1906
|
+
console.log(` Hostname updated to: ${cleanHostname}`);
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
if (Object.keys(updates).length > 0) {
|
|
1910
|
+
config.setAgent(updates);
|
|
1911
|
+
console.log(' ✅ Configuration saved.');
|
|
1912
|
+
}
|
|
1913
|
+
},
|
|
1914
|
+
|
|
1915
|
+
server: (args) => {
|
|
1916
|
+
const explicitPort = args.flags.port || args.flags.p || process.env.PORT;
|
|
1917
|
+
if (explicitPort) {
|
|
1918
|
+
process.env.PORT = explicitPort;
|
|
1919
|
+
console.log(`Starting A2A server on port ${explicitPort}...`);
|
|
1920
|
+
} else {
|
|
1921
|
+
console.log('Starting A2A server (scanning for available port)...');
|
|
1922
|
+
}
|
|
1923
|
+
require('../src/server.js');
|
|
1924
|
+
},
|
|
1925
|
+
|
|
1926
|
+
// ==========================================================================
|
|
1927
|
+
// quickstart — full onboarding flow
|
|
1928
|
+
//
|
|
1929
|
+
// OBJECTIVE: Walk an installing agent (or human) through every step needed
|
|
1930
|
+
// to get A2A Calling running, in a single pass. The postinstall hook calls
|
|
1931
|
+
// this automatically, so `npm install -g a2acalling` completes the entire
|
|
1932
|
+
// setup without any follow-up commands.
|
|
1933
|
+
//
|
|
1934
|
+
// Every prompt auto-accepts its default when there is no TTY, so the full
|
|
1935
|
+
// flow completes unattended in non-interactive environments (agents).
|
|
1936
|
+
//
|
|
1937
|
+
// STEPS:
|
|
1938
|
+
// Step 1 — Port selection: scan for an available port and bind it.
|
|
1939
|
+
// Step 2 — Hostname detection: look up the external IP so remote agents
|
|
1940
|
+
// know where to reach this instance.
|
|
1941
|
+
// Step 3 — Server start: launch the A2A server as a detached process,
|
|
1942
|
+
// confirm it's listening, save config, and print a verify hint.
|
|
1943
|
+
// Step 4 — Disclosure prompt: output a full agent-readable prompt that
|
|
1944
|
+
// instructs the agent to scan its own workspace files, extract
|
|
1945
|
+
// tiered disclosure topics, and submit them back via
|
|
1946
|
+
// `a2a quickstart --submit '<json>'`.
|
|
1947
|
+
//
|
|
1948
|
+
// The disclosure prompt does NOT pre-scan files itself — it tells the agent
|
|
1949
|
+
// which files to look for (USER.md, SOUL.md, etc.) and lets the agent read
|
|
1950
|
+
// them with its own tools. This is intentional: the installer runs in a
|
|
1951
|
+
// subprocess where it has no access to the agent's file-reading capabilities.
|
|
1952
|
+
// ==========================================================================
|
|
1953
|
+
quickstart: async (args) => {
|
|
1954
|
+
const { A2AConfig } = require('../src/lib/config');
|
|
1955
|
+
const { isPortListening } = require('../src/lib/port-scanner');
|
|
1956
|
+
const { buildExtractionPrompt } = require('../src/lib/disclosure');
|
|
1957
|
+
const { getExternalIp } = require('../src/lib/external-ip');
|
|
1958
|
+
|
|
1959
|
+
const config = new A2AConfig();
|
|
1960
|
+
const interactive = isInteractiveShell();
|
|
1961
|
+
|
|
1962
|
+
// Handle `quickstart --submit '<json>'` — this is the agent calling back
|
|
1963
|
+
// after it has scanned its workspace and built the disclosure JSON.
|
|
1964
|
+
if (await handleDisclosureSubmit(args, 'quickstart')) {
|
|
1965
|
+
return;
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
if (args.flags.force) {
|
|
1969
|
+
config.resetOnboarding();
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
// Already onboarded — skip unless --force
|
|
1973
|
+
if (config.isOnboarded() && !args.flags.force) {
|
|
1974
|
+
console.log('\nOnboarding already complete. Use --force to re-run.\n');
|
|
1975
|
+
return;
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1978
|
+
// Resume point: if the server is already running and we're waiting for the
|
|
1979
|
+
// agent to submit disclosure topics, skip straight to the disclosure prompt.
|
|
1980
|
+
// This happens when the agent re-runs quickstart after a previous partial run.
|
|
1981
|
+
let currentStep = 'not_started';
|
|
1982
|
+
try {
|
|
1983
|
+
const cfg = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
|
|
1984
|
+
currentStep = cfg.onboarding?.step || 'not_started';
|
|
1985
|
+
} catch (e) {
|
|
1986
|
+
if (e.code !== 'ENOENT' && e.name !== 'SyntaxError') {
|
|
1987
|
+
console.error(` Warning: could not read config: ${e.message}`);
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
if (currentStep === 'awaiting_disclosure' && !args.flags.force) {
|
|
1991
|
+
console.log('\nStep 1 already complete. Server is running.\n');
|
|
1992
|
+
console.log('Step 2 of 4: Configure disclosure topics\n');
|
|
1993
|
+
console.log(buildExtractionPrompt());
|
|
1994
|
+
console.log('\n Read your workspace files, extract topics, and present to your owner for review.');
|
|
1995
|
+
console.log(" Then submit with: a2a quickstart --submit '<json>'\n");
|
|
1996
|
+
return;
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
printStepHeader('🤝 A2A Calling — First-Time Setup');
|
|
2000
|
+
|
|
2001
|
+
// Interactive: ask for confirmation. Non-interactive: auto-accepts (Y).
|
|
2002
|
+
const continueSetup = await promptYesNo('Continue with setup? [Y/n] ');
|
|
2003
|
+
if (!continueSetup) {
|
|
2004
|
+
console.log('\nSetup cancelled.\n');
|
|
2005
|
+
return;
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
// ── Step 1: Port selection ───────────────────────────────────────────
|
|
2009
|
+
// Port 80 is strongly preferred (no firewall config needed for external access).
|
|
2010
|
+
// If port 80 is available and bindable, use it. Otherwise fall back to 3001-3020.
|
|
2011
|
+
printSection('Port Configuration');
|
|
2012
|
+
const preferredPort = parsePort(args.flags.port || args.flags.p, null);
|
|
2013
|
+
const candidates = await inspectPorts(preferredPort);
|
|
2014
|
+
const availableCandidates = candidates.filter(c => c.available);
|
|
2015
|
+
|
|
2016
|
+
// Strongly prefer port 80 if available
|
|
2017
|
+
const port80Candidate = candidates.find(c => c.port === 80);
|
|
2018
|
+
const port80Available = port80Candidate && port80Candidate.available;
|
|
2019
|
+
|
|
2020
|
+
let recommendedPort;
|
|
2021
|
+
if (port80Available) {
|
|
2022
|
+
recommendedPort = 80;
|
|
2023
|
+
} else if (availableCandidates.length) {
|
|
2024
|
+
recommendedPort = availableCandidates[0].port;
|
|
2025
|
+
} else {
|
|
2026
|
+
recommendedPort = null;
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
if (!recommendedPort) {
|
|
2030
|
+
console.error(' Could not find a bindable port in the scan range.');
|
|
2031
|
+
console.error(' Re-run with --port <number> after freeing one of these ports.\n');
|
|
2032
|
+
if (interactive) {
|
|
2033
|
+
console.log(' Ports scanned:');
|
|
2034
|
+
summarizePortResults(candidates).forEach(line => console.log(` ${line}`));
|
|
2035
|
+
}
|
|
2036
|
+
process.exit(1);
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
let serverPort = recommendedPort;
|
|
2040
|
+
let proxyStrategy = false; // true if user chose reverse proxy option
|
|
2041
|
+
|
|
2042
|
+
if (port80Available) {
|
|
2043
|
+
// Port 80 available — simple confirm
|
|
2044
|
+
console.log(' Port 80 is available — using it for easiest external access.');
|
|
2045
|
+
const portPrompt = `Use port 80? [Y/n]: `;
|
|
2046
|
+
const portChoice = await promptText(portPrompt, 'y');
|
|
2047
|
+
|
|
2048
|
+
if (!interactive) {
|
|
2049
|
+
serverPort = 80;
|
|
2050
|
+
} else if (!['', 'y', 'Y', 'yes', 'YES', 'ye'].includes(String(portChoice).trim())) {
|
|
2051
|
+
if (/^(n|no|custom|c)$/i.test(String(portChoice).trim())) {
|
|
2052
|
+
let customPort = null;
|
|
2053
|
+
while (customPort === null) {
|
|
2054
|
+
const raw = await promptText('Enter a custom port number: ', String(recommendedPort));
|
|
2055
|
+
const parsed = parsePort(raw, null);
|
|
2056
|
+
if (!parsed) {
|
|
2057
|
+
console.log(' Invalid port. Enter a value between 1 and 65535.');
|
|
2058
|
+
continue;
|
|
2059
|
+
}
|
|
2060
|
+
const checked = await (async () => {
|
|
2061
|
+
const scan = await inspectPorts(parsed);
|
|
2062
|
+
return scan[0];
|
|
2063
|
+
})();
|
|
2064
|
+
if (!checked.available) {
|
|
2065
|
+
console.log(` Port ${parsed} is unavailable (${checked.code || 'in use'}).`);
|
|
2066
|
+
continue;
|
|
2067
|
+
}
|
|
2068
|
+
customPort = parsed;
|
|
2069
|
+
}
|
|
2070
|
+
serverPort = customPort;
|
|
2071
|
+
} else {
|
|
2072
|
+
const parsed = parsePort(portChoice, null);
|
|
2073
|
+
if (parsed) {
|
|
2074
|
+
const checked = await (async () => {
|
|
2075
|
+
const scan = await inspectPorts(parsed);
|
|
2076
|
+
return scan[0];
|
|
2077
|
+
})();
|
|
2078
|
+
if (!checked.available) {
|
|
2079
|
+
console.log(` Port ${parsed} is unavailable (${checked.code || 'in use'}).`);
|
|
2080
|
+
} else {
|
|
2081
|
+
serverPort = parsed;
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
} else {
|
|
2087
|
+
// Port 80 NOT available — show the fallback strategy prompt
|
|
2088
|
+
const fallback = await promptPortFallbackStrategy(recommendedPort, interactive);
|
|
2089
|
+
|
|
2090
|
+
if (fallback.strategy === 'kill') {
|
|
2091
|
+
const confirmKill = await promptYesNo(` Kill ${(fallback.processInfo && fallback.processInfo.name) || 'process'} (PID ${(fallback.processInfo && fallback.processInfo.pid) || '?'})? (y/N) `);
|
|
2092
|
+
if (confirmKill) {
|
|
2093
|
+
const killed = await killPortProcess(fallback.processInfo);
|
|
2094
|
+
if (killed) {
|
|
2095
|
+
serverPort = 80;
|
|
2096
|
+
} else {
|
|
2097
|
+
console.log(`\n Falling back to port ${recommendedPort}.`);
|
|
2098
|
+
console.log(' You can set up a reverse proxy after setup completes.\n');
|
|
2099
|
+
serverPort = recommendedPort;
|
|
2100
|
+
}
|
|
2101
|
+
} else {
|
|
2102
|
+
console.log(` Skipped. Using port ${recommendedPort}.\n`);
|
|
2103
|
+
serverPort = recommendedPort;
|
|
2104
|
+
}
|
|
2105
|
+
} else if (fallback.strategy === 'proxy') {
|
|
2106
|
+
serverPort = fallback.port;
|
|
2107
|
+
proxyStrategy = true;
|
|
2108
|
+
} else {
|
|
2109
|
+
serverPort = fallback.port;
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2113
|
+
// ── Step 2: Hostname detection ───────────────────────────────────────
|
|
2114
|
+
// Look up the machine's external IP so invite URLs point to a routable
|
|
2115
|
+
// address. Non-interactive: auto-uses the detected IP. Interactive: lets
|
|
2116
|
+
// the user choose IP, domain, or skip.
|
|
2117
|
+
printSection('Hostname Configuration');
|
|
2118
|
+
const ipResult = await getExternalIp();
|
|
2119
|
+
const externalIp = ipResult.ip || null;
|
|
2120
|
+
let publicHost = `localhost:${serverPort}`;
|
|
2121
|
+
|
|
2122
|
+
if (externalIp) {
|
|
2123
|
+
const detectedHost = serverPort === 80 ? externalIp : `${externalIp}:${serverPort}`;
|
|
2124
|
+
console.log(` Detected external IP: ${detectedHost}`);
|
|
2125
|
+
if (interactive) {
|
|
2126
|
+
const hostChoiceRaw = await promptText(
|
|
2127
|
+
'How should other agents reach you?\n'
|
|
2128
|
+
+ ' 1. Use IP directly\n'
|
|
2129
|
+
+ ' 2. Enter a domain name\n'
|
|
2130
|
+
+ ' 3. Skip (configure later)\n'
|
|
2131
|
+
+ 'Choice [1/2/3]: ',
|
|
2132
|
+
'1'
|
|
2133
|
+
);
|
|
2134
|
+
const hostChoice = String(hostChoiceRaw || '').trim();
|
|
2135
|
+
if (hostChoice === '2') {
|
|
2136
|
+
const manualHost = await promptText('Enter your public hostname: ', '');
|
|
2137
|
+
if (manualHost) publicHost = String(manualHost).trim();
|
|
2138
|
+
} else if (hostChoice === '3') {
|
|
2139
|
+
publicHost = process.env.A2A_HOSTNAME || `localhost:${serverPort}`;
|
|
2140
|
+
} else {
|
|
2141
|
+
publicHost = detectedHost;
|
|
2142
|
+
}
|
|
2143
|
+
} else {
|
|
2144
|
+
publicHost = detectedHost;
|
|
2145
|
+
}
|
|
2146
|
+
} else if (interactive) {
|
|
2147
|
+
const hostChoiceRaw = await promptText(
|
|
2148
|
+
'External IP unavailable.\nHow should other agents reach you?\n'
|
|
2149
|
+
+ ' 1. Enter a domain name\n'
|
|
2150
|
+
+ ' 2. Skip (use localhost)\n'
|
|
2151
|
+
+ 'Choice [1/2]: ',
|
|
2152
|
+
'2'
|
|
2153
|
+
);
|
|
2154
|
+
const hostChoice = String(hostChoiceRaw || '').trim();
|
|
2155
|
+
if (hostChoice === '1') {
|
|
2156
|
+
const manualHost = await promptText('Enter your public hostname: ', '');
|
|
2157
|
+
if (manualHost) publicHost = String(manualHost).trim();
|
|
2158
|
+
}
|
|
2159
|
+
} else if (ipResult.error) {
|
|
2160
|
+
console.log(` External IP lookup failed: ${ipResult.error}`);
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2163
|
+
// ── Step 3: Server start ─────────────────────────────────────────────
|
|
2164
|
+
// Launch the A2A Express server as a detached background process, wait
|
|
2165
|
+
// for it to bind, then save the config with the server PID and port.
|
|
2166
|
+
// Non-interactive: auto-starts. Interactive: asks for confirmation.
|
|
2167
|
+
// Also prints a one-line networking hint (reverse proxy or firewall)
|
|
2168
|
+
// and a curl command the agent can use to verify external reachability.
|
|
2169
|
+
printSection('Starting Server');
|
|
2170
|
+
console.log(' Configuration summary:');
|
|
2171
|
+
console.log(` Port: ${serverPort}`);
|
|
2172
|
+
console.log(` Public host: ${publicHost}`);
|
|
2173
|
+
|
|
2174
|
+
const startServer = await promptYesNo('Start the A2A server now? [Y/n] ');
|
|
2175
|
+
if (!startServer) {
|
|
2176
|
+
console.log('\nServer not started. Run with:\n a2a server --port <port> --hostname <host>\n');
|
|
2177
|
+
return;
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
// Pre-start cleanup: kill any existing a2a server from a previous run
|
|
2181
|
+
try {
|
|
2182
|
+
const { killExistingServer } = require('../src/lib/pid-file');
|
|
2183
|
+
const cleanup = killExistingServer();
|
|
2184
|
+
if (cleanup.killed) {
|
|
2185
|
+
console.log(` Stopped previous server (PID ${cleanup.pid}).`);
|
|
2186
|
+
// Brief pause to let the port free up
|
|
2187
|
+
await new Promise(r => setTimeout(r, 500));
|
|
2188
|
+
}
|
|
2189
|
+
} catch (e) {
|
|
2190
|
+
// Best effort — continue with startup
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
const isAlreadyListening = await isPortListening(serverPort, '127.0.0.1', { timeoutMs: 250 });
|
|
2194
|
+
let serverPid = null;
|
|
2195
|
+
if (!isAlreadyListening.listening) {
|
|
2196
|
+
const serverScript = path.join(__dirname, '../src/server.js');
|
|
2197
|
+
const child = spawn(process.execPath, [serverScript], {
|
|
2198
|
+
env: { ...process.env, PORT: String(serverPort) },
|
|
2199
|
+
detached: true,
|
|
2200
|
+
stdio: 'ignore'
|
|
2201
|
+
});
|
|
2202
|
+
serverPid = child.pid;
|
|
2203
|
+
child.unref();
|
|
2204
|
+
} else {
|
|
2205
|
+
console.log(' Existing server detected on this port.');
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2208
|
+
async function waitForServer(port) {
|
|
2209
|
+
for (let i = 0; i < 18; i++) {
|
|
2210
|
+
const listening = await isPortListening(port, '127.0.0.1', { timeoutMs: 250 });
|
|
2211
|
+
if (listening.listening) return true;
|
|
2212
|
+
await new Promise(r => setTimeout(r, 250));
|
|
2213
|
+
}
|
|
2214
|
+
return false;
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
const serverUp = await waitForServer(serverPort);
|
|
2218
|
+
if (!serverUp) {
|
|
2219
|
+
console.log(' Server failed to start. Check logs and retry:');
|
|
2220
|
+
console.log(` PORT=${serverPort} node ${path.join(__dirname, '../src/server.js')}`);
|
|
2221
|
+
process.exit(1);
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
if (serverPid) {
|
|
2225
|
+
console.log(' Server started.');
|
|
2226
|
+
const existingPids = (config.getOnboarding().server_pids || []).filter(p => {
|
|
2227
|
+
try { process.kill(p, 0); return true; } catch (e) { return false; }
|
|
2228
|
+
});
|
|
2229
|
+
if (!existingPids.includes(serverPid)) existingPids.push(serverPid);
|
|
2230
|
+
config.setOnboarding({
|
|
2231
|
+
server_pid: serverPid,
|
|
2232
|
+
server_pids: existingPids,
|
|
2233
|
+
server_port: serverPort
|
|
2234
|
+
});
|
|
2235
|
+
} else {
|
|
2236
|
+
console.log(' Using existing server.');
|
|
2237
|
+
// Persist detected server port even when reusing an already-running process.
|
|
2238
|
+
// Native app discovery depends on onboarding.server_port as a primary hint.
|
|
2239
|
+
config.setOnboarding({ server_port: serverPort });
|
|
2240
|
+
}
|
|
2241
|
+
console.log(' ✅ A2A server is running');
|
|
2242
|
+
|
|
2243
|
+
if (externalIp) {
|
|
2244
|
+
if (serverPort === 80) {
|
|
2245
|
+
// Port 80 — optimal setup, no extra config needed
|
|
2246
|
+
console.log(`\n ✅ Running on port 80 — external agents can reach you directly.`);
|
|
2247
|
+
console.log(` Invite hostname: ${externalIp}`);
|
|
2248
|
+
publicHost = externalIp;
|
|
2249
|
+
} else if (proxyStrategy) {
|
|
2250
|
+
// User chose to set up a reverse proxy
|
|
2251
|
+
const proxy = generateProxyConfig(serverPort);
|
|
2252
|
+
|
|
2253
|
+
console.log(`\n ━━━ Reverse Proxy Configuration ━━━`);
|
|
2254
|
+
console.log(`\n A2A server running on port ${serverPort}. Configure your web server`);
|
|
2255
|
+
console.log(` to proxy port 80 → ${serverPort} so invite URLs work without a port number.\n`);
|
|
2256
|
+
|
|
2257
|
+
if (proxy.hasNginx) {
|
|
2258
|
+
console.log(' ┌─────────────────────────────────────────────────────────────────┐');
|
|
2259
|
+
console.log(' │ nginx — add inside your server {} block │');
|
|
2260
|
+
console.log(' │ File: /etc/nginx/sites-available/default │');
|
|
2261
|
+
console.log(' └─────────────────────────────────────────────────────────────────┘');
|
|
2262
|
+
console.log('');
|
|
2263
|
+
proxy.nginxConfig.split('\n').forEach(line => console.log(` ${line}`));
|
|
2264
|
+
console.log('');
|
|
2265
|
+
console.log(' To apply:');
|
|
2266
|
+
console.log(' 1. sudo nano /etc/nginx/sites-available/default');
|
|
2267
|
+
console.log(' 2. Add the config above inside your server { } block');
|
|
2268
|
+
console.log(' 3. sudo nginx -t');
|
|
2269
|
+
console.log(' 4. sudo systemctl reload nginx');
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
if (proxy.hasCaddy) {
|
|
2273
|
+
console.log('');
|
|
2274
|
+
console.log(' ┌─────────────────────────────────────────────────────────────────┐');
|
|
2275
|
+
console.log(' │ Caddy config │');
|
|
2276
|
+
console.log(' └─────────────────────────────────────────────────────────────────┘');
|
|
2277
|
+
console.log('');
|
|
2278
|
+
proxy.caddyConfig.split('\n').forEach(line => console.log(` ${line}`));
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
if (!proxy.hasNginx && !proxy.hasCaddy) {
|
|
2282
|
+
console.log(' No nginx or Caddy detected. Install one:');
|
|
2283
|
+
console.log(' sudo apt install nginx # Debian/Ubuntu');
|
|
2284
|
+
console.log(' sudo yum install nginx # RHEL/CentOS');
|
|
2285
|
+
console.log('');
|
|
2286
|
+
console.log(' Then add this proxy config:');
|
|
2287
|
+
proxy.nginxConfig.split('\n').forEach(line => console.log(` ${line}`));
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2290
|
+
// With reverse proxy, invite URLs use port 80 (no port in URL)
|
|
2291
|
+
console.log(`\n After applying, invite hostname will be: ${externalIp} (no port needed)`);
|
|
2292
|
+
publicHost = externalIp;
|
|
2293
|
+
} else {
|
|
2294
|
+
// User chose 'continue' on non-standard port — brief reminder
|
|
2295
|
+
console.log(`\n ⚠ Running on port ${serverPort} (non-standard).`);
|
|
2296
|
+
console.log(` Invite hostname: ${publicHost}`);
|
|
2297
|
+
console.log('');
|
|
2298
|
+
console.log(' ⚠️ Remote agents using your invite URL will try port 80 by default.');
|
|
2299
|
+
console.log(' Without a reverse proxy, inbound calls on port 80 will fail silently.');
|
|
2300
|
+
console.log(`\n To set up a reverse proxy later:`);
|
|
2301
|
+
console.log(` a2a config --hostname ${externalIp}`);
|
|
2302
|
+
console.log(` Then configure nginx/caddy to proxy port 80 → ${serverPort}.`);
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
const verifyUrl = `http://${publicHost}/api/a2a/ping`;
|
|
2306
|
+
console.log(`\n Verify: curl -s ${verifyUrl}`);
|
|
2307
|
+
|
|
2308
|
+
// Fix 6: Actually run the connectivity check
|
|
2309
|
+
const http = require('http');
|
|
2310
|
+
const verifyOk = await new Promise(resolve => {
|
|
2311
|
+
const req = http.request({
|
|
2312
|
+
hostname: '127.0.0.1',
|
|
2313
|
+
port: serverPort,
|
|
2314
|
+
path: '/api/a2a/ping',
|
|
2315
|
+
method: 'GET',
|
|
2316
|
+
timeout: 2000
|
|
2317
|
+
}, (res) => {
|
|
2318
|
+
res.resume();
|
|
2319
|
+
resolve(res.statusCode === 200);
|
|
2320
|
+
});
|
|
2321
|
+
req.on('error', () => resolve(false));
|
|
2322
|
+
req.on('timeout', () => { req.destroy(); resolve(false); });
|
|
2323
|
+
req.end();
|
|
2324
|
+
});
|
|
2325
|
+
|
|
2326
|
+
if (verifyOk) {
|
|
2327
|
+
console.log(' ✅ Local connectivity verified');
|
|
2328
|
+
} else {
|
|
2329
|
+
console.log(' ⚠️ Local server check failed — server may still be starting');
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
// Fix 7: Surface invite-host warnings during quickstart
|
|
2333
|
+
try {
|
|
2334
|
+
const { resolveInviteHost } = require('../src/lib/invite-host');
|
|
2335
|
+
const resolved = await resolveInviteHost({
|
|
2336
|
+
hostname: publicHost,
|
|
2337
|
+
port: serverPort
|
|
2338
|
+
});
|
|
2339
|
+
if (resolved.warnings && resolved.warnings.length) {
|
|
2340
|
+
console.log('\n ━━━ Network Warnings ━━━');
|
|
2341
|
+
for (const w of resolved.warnings) {
|
|
2342
|
+
console.warn(` ⚠️ ${w}`);
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
} catch (_) {}
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
// A2A-52: Generate Ed25519 keypair for cryptographic identity (skip if already exists)
|
|
2349
|
+
const existingKeypair = config.getKeypair();
|
|
2350
|
+
if (!existingKeypair) {
|
|
2351
|
+
const { generateKeypair, fingerprint: fpFunc } = require('../src/lib/crypto');
|
|
2352
|
+
const keypair = generateKeypair();
|
|
2353
|
+
config.setKeypair(keypair.privateKey, keypair.publicKey);
|
|
2354
|
+
const fp = fpFunc(keypair.publicKey);
|
|
2355
|
+
console.log(`\n 🔑 Ed25519 identity generated`);
|
|
2356
|
+
console.log(` Fingerprint: ${fp}`);
|
|
2357
|
+
} else {
|
|
2358
|
+
const { fingerprint: fpFunc } = require('../src/lib/crypto');
|
|
2359
|
+
console.log(`\n 🔑 Ed25519 identity exists (not overwritten)`);
|
|
2360
|
+
console.log(` Fingerprint: ${fpFunc(existingKeypair.publicKey)}`);
|
|
2361
|
+
}
|
|
2362
|
+
|
|
2363
|
+
// Save server config and advance onboarding state to awaiting_disclosure.
|
|
2364
|
+
config.setAgent({ hostname: publicHost });
|
|
2365
|
+
config.setOnboarding({ step: 'awaiting_disclosure' });
|
|
2366
|
+
|
|
2367
|
+
// ── Step 4: Disclosure prompt ────────────────────────────────────────
|
|
2368
|
+
// Output a full agent-readable prompt that tells the agent to:
|
|
2369
|
+
// 1. Scan its own workspace for USER.md, SOUL.md, etc.
|
|
2370
|
+
// 2. Extract tiered disclosure topics from those files
|
|
2371
|
+
// 3. Build the required JSON structure
|
|
2372
|
+
// 4. Submit it back via `a2a quickstart --submit '<json>'`
|
|
2373
|
+
//
|
|
2374
|
+
// IMPORTANT: We do NOT pre-scan files here. The installer runs in a
|
|
2375
|
+
// subprocess and has no access to the agent's file-reading tools. The
|
|
2376
|
+
// prompt lists which files to look for and the agent reads them itself.
|
|
2377
|
+
printSection('Disclosure Topic Extraction');
|
|
2378
|
+
console.log('Step 2 of 4: Configure disclosure topics\n');
|
|
2379
|
+
console.log(buildExtractionPrompt());
|
|
2380
|
+
console.log('\n Read your workspace files, extract topics, and present to your owner for review.');
|
|
2381
|
+
console.log(" Then submit with: a2a quickstart --submit '<json>'\n");
|
|
2382
|
+
},
|
|
2383
|
+
|
|
2384
|
+
|
|
2385
|
+
install: () => {
|
|
2386
|
+
require('../scripts/install-openclaw.js');
|
|
2387
|
+
},
|
|
2388
|
+
|
|
2389
|
+
setup: () => {
|
|
2390
|
+
require('../scripts/install-openclaw.js');
|
|
2391
|
+
},
|
|
2392
|
+
|
|
2393
|
+
app: (args) => {
|
|
2394
|
+
const action = String(args._[1] || 'status').trim().toLowerCase();
|
|
2395
|
+
const force = Boolean(args.flags.force || args.flags.f);
|
|
2396
|
+
const quiet = Boolean(args.flags.quiet || args.flags.q);
|
|
2397
|
+
const pkgVersion = require('../package.json').version;
|
|
2398
|
+
|
|
2399
|
+
if (action === 'status') {
|
|
2400
|
+
const installedAppPath = findNativeApp();
|
|
2401
|
+
const preferredPath = getNativeAppPaths().appPath;
|
|
2402
|
+
const version = installedAppPath ? parseInstalledNativeAppVersion(installedAppPath) : null;
|
|
2403
|
+
console.log('A2A Native App Status\n');
|
|
2404
|
+
console.log(` Platform: ${os.platform()}`);
|
|
2405
|
+
console.log(` CLI version: ${pkgVersion}`);
|
|
2406
|
+
if (os.platform() !== 'darwin') {
|
|
2407
|
+
console.log(' Native app: Not supported on this platform');
|
|
2408
|
+
return;
|
|
2409
|
+
}
|
|
2410
|
+
console.log(` Installed: ${installedAppPath ? 'yes' : 'no'}`);
|
|
2411
|
+
console.log(` App path: ${installedAppPath || preferredPath}`);
|
|
2412
|
+
console.log(` App version: ${version || '(unknown)'}`);
|
|
2413
|
+
if (!installedAppPath) {
|
|
2414
|
+
console.log('\nInstall with: a2a app install');
|
|
2415
|
+
}
|
|
2416
|
+
return;
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
if (action === 'install') {
|
|
2420
|
+
if (os.platform() === 'darwin' && !force && !isOnboarded()) {
|
|
2421
|
+
console.error('Onboarding not complete. Run `a2a quickstart` first, then install the app.');
|
|
2422
|
+
console.error('Use `a2a app install --force` to bypass this check.');
|
|
2423
|
+
process.exit(1);
|
|
2424
|
+
}
|
|
2425
|
+
const result = installNativeMacApp({ force, quiet });
|
|
2426
|
+
if (result.skipped === 'not_macos') {
|
|
2427
|
+
console.error('Native app install is only available on macOS.');
|
|
2428
|
+
process.exit(1);
|
|
2429
|
+
}
|
|
2430
|
+
if (!result.success) {
|
|
2431
|
+
console.error(`Native app install failed: ${result.error || 'unknown error'}`);
|
|
2432
|
+
process.exit(1);
|
|
2433
|
+
}
|
|
2434
|
+
if (result.reason === 'already_current') {
|
|
2435
|
+
console.log(`Native app already installed at current version (${result.version}).`);
|
|
2436
|
+
console.log(`Path: ${result.appPath}`);
|
|
2437
|
+
return;
|
|
2438
|
+
}
|
|
2439
|
+
console.log(`Native app installed (v${result.version}).`);
|
|
2440
|
+
console.log(`Path: ${result.appPath}`);
|
|
2441
|
+
return;
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
if (action === 'uninstall') {
|
|
2445
|
+
const result = uninstallNativeMacApp();
|
|
2446
|
+
if (result.skipped === 'not_macos') {
|
|
2447
|
+
console.error('Native app uninstall is only available on macOS.');
|
|
2448
|
+
process.exit(1);
|
|
2449
|
+
}
|
|
2450
|
+
if (!result.success) {
|
|
2451
|
+
console.error(`Native app uninstall failed: ${result.error || 'unknown error'}`);
|
|
2452
|
+
process.exit(1);
|
|
2453
|
+
}
|
|
2454
|
+
if (!result.removed) {
|
|
2455
|
+
console.log('Native app is not installed.');
|
|
2456
|
+
return;
|
|
2457
|
+
}
|
|
2458
|
+
console.log(`Native app removed: ${result.appPath}`);
|
|
2459
|
+
return;
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
console.error('Usage: a2a app <status|install|uninstall> [--force] [--quiet]');
|
|
2463
|
+
process.exit(1);
|
|
2464
|
+
},
|
|
2465
|
+
|
|
2466
|
+
uninstall: async (args) => {
|
|
2467
|
+
const fs = require('fs');
|
|
2468
|
+
const path = require('path');
|
|
2469
|
+
const { spawnSync } = require('child_process');
|
|
2470
|
+
|
|
2471
|
+
const keepConfig = Boolean(args.flags['keep-config'] || args.flags.keepConfig);
|
|
2472
|
+
const force = Boolean(args.flags.force || args.flags.f);
|
|
2473
|
+
|
|
2474
|
+
const configDir = process.env.A2A_CONFIG_DIR ||
|
|
2475
|
+
process.env.OPENCLAW_CONFIG_DIR ||
|
|
2476
|
+
path.join(process.env.HOME || '/tmp', '.config', 'openclaw');
|
|
2477
|
+
|
|
2478
|
+
const configFile = path.join(configDir, 'a2a-config.json');
|
|
2479
|
+
const disclosureFile = path.join(configDir, 'a2a-disclosure.json');
|
|
2480
|
+
const tokensFile = path.join(configDir, 'a2a-tokens.json');
|
|
2481
|
+
const tokenStoreFile = path.join(configDir, 'a2a.json');
|
|
2482
|
+
const externalIpFile = path.join(configDir, 'a2a-external-ip.json');
|
|
2483
|
+
const dbFile = path.join(configDir, 'a2a-conversations.db');
|
|
2484
|
+
const logsDbFile = path.join(configDir, 'a2a-logs.db');
|
|
2485
|
+
const callbookDbFile = path.join(configDir, 'a2a-callbook.db');
|
|
2486
|
+
|
|
2487
|
+
console.log(`\n🗑️ A2A Uninstall`);
|
|
2488
|
+
console.log('─────────────────\n');
|
|
2489
|
+
|
|
2490
|
+
if (!keepConfig && !force) {
|
|
2491
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
2492
|
+
console.error('Refusing to prompt without a TTY. Re-run with --force to confirm uninstall.');
|
|
2493
|
+
process.exit(1);
|
|
2494
|
+
}
|
|
2495
|
+
|
|
2496
|
+
const existing = [configFile, disclosureFile, tokensFile, tokenStoreFile, externalIpFile, dbFile, logsDbFile, callbookDbFile].filter(f => fs.existsSync(f));
|
|
2497
|
+
const list = existing.length ? existing.map(f => ` - ${f}`).join('\n') : ' (no local config/database files found)';
|
|
2498
|
+
const ok = await promptYesNo(
|
|
2499
|
+
`This will stop the pm2 process "a2a" and delete:\n${list}\nProceed? (y/N) `
|
|
2500
|
+
);
|
|
2501
|
+
if (!ok) {
|
|
2502
|
+
console.log('\nCancelled.\n');
|
|
2503
|
+
return;
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
|
|
2507
|
+
function pm2Exists() {
|
|
2508
|
+
const res = spawnSync('pm2', ['--version'], { stdio: 'ignore', timeout: 4000 });
|
|
2509
|
+
if (res.error && res.error.code === 'ENOENT') return false;
|
|
2510
|
+
return res.status === 0;
|
|
2511
|
+
}
|
|
2512
|
+
|
|
2513
|
+
function pm2HasProcess(name) {
|
|
2514
|
+
const res = spawnSync('pm2', ['describe', name], { encoding: 'utf8', timeout: 6000 });
|
|
2515
|
+
if (res.error && res.error.code === 'ENOENT') return false;
|
|
2516
|
+
return res.status === 0;
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
function pm2StopAndDelete(name) {
|
|
2520
|
+
if (!pm2Exists()) return { ok: true, skipped: true };
|
|
2521
|
+
if (!pm2HasProcess(name)) return { ok: true, skipped: true };
|
|
2522
|
+
|
|
2523
|
+
const stop = spawnSync('pm2', ['stop', name], { encoding: 'utf8', timeout: 8000 });
|
|
2524
|
+
if (stop.status !== 0) {
|
|
2525
|
+
const msg = (stop.stderr || stop.stdout || '').trim();
|
|
2526
|
+
return { ok: false, error: msg || 'pm2 stop failed' };
|
|
2527
|
+
}
|
|
2528
|
+
|
|
2529
|
+
const del = spawnSync('pm2', ['delete', name], { encoding: 'utf8', timeout: 8000 });
|
|
2530
|
+
if (del.status !== 0) {
|
|
2531
|
+
const msg = (del.stderr || del.stdout || '').trim();
|
|
2532
|
+
return { ok: false, error: msg || 'pm2 delete failed' };
|
|
2533
|
+
}
|
|
2534
|
+
|
|
2535
|
+
return { ok: true };
|
|
2536
|
+
}
|
|
2537
|
+
|
|
2538
|
+
function rmFileSafe(filePath) {
|
|
2539
|
+
try {
|
|
2540
|
+
fs.rmSync(filePath, { force: true });
|
|
2541
|
+
return { ok: true };
|
|
2542
|
+
} catch (err) {
|
|
2543
|
+
return { ok: false, error: err.message };
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2547
|
+
// Check if a TCP port is currently occupied (async, fast)
|
|
2548
|
+
function isPortOccupied(port) {
|
|
2549
|
+
if (!port) return false;
|
|
2550
|
+
const net = require('net');
|
|
2551
|
+
return new Promise((resolve) => {
|
|
2552
|
+
const socket = net.connect({ host: '127.0.0.1', port });
|
|
2553
|
+
let settled = false;
|
|
2554
|
+
const finish = (result) => {
|
|
2555
|
+
if (settled) return;
|
|
2556
|
+
settled = true;
|
|
2557
|
+
try { socket.destroy(); } catch (e) {}
|
|
2558
|
+
resolve(result);
|
|
2559
|
+
};
|
|
2560
|
+
socket.setTimeout(500, () => finish(false));
|
|
2561
|
+
socket.once('connect', () => finish(true));
|
|
2562
|
+
socket.once('error', () => finish(false));
|
|
2563
|
+
});
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2566
|
+
// Find PID listening on a given port by parsing /proc/net/tcp6 (Linux)
|
|
2567
|
+
function findPidOnPort(port) {
|
|
2568
|
+
try {
|
|
2569
|
+
// Try fuser first (most reliable)
|
|
2570
|
+
const fuserResult = spawnSync('fuser', [`${port}/tcp`], {
|
|
2571
|
+
encoding: 'utf8', timeout: 3000, stdio: ['pipe', 'pipe', 'pipe']
|
|
2572
|
+
});
|
|
2573
|
+
if (fuserResult.status === 0 && fuserResult.stdout) {
|
|
2574
|
+
const pids = fuserResult.stdout.trim().split(/\s+/).map(Number).filter(p => p > 0);
|
|
2575
|
+
if (pids.length > 0) return pids;
|
|
2576
|
+
}
|
|
2577
|
+
} catch (e) { /* fuser not available, fall through */ }
|
|
2578
|
+
|
|
2579
|
+
try {
|
|
2580
|
+
// Fallback: parse /proc/net/tcp and /proc/net/tcp6
|
|
2581
|
+
const portHex = port.toString(16).toUpperCase().padStart(4, '0');
|
|
2582
|
+
const pids = [];
|
|
2583
|
+
for (const proto of ['/proc/net/tcp', '/proc/net/tcp6']) {
|
|
2584
|
+
let content;
|
|
2585
|
+
try { content = fs.readFileSync(proto, 'utf8'); } catch (e) { continue; }
|
|
2586
|
+
const lines = content.split('\n');
|
|
2587
|
+
for (const line of lines) {
|
|
2588
|
+
const parts = line.trim().split(/\s+/);
|
|
2589
|
+
if (parts.length < 10) continue;
|
|
2590
|
+
const localAddr = parts[1]; // e.g., 00000000:1F90
|
|
2591
|
+
const localPort = localAddr.split(':')[1];
|
|
2592
|
+
if (localPort && localPort.toUpperCase() === portHex) {
|
|
2593
|
+
const inode = parts[9];
|
|
2594
|
+
// Search /proc/*/fd/* for socket inodes
|
|
2595
|
+
try {
|
|
2596
|
+
const procDirs = fs.readdirSync('/proc').filter(d => /^\d+$/.test(d));
|
|
2597
|
+
for (const pid of procDirs) {
|
|
2598
|
+
try {
|
|
2599
|
+
const fdDir = `/proc/${pid}/fd`;
|
|
2600
|
+
const fds = fs.readdirSync(fdDir);
|
|
2601
|
+
for (const fd of fds) {
|
|
2602
|
+
try {
|
|
2603
|
+
const link = fs.readlinkSync(`${fdDir}/${fd}`);
|
|
2604
|
+
if (link === `socket:[${inode}]`) {
|
|
2605
|
+
pids.push(Number(pid));
|
|
2606
|
+
}
|
|
2607
|
+
} catch (e) {}
|
|
2608
|
+
}
|
|
2609
|
+
} catch (e) {}
|
|
2610
|
+
}
|
|
2611
|
+
} catch (e) {}
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
if (pids.length > 0) return [...new Set(pids)];
|
|
2616
|
+
} catch (e) { /* /proc parsing failed */ }
|
|
2617
|
+
|
|
2618
|
+
return [];
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
// Kill a specific PID with SIGTERM, wait, then SIGKILL if needed
|
|
2622
|
+
function killPidSync(pid) {
|
|
2623
|
+
try {
|
|
2624
|
+
process.kill(pid, 0); // existence check
|
|
2625
|
+
} catch (e) {
|
|
2626
|
+
return { ok: true, skipped: true };
|
|
2627
|
+
}
|
|
2628
|
+
|
|
2629
|
+
process.kill(pid, 'SIGTERM');
|
|
2630
|
+
const start = Date.now();
|
|
2631
|
+
while (Date.now() - start < 3000) {
|
|
2632
|
+
try {
|
|
2633
|
+
process.kill(pid, 0);
|
|
2634
|
+
spawnSync('sleep', ['0.1'], { timeout: 500 });
|
|
2635
|
+
} catch (e) {
|
|
2636
|
+
return { ok: true, pid };
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
// Still alive — force kill
|
|
2641
|
+
try {
|
|
2642
|
+
process.kill(pid, 'SIGKILL');
|
|
2643
|
+
// Brief wait for SIGKILL to take effect
|
|
2644
|
+
spawnSync('sleep', ['0.2'], { timeout: 500 });
|
|
2645
|
+
try {
|
|
2646
|
+
process.kill(pid, 0);
|
|
2647
|
+
return { ok: false, pid, error: `PID ${pid} survived SIGKILL` };
|
|
2648
|
+
} catch (e) {
|
|
2649
|
+
return { ok: true, pid, forced: true };
|
|
2650
|
+
}
|
|
2651
|
+
} catch (e) {
|
|
2652
|
+
return { ok: true, pid };
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
|
|
2656
|
+
// Kill server by PID from config and PID file (detached process started by quickstart)
|
|
2657
|
+
// Then verify the port is actually freed; if not, find and kill whatever holds it.
|
|
2658
|
+
async function killServerPid() {
|
|
2659
|
+
let pid, serverPort, serverPids = [];
|
|
2660
|
+
try {
|
|
2661
|
+
const { A2AConfig } = require('../src/lib/config');
|
|
2662
|
+
const cfg = new A2AConfig();
|
|
2663
|
+
const onboarding = cfg.getOnboarding();
|
|
2664
|
+
pid = onboarding.server_pid;
|
|
2665
|
+
serverPort = onboarding.server_port;
|
|
2666
|
+
serverPids = Array.isArray(onboarding.server_pids) ? onboarding.server_pids : [];
|
|
2667
|
+
} catch (err) {
|
|
2668
|
+
// Config read failed — not fatal, continue
|
|
2669
|
+
}
|
|
2670
|
+
|
|
2671
|
+
// Step 0: Try PID file first (most reliable source)
|
|
2672
|
+
try {
|
|
2673
|
+
const { readPidFile, removePidFile } = require('../src/lib/pid-file');
|
|
2674
|
+
const filePid = readPidFile();
|
|
2675
|
+
if (filePid) {
|
|
2676
|
+
killPidSync(filePid);
|
|
2677
|
+
removePidFile();
|
|
2678
|
+
// If config PID is the same, don't double-kill
|
|
2679
|
+
if (filePid === pid) pid = null;
|
|
2680
|
+
}
|
|
2681
|
+
} catch (e) {
|
|
2682
|
+
// pid-file module load failed — continue with config PID
|
|
2683
|
+
}
|
|
2684
|
+
|
|
2685
|
+
// Step 1: Try to kill all tracked PIDs from config
|
|
2686
|
+
const allPids = new Set();
|
|
2687
|
+
if (pid) allPids.add(pid);
|
|
2688
|
+
for (const p of serverPids) {
|
|
2689
|
+
if (typeof p === 'number' && p > 0) allPids.add(p);
|
|
2690
|
+
}
|
|
2691
|
+
for (const p of allPids) {
|
|
2692
|
+
killPidSync(p);
|
|
2693
|
+
}
|
|
2694
|
+
|
|
2695
|
+
// Step 2: Verify the port is freed
|
|
2696
|
+
if (serverPort) {
|
|
2697
|
+
const stillOccupied = await isPortOccupied(serverPort);
|
|
2698
|
+
if (stillOccupied) {
|
|
2699
|
+
// Port is still held — find and kill whatever is on it
|
|
2700
|
+
const pids = findPidOnPort(serverPort);
|
|
2701
|
+
let killedAny = false;
|
|
2702
|
+
for (const p of pids) {
|
|
2703
|
+
const result = killPidSync(p);
|
|
2704
|
+
if (result.ok && !result.skipped) killedAny = true;
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2707
|
+
// Final check
|
|
2708
|
+
const stillUp = await isPortOccupied(serverPort);
|
|
2709
|
+
if (stillUp) {
|
|
2710
|
+
return { ok: false, pid, port: serverPort, error: `Port ${serverPort} is still occupied after kill attempts` };
|
|
2711
|
+
}
|
|
2712
|
+
return { ok: true, pid, port: serverPort, portKill: killedAny };
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
|
|
2716
|
+
// Clean up PID file if it still exists
|
|
2717
|
+
try {
|
|
2718
|
+
const { removePidFile } = require('../src/lib/pid-file');
|
|
2719
|
+
removePidFile();
|
|
2720
|
+
} catch (e) {}
|
|
2721
|
+
|
|
2722
|
+
return { ok: true, pid, port: serverPort, skipped: !pid && allPids.size === 0 };
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2725
|
+
process.stdout.write('Stopping server... ');
|
|
2726
|
+
const pidResult = await killServerPid();
|
|
2727
|
+
const stopped = pm2StopAndDelete('a2a');
|
|
2728
|
+
if (!pidResult.ok && !stopped.ok) {
|
|
2729
|
+
console.log('❌');
|
|
2730
|
+
console.error(` ${pidResult.error || stopped.error}`);
|
|
2731
|
+
process.exit(1);
|
|
2732
|
+
}
|
|
2733
|
+
if (!pidResult.ok) {
|
|
2734
|
+
console.log('⚠️');
|
|
2735
|
+
console.error(` Warning: ${pidResult.error}`);
|
|
2736
|
+
} else {
|
|
2737
|
+
console.log('✅');
|
|
2738
|
+
}
|
|
2739
|
+
|
|
2740
|
+
let configOk = true;
|
|
2741
|
+
let dbOk = true;
|
|
2742
|
+
|
|
2743
|
+
if (!keepConfig) {
|
|
2744
|
+
process.stdout.write('Removing config... ');
|
|
2745
|
+
const c1 = rmFileSafe(configFile);
|
|
2746
|
+
const c2 = rmFileSafe(disclosureFile);
|
|
2747
|
+
const c3 = rmFileSafe(tokensFile);
|
|
2748
|
+
const c4 = rmFileSafe(tokenStoreFile);
|
|
2749
|
+
const c5 = rmFileSafe(externalIpFile);
|
|
2750
|
+
configOk = Boolean(c1.ok && c2.ok && c3.ok && c4.ok && c5.ok);
|
|
2751
|
+
console.log(configOk ? '✅' : '❌');
|
|
2752
|
+
if (!configOk) {
|
|
2753
|
+
if (!c1.ok) console.error(` ${configFile}: ${c1.error}`);
|
|
2754
|
+
if (!c2.ok) console.error(` ${disclosureFile}: ${c2.error}`);
|
|
2755
|
+
if (!c3.ok) console.error(` ${tokensFile}: ${c3.error}`);
|
|
2756
|
+
if (!c4.ok) console.error(` ${tokenStoreFile}: ${c4.error}`);
|
|
2757
|
+
if (!c5.ok) console.error(` ${externalIpFile}: ${c5.error}`);
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
process.stdout.write('Removing database... ');
|
|
2761
|
+
const d1 = rmFileSafe(dbFile);
|
|
2762
|
+
const d2 = rmFileSafe(logsDbFile);
|
|
2763
|
+
const d3 = rmFileSafe(callbookDbFile);
|
|
2764
|
+
dbOk = Boolean(d1.ok && d2.ok && d3.ok);
|
|
2765
|
+
console.log(dbOk ? '✅' : '❌');
|
|
2766
|
+
if (!dbOk) {
|
|
2767
|
+
if (!d1.ok) console.error(` ${dbFile}: ${d1.error}`);
|
|
2768
|
+
if (!d2.ok) console.error(` ${logsDbFile}: ${d2.error}`);
|
|
2769
|
+
if (!d3.ok) console.error(` ${callbookDbFile}: ${d3.error}`);
|
|
2770
|
+
}
|
|
2771
|
+
|
|
2772
|
+
if (!configOk || !dbOk) {
|
|
2773
|
+
process.exit(1);
|
|
2774
|
+
}
|
|
2775
|
+
} else {
|
|
2776
|
+
console.log('Removing config... ⏭️');
|
|
2777
|
+
console.log('Removing database... ⏭️');
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2780
|
+
// ── Remove installed skill files using the shared cleanup module ──────
|
|
2781
|
+
//
|
|
2782
|
+
// The postinstall script writes .a2a-manifest.json listing every file it
|
|
2783
|
+
// installed. We delegate to the shared cleanupProjectFiles() function which
|
|
2784
|
+
// handles CLAUDE.md section removal, file deletion, and empty directory
|
|
2785
|
+
// cleanup. This is the same logic used by the npm preuninstall hook,
|
|
2786
|
+
// ensuring both uninstall paths behave identically.
|
|
2787
|
+
//
|
|
2788
|
+
// We check multiple candidate directories for the manifest because `a2a
|
|
2789
|
+
// uninstall` might be run from a different directory than the one where
|
|
2790
|
+
// the package was originally installed.
|
|
2791
|
+
const manifestCandidates = [
|
|
2792
|
+
process.env.INIT_CWD,
|
|
2793
|
+
process.cwd(),
|
|
2794
|
+
].filter(Boolean);
|
|
2795
|
+
// Deduplicate paths (INIT_CWD and cwd may be identical)
|
|
2796
|
+
const uniqueDirs = [...new Set(manifestCandidates.map(d => path.resolve(d)))];
|
|
2797
|
+
|
|
2798
|
+
let projectCleaned = false;
|
|
2799
|
+
for (const candidateDir of uniqueDirs) {
|
|
2800
|
+
if (fs.existsSync(path.join(candidateDir, '.a2a-manifest.json'))) {
|
|
2801
|
+
process.stdout.write('Removing installed skill files... ');
|
|
2802
|
+
try {
|
|
2803
|
+
const { cleanupProjectFiles } = require('../scripts/cleanup');
|
|
2804
|
+
const cleanResult = cleanupProjectFiles(candidateDir);
|
|
2805
|
+
const hasErrors = cleanResult.errors.length > 0;
|
|
2806
|
+
console.log(hasErrors ? '⚠️' : '✅');
|
|
2807
|
+
if (cleanResult.removed.length > 0) {
|
|
2808
|
+
for (const f of cleanResult.removed) {
|
|
2809
|
+
console.log(` - ${f}`);
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
if (cleanResult.preserved.length > 0) {
|
|
2813
|
+
for (const f of cleanResult.preserved) {
|
|
2814
|
+
console.log(` ~ ${f}`);
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
if (hasErrors) {
|
|
2818
|
+
for (const e of cleanResult.errors) {
|
|
2819
|
+
console.error(` ! ${e}`);
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
projectCleaned = true;
|
|
2823
|
+
} catch (err) {
|
|
2824
|
+
console.log('⚠️');
|
|
2825
|
+
console.error(` Cleanup error: ${err.message}`);
|
|
2826
|
+
}
|
|
2827
|
+
break; // Only clean up once — first manifest found wins
|
|
2828
|
+
}
|
|
2829
|
+
}
|
|
2830
|
+
if (!projectCleaned) {
|
|
2831
|
+
// No manifest found in any candidate directory. This is expected when
|
|
2832
|
+
// `a2a uninstall` is run after `npm uninstall` (preuninstall already cleaned).
|
|
2833
|
+
console.log('Removing installed skill files... ⏭️ (no manifest found)');
|
|
2834
|
+
}
|
|
2835
|
+
|
|
2836
|
+
// Remove native macOS app if present
|
|
2837
|
+
if (os.platform() === 'darwin') {
|
|
2838
|
+
const appCandidates = [
|
|
2839
|
+
path.join(os.homedir(), 'Applications', 'A2A Callbook.app'),
|
|
2840
|
+
'/Applications/A2A Callbook.app',
|
|
2841
|
+
];
|
|
2842
|
+
for (const appPath of appCandidates) {
|
|
2843
|
+
if (fs.existsSync(appPath)) {
|
|
2844
|
+
try {
|
|
2845
|
+
fs.rmSync(appPath, { recursive: true, force: true });
|
|
2846
|
+
console.log(`Removed ${appPath}`);
|
|
2847
|
+
} catch (err) {
|
|
2848
|
+
console.log(`Could not remove ${appPath}: ${err.message}`);
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
|
|
2854
|
+
console.log('\nTo complete removal:');
|
|
2855
|
+
console.log(' npm uninstall -g a2acalling\n');
|
|
2856
|
+
console.log(`Config preserved: ${keepConfig ? 'yes' : 'no'}`);
|
|
2857
|
+
console.log(`Location: ${configDir}`);
|
|
2858
|
+
},
|
|
2859
|
+
|
|
2860
|
+
update: async (args) => {
|
|
2861
|
+
const { execSync } = require('child_process');
|
|
2862
|
+
const path = require('path');
|
|
2863
|
+
const pkg = require('../package.json');
|
|
2864
|
+
const { A2AConfig } = require('../src/lib/config');
|
|
2865
|
+
const currentVersion = pkg.version;
|
|
2866
|
+
const checkOnly = args.flags.check || args.flags.c;
|
|
2867
|
+
const autoMode = args.flags.auto ? String(args.flags.auto).trim().toLowerCase() : null;
|
|
2868
|
+
|
|
2869
|
+
if (autoMode) {
|
|
2870
|
+
const config = new A2AConfig();
|
|
2871
|
+
if (autoMode === 'status') {
|
|
2872
|
+
const au = config.getAutoUpdate ? config.getAutoUpdate() : { enabled: true, intervalMs: 3600000, allowMajor: false };
|
|
2873
|
+
console.log('Auto-update configuration:\n');
|
|
2874
|
+
console.log(` Enabled: ${au.enabled ? 'yes' : 'no'}`);
|
|
2875
|
+
console.log(` Interval: ${Math.floor((au.intervalMs || 3600000) / 1000)}s`);
|
|
2876
|
+
console.log(` Allow major updates: ${au.allowMajor ? 'yes' : 'no'}`);
|
|
2877
|
+
console.log(` Last known good version: ${au.lastGoodVersion || '(none)'}`);
|
|
2878
|
+
return;
|
|
2879
|
+
}
|
|
2880
|
+
if (autoMode === 'on' || autoMode === 'off') {
|
|
2881
|
+
config.setAutoUpdate({ enabled: autoMode === 'on' });
|
|
2882
|
+
console.log(`Auto-update ${autoMode === 'on' ? 'enabled' : 'disabled'}.`);
|
|
2883
|
+
return;
|
|
2884
|
+
}
|
|
2885
|
+
console.error('Invalid --auto value. Use: on | off | status');
|
|
2886
|
+
process.exit(1);
|
|
2887
|
+
}
|
|
2888
|
+
|
|
2889
|
+
console.log(`\n📦 A2A Update\n${'─'.repeat(50)}\n`);
|
|
2890
|
+
console.log(` Installed: v${currentVersion}`);
|
|
2891
|
+
|
|
2892
|
+
// Detect install method
|
|
2893
|
+
const pkgRoot = path.resolve(__dirname, '..');
|
|
2894
|
+
const isGitRepo = require('fs').existsSync(path.join(pkgRoot, '.git'));
|
|
2895
|
+
|
|
2896
|
+
if (isGitRepo) {
|
|
2897
|
+
// Git clone — use git pull
|
|
2898
|
+
console.log(` Source: git (${pkgRoot})\n`);
|
|
2899
|
+
|
|
2900
|
+
if (checkOnly) {
|
|
2901
|
+
try {
|
|
2902
|
+
execSync('git fetch --quiet', { cwd: pkgRoot, stdio: 'pipe' });
|
|
2903
|
+
const behind = execSync('git rev-list HEAD..@{u} --count', { cwd: pkgRoot, encoding: 'utf8' }).trim();
|
|
2904
|
+
if (behind === '0') {
|
|
2905
|
+
console.log(' \u2705 Already up to date.\n');
|
|
2906
|
+
} else {
|
|
2907
|
+
console.log(` \u2b06\ufe0f ${behind} commit(s) behind. Run "a2a update" to pull.\n`);
|
|
2908
|
+
}
|
|
2909
|
+
} catch (e) {
|
|
2910
|
+
console.log(' \u26a0\ufe0f Could not check remote (no upstream or network error).\n');
|
|
2911
|
+
}
|
|
2912
|
+
return;
|
|
2913
|
+
}
|
|
2914
|
+
|
|
2915
|
+
console.log(' Pulling latest...');
|
|
2916
|
+
try {
|
|
2917
|
+
const output = execSync('git pull --ff-only 2>&1', { cwd: pkgRoot, encoding: 'utf8' });
|
|
2918
|
+
console.log(` ${output.trim()}\n`);
|
|
2919
|
+
} catch (e) {
|
|
2920
|
+
const stderr = e.stderr ? e.stderr.toString() : e.message;
|
|
2921
|
+
console.error(` \u274c Git pull failed: ${stderr.trim()}`);
|
|
2922
|
+
console.error(' Try: cd ' + pkgRoot + ' && git pull manually.\n');
|
|
2923
|
+
process.exit(1);
|
|
2924
|
+
}
|
|
2925
|
+
|
|
2926
|
+
// Re-install deps if package.json changed
|
|
2927
|
+
console.log(' Installing dependencies...');
|
|
2928
|
+
try {
|
|
2929
|
+
execSync('npm install --production 2>&1', { cwd: pkgRoot, encoding: 'utf8', timeout: 120000 });
|
|
2930
|
+
} catch (e) {
|
|
2931
|
+
console.warn(' \u26a0\ufe0f npm install had warnings (non-fatal).');
|
|
2932
|
+
}
|
|
2933
|
+
} else {
|
|
2934
|
+
// npm global install — use npm update
|
|
2935
|
+
console.log(' Source: npm global\n');
|
|
2936
|
+
|
|
2937
|
+
// Check latest version on npm
|
|
2938
|
+
let latestVersion;
|
|
2939
|
+
try {
|
|
2940
|
+
latestVersion = execSync('npm view a2acalling version 2>/dev/null', { encoding: 'utf8', timeout: 15000 }).trim();
|
|
2941
|
+
console.log(` Latest: v${latestVersion}`);
|
|
2942
|
+
} catch (e) {
|
|
2943
|
+
console.error(' \u274c Could not check npm registry. Check your network.\n');
|
|
2944
|
+
process.exit(1);
|
|
2945
|
+
}
|
|
2946
|
+
|
|
2947
|
+
if (latestVersion === currentVersion) {
|
|
2948
|
+
console.log('\n \u2705 Already up to date.\n');
|
|
2949
|
+
return;
|
|
2950
|
+
}
|
|
2951
|
+
|
|
2952
|
+
if (checkOnly) {
|
|
2953
|
+
console.log(`\n \u2b06\ufe0f Update available: v${currentVersion} \u2192 v${latestVersion}`);
|
|
2954
|
+
console.log(' Run "a2a update" to install.\n');
|
|
2955
|
+
return;
|
|
2956
|
+
}
|
|
2957
|
+
|
|
2958
|
+
console.log(`\n Updating v${currentVersion} \u2192 v${latestVersion}...`);
|
|
2959
|
+
try {
|
|
2960
|
+
execSync('npm install -g a2acalling@latest 2>&1', { encoding: 'utf8', timeout: 120000 });
|
|
2961
|
+
console.log(' \u2705 npm package updated.\n');
|
|
2962
|
+
} catch (e) {
|
|
2963
|
+
const stderr = e.stderr ? e.stderr.toString() : e.message;
|
|
2964
|
+
console.error(` \u274c npm update failed: ${stderr.trim()}`);
|
|
2965
|
+
console.error(' Try: npm install -g a2acalling@latest manually.\n');
|
|
2966
|
+
process.exit(1);
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
|
|
2970
|
+
// Re-run install to sync SKILL.md and config
|
|
2971
|
+
console.log(' Syncing SKILL.md and config...');
|
|
2972
|
+
try {
|
|
2973
|
+
const installScript = path.join(pkgRoot, 'scripts', 'install-openclaw.js');
|
|
2974
|
+
execSync(`node "${installScript}" install 2>&1`, { encoding: 'utf8', timeout: 30000 });
|
|
2975
|
+
} catch (e) {
|
|
2976
|
+
console.warn(' \u26a0\ufe0f Post-update install sync had warnings (non-fatal).');
|
|
2977
|
+
}
|
|
2978
|
+
|
|
2979
|
+
// Show new version
|
|
2980
|
+
try {
|
|
2981
|
+
delete require.cache[require.resolve('../package.json')];
|
|
2982
|
+
const newPkg = require('../package.json');
|
|
2983
|
+
console.log(`\n \u2705 Updated to v${newPkg.version}\n`);
|
|
2984
|
+
} catch (e) {
|
|
2985
|
+
console.log('\n \u2705 Update complete.\n');
|
|
2986
|
+
}
|
|
2987
|
+
},
|
|
2988
|
+
|
|
2989
|
+
onboard: async (args) => {
|
|
2990
|
+
if (await handleDisclosureSubmit(args, 'onboard')) {
|
|
2991
|
+
return;
|
|
2992
|
+
}
|
|
2993
|
+
|
|
2994
|
+
// ── No --submit: same as quickstart ───────────────────────
|
|
2995
|
+
return commands.quickstart(args);
|
|
2996
|
+
},
|
|
2997
|
+
|
|
2998
|
+
skills: (args) => {
|
|
2999
|
+
const { installSkills, SKILL_FILES } = require('../scripts/install-skills');
|
|
3000
|
+
const check = args.flags.check || args.flags.c;
|
|
3001
|
+
const force = args.flags.force;
|
|
3002
|
+
const targetDir = process.cwd();
|
|
3003
|
+
|
|
3004
|
+
if (check) {
|
|
3005
|
+
console.log('A2A skills for this project:\n');
|
|
3006
|
+
for (const file of SKILL_FILES) {
|
|
3007
|
+
const destPath = path.join(targetDir, file.dest);
|
|
3008
|
+
const exists = fs.existsSync(destPath);
|
|
3009
|
+
const icon = exists ? ' \u2713' : ' \u2717';
|
|
3010
|
+
console.log(`${icon} ${file.dest}${exists ? ' (installed)' : ' (not installed)'}`);
|
|
3011
|
+
}
|
|
3012
|
+
console.log(`\nRun "a2a skills" to install missing files.`);
|
|
3013
|
+
return;
|
|
3014
|
+
}
|
|
3015
|
+
|
|
3016
|
+
const result = installSkills(targetDir, { force });
|
|
3017
|
+
|
|
3018
|
+
if (result.installed.length) {
|
|
3019
|
+
console.log(`\n Installed ${result.installed.length} A2A skill file(s):\n`);
|
|
3020
|
+
result.installed.forEach(f => console.log(` + ${f}`));
|
|
3021
|
+
}
|
|
3022
|
+
if (result.skipped.length) {
|
|
3023
|
+
console.log(`\n Skipped ${result.skipped.length} unchanged file(s)`);
|
|
3024
|
+
}
|
|
3025
|
+
if (result.errors.length) {
|
|
3026
|
+
console.error(`\n Errors:`);
|
|
3027
|
+
result.errors.forEach(e => console.error(` ! ${e.file}: ${e.error}`));
|
|
3028
|
+
}
|
|
3029
|
+
|
|
3030
|
+
if (result.installed.length === 0 && result.skipped.length > 0) {
|
|
3031
|
+
console.log('\n All skills already installed. Use --force to overwrite.\n');
|
|
3032
|
+
} else if (result.installed.length > 0) {
|
|
3033
|
+
console.log('\n Skills ready. In Claude Code, type /a2a- to see available commands.');
|
|
3034
|
+
console.log(' In Codex CLI, A2A instructions are in .codex/AGENTS.md\n');
|
|
3035
|
+
}
|
|
3036
|
+
},
|
|
3037
|
+
|
|
3038
|
+
version: () => {
|
|
3039
|
+
const pkg = require('../package.json');
|
|
3040
|
+
console.log(pkg.version);
|
|
3041
|
+
},
|
|
3042
|
+
|
|
3043
|
+
help: () => {
|
|
3044
|
+
console.log(`A2A Calling - Agent-to-Agent Communication
|
|
3045
|
+
|
|
3046
|
+
Usage: a2a <command> [options]
|
|
3047
|
+
|
|
3048
|
+
Commands:
|
|
3049
|
+
create Create an A2A token
|
|
3050
|
+
--name, -n Token/agent name
|
|
3051
|
+
--owner, -o Owner name (human behind the agent)
|
|
3052
|
+
--expires, -e Expiration (1h, 1d, 7d, 30d, never)
|
|
3053
|
+
--permissions, -p Tier (public, friends, family)
|
|
3054
|
+
--topics Custom topics (comma-separated, overrides tier defaults)
|
|
3055
|
+
--tools Custom tool allowlist (comma-separated, overrides tier defaults)
|
|
3056
|
+
--notify Owner notification (all, summary, none)
|
|
3057
|
+
--max-calls Maximum invocations (default: 100)
|
|
3058
|
+
--timeout-ms Per-token Claude turn timeout in milliseconds
|
|
3059
|
+
--link, -l Auto-link to contact name
|
|
3060
|
+
|
|
3061
|
+
list List active tokens
|
|
3062
|
+
revoke <id> Revoke a token
|
|
3063
|
+
|
|
3064
|
+
Contacts:
|
|
3065
|
+
contacts List all contacts (shows permission badges)
|
|
3066
|
+
contacts add <url> Add a contact
|
|
3067
|
+
--name, -n Agent name
|
|
3068
|
+
--owner, -o Owner name
|
|
3069
|
+
--server-name Server label (optional)
|
|
3070
|
+
--notes Notes about this contact
|
|
3071
|
+
--tags Comma-separated tags
|
|
3072
|
+
--link Link to token ID you gave them
|
|
3073
|
+
contacts show <n> Show contact details + linked token
|
|
3074
|
+
contacts edit <n> Edit contact metadata
|
|
3075
|
+
--server-name Server label (optional)
|
|
3076
|
+
contacts link <n> <tok> Link a token to a contact
|
|
3077
|
+
contacts ping <n> Ping contact, update status
|
|
3078
|
+
contacts rm <n> Remove contact
|
|
3079
|
+
|
|
3080
|
+
Permission badges: 🌐 public 🔧 friends ⚡ family
|
|
3081
|
+
|
|
3082
|
+
Conversations:
|
|
3083
|
+
conversations List all conversations
|
|
3084
|
+
--contact Filter by contact
|
|
3085
|
+
--status Filter by status (active, concluded, timeout)
|
|
3086
|
+
--limit Max results (default: 20)
|
|
3087
|
+
conversations show <id> Show conversation with messages
|
|
3088
|
+
--messages Number of recent messages (default: 20)
|
|
3089
|
+
conversations end <id> End and summarize conversation
|
|
3090
|
+
|
|
3091
|
+
Calling:
|
|
3092
|
+
call <contact|url> <msg> Call a contact (multi-turn by default)
|
|
3093
|
+
--single Single-turn call (one message, one response)
|
|
3094
|
+
--min-turns N Minimum turns before close (default: 8)
|
|
3095
|
+
--max-turns N Maximum turns (default: 25)
|
|
3096
|
+
ping <url> Check if agent is reachable
|
|
3097
|
+
status <url> Get A2A status
|
|
3098
|
+
gui Open the local dashboard GUI in a browser
|
|
3099
|
+
--tab, -t Optional: contacts|calls|logs|permissions|invites
|
|
3100
|
+
app Manage native macOS app
|
|
3101
|
+
status Show native app installation status (default)
|
|
3102
|
+
install Install/update native app from GitHub release
|
|
3103
|
+
--force, -f Reinstall/bypass onboarding guard
|
|
3104
|
+
--quiet, -q Suppress download/extract output
|
|
3105
|
+
uninstall Remove native app from ~/Applications
|
|
3106
|
+
|
|
3107
|
+
Server:
|
|
3108
|
+
server Start the A2A server
|
|
3109
|
+
--port, -p Port to listen on (default: 3001)
|
|
3110
|
+
|
|
3111
|
+
quickstart Set up A2A server and start onboarding
|
|
3112
|
+
--port, -p Preferred server port (default: 80, fallback: 3001+)
|
|
3113
|
+
--submit '<json>' Submit disclosure JSON (Step 3 of onboarding)
|
|
3114
|
+
--force Reset onboarding and re-run from scratch
|
|
3115
|
+
|
|
3116
|
+
onboard Submit disclosure topics or resume quickstart
|
|
3117
|
+
--submit '<json>' Submit disclosure JSON (Step 3 of onboarding)
|
|
3118
|
+
--name Agent name for invite generation
|
|
3119
|
+
--force Re-run even if already onboarded
|
|
3120
|
+
|
|
3121
|
+
update Update A2A to latest version (npm or git pull)
|
|
3122
|
+
--check, -c Check for updates without installing
|
|
3123
|
+
--auto Manage auto-update: on|off|status
|
|
3124
|
+
|
|
3125
|
+
install Install A2A for OpenClaw
|
|
3126
|
+
setup Auto setup (gateway-aware dashboard install)
|
|
3127
|
+
uninstall Stop server and remove local config/DB
|
|
3128
|
+
--keep-config Preserve config/DB (for reinstall)
|
|
3129
|
+
--force Skip confirmation prompt
|
|
3130
|
+
skills Install Claude Code + Codex CLI skills
|
|
3131
|
+
--check, -c Show what would be installed
|
|
3132
|
+
--force Overwrite existing files
|
|
3133
|
+
version Show installed package version
|
|
3134
|
+
|
|
3135
|
+
Examples:
|
|
3136
|
+
a2a create --name "bappybot" --owner "Benjamin Pollack" --expires 7d
|
|
3137
|
+
a2a create --name "custom" --topics "chat,calendar.read,email.read"
|
|
3138
|
+
a2a create --name "research" --tools "Read,Grep,Glob,WebSearch,WebFetch"
|
|
3139
|
+
a2a contacts add a2a://host/fed_xxx --name "Alice" --owner "Alice Chen"
|
|
3140
|
+
a2a contacts link Alice tok_abc123
|
|
3141
|
+
a2a call Alice "Hello!"
|
|
3142
|
+
a2a conversations show conv_abc123
|
|
3143
|
+
a2a app status
|
|
3144
|
+
a2a app install --force
|
|
3145
|
+
a2a server --port 3001
|
|
3146
|
+
`);
|
|
3147
|
+
}
|
|
3148
|
+
};
|
|
3149
|
+
|
|
3150
|
+
// Main
|
|
3151
|
+
const args = parseArgs(process.argv);
|
|
3152
|
+
|
|
3153
|
+
// Handle --version flag before command dispatch (standard CLI convention)
|
|
3154
|
+
if (args.flags.version || args.flags.v) {
|
|
3155
|
+
commands.version();
|
|
3156
|
+
process.exit(0);
|
|
3157
|
+
}
|
|
3158
|
+
|
|
3159
|
+
const command = args._[0] || 'help';
|
|
3160
|
+
|
|
3161
|
+
if (!commands[command]) {
|
|
3162
|
+
console.error(`Unknown command: ${command}`);
|
|
3163
|
+
console.log('Run "a2a help" for usage.');
|
|
3164
|
+
process.exit(1);
|
|
3165
|
+
}
|
|
3166
|
+
|
|
3167
|
+
// If onboarding is incomplete, enforceOnboarding runs quickstart inline
|
|
3168
|
+
// (verbose, full output) and returns a Promise. Otherwise returns undefined
|
|
3169
|
+
// and we proceed to the requested command.
|
|
3170
|
+
const onboardResult = enforceOnboarding(command);
|
|
3171
|
+
if (onboardResult instanceof Promise) {
|
|
3172
|
+
onboardResult.catch(err => {
|
|
3173
|
+
console.error(err.message);
|
|
3174
|
+
process.exit(1);
|
|
3175
|
+
});
|
|
3176
|
+
} else {
|
|
3177
|
+
const result = commands[command](args);
|
|
3178
|
+
if (result instanceof Promise) {
|
|
3179
|
+
result.catch(err => {
|
|
3180
|
+
console.error(err.message);
|
|
3181
|
+
process.exit(1);
|
|
3182
|
+
});
|
|
3183
|
+
}
|
|
3184
|
+
}
|