@openparachute/agent 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/scheduled_tasks.lock +1 -0
- package/.claude/settings.json +5 -0
- package/.claude/skills/add-atomic-chat-tool/SKILL.md +243 -0
- package/.claude/skills/add-atomic-chat-tool/atomic-chat-mcp-stdio.ts +229 -0
- package/.claude/skills/add-codex/SKILL.md +161 -0
- package/.claude/skills/add-dashboard/SKILL.md +138 -0
- package/.claude/skills/add-dashboard/resources/dashboard-pusher.ts +495 -0
- package/.claude/skills/add-emacs/SKILL.md +296 -0
- package/.claude/skills/add-gcal-tool/SKILL.md +210 -0
- package/.claude/skills/add-gchat/REMOVE.md +6 -0
- package/.claude/skills/add-gchat/SKILL.md +92 -0
- package/.claude/skills/add-gchat/VERIFY.md +3 -0
- package/.claude/skills/add-github/REMOVE.md +6 -0
- package/.claude/skills/add-github/SKILL.md +148 -0
- package/.claude/skills/add-github/VERIFY.md +3 -0
- package/.claude/skills/add-gmail-tool/SKILL.md +229 -0
- package/.claude/skills/add-imessage/REMOVE.md +6 -0
- package/.claude/skills/add-imessage/SKILL.md +113 -0
- package/.claude/skills/add-imessage/VERIFY.md +3 -0
- package/.claude/skills/add-karpathy-llm-wiki/SKILL.md +110 -0
- package/.claude/skills/add-karpathy-llm-wiki/llm-wiki.md +75 -0
- package/.claude/skills/add-linear/REMOVE.md +6 -0
- package/.claude/skills/add-linear/SKILL.md +168 -0
- package/.claude/skills/add-linear/VERIFY.md +3 -0
- package/.claude/skills/add-macos-statusbar/SKILL.md +133 -0
- package/.claude/skills/add-macos-statusbar/add/src/statusbar.swift +147 -0
- package/.claude/skills/add-matrix/REMOVE.md +6 -0
- package/.claude/skills/add-matrix/SKILL.md +148 -0
- package/.claude/skills/add-matrix/VERIFY.md +3 -0
- package/.claude/skills/add-ollama-provider/SKILL.md +179 -0
- package/.claude/skills/add-ollama-tool/SKILL.md +193 -0
- package/.claude/skills/add-opencode/SKILL.md +229 -0
- package/.claude/skills/add-parallel/SKILL.md +290 -0
- package/.claude/skills/add-resend/REMOVE.md +6 -0
- package/.claude/skills/add-resend/SKILL.md +93 -0
- package/.claude/skills/add-resend/VERIFY.md +3 -0
- package/.claude/skills/add-signal/REMOVE.md +13 -0
- package/.claude/skills/add-signal/SKILL.md +318 -0
- package/.claude/skills/add-signal/VERIFY.md +5 -0
- package/.claude/skills/add-slack/REMOVE.md +6 -0
- package/.claude/skills/add-slack/SKILL.md +112 -0
- package/.claude/skills/add-slack/VERIFY.md +3 -0
- package/.claude/skills/add-teams/REMOVE.md +6 -0
- package/.claude/skills/add-teams/SKILL.md +207 -0
- package/.claude/skills/add-teams/VERIFY.md +3 -0
- package/.claude/skills/add-vercel/SKILL.md +147 -0
- package/.claude/skills/add-vercel/container-skills/vercel-cli/SKILL.md +103 -0
- package/.claude/skills/add-webex/REMOVE.md +6 -0
- package/.claude/skills/add-webex/SKILL.md +88 -0
- package/.claude/skills/add-webex/VERIFY.md +3 -0
- package/.claude/skills/add-wechat/REMOVE.md +49 -0
- package/.claude/skills/add-wechat/SKILL.md +170 -0
- package/.claude/skills/add-wechat/scripts/wire-dm.ts +172 -0
- package/.claude/skills/add-whatsapp/SKILL.md +264 -0
- package/.claude/skills/add-whatsapp-cloud/REMOVE.md +6 -0
- package/.claude/skills/add-whatsapp-cloud/SKILL.md +95 -0
- package/.claude/skills/add-whatsapp-cloud/VERIFY.md +3 -0
- package/.claude/skills/claw/SKILL.md +131 -0
- package/.claude/skills/claw/scripts/claw +374 -0
- package/.claude/skills/convert-to-apple-container/SKILL.md +212 -0
- package/.claude/skills/customize/SKILL.md +110 -0
- package/.claude/skills/debug/SKILL.md +349 -0
- package/.claude/skills/get-qodo-rules/SKILL.md +122 -0
- package/.claude/skills/get-qodo-rules/references/output-format.md +41 -0
- package/.claude/skills/get-qodo-rules/references/pagination.md +33 -0
- package/.claude/skills/get-qodo-rules/references/repository-scope.md +26 -0
- package/.claude/skills/init-first-agent/SKILL.md +120 -0
- package/.claude/skills/init-onecli/SKILL.md +270 -0
- package/.claude/skills/manage-channels/SKILL.md +87 -0
- package/.claude/skills/manage-mounts/SKILL.md +47 -0
- package/.claude/skills/migrate-from-openclaw/MIGRATE_CRONS.md +100 -0
- package/.claude/skills/migrate-from-openclaw/SKILL.md +447 -0
- package/.claude/skills/migrate-from-openclaw/scripts/discover-openclaw.ts +734 -0
- package/.claude/skills/migrate-from-openclaw/scripts/extract-channel-credentials.ts +476 -0
- package/.claude/skills/migrate-nanoclaw/SKILL.md +484 -0
- package/.claude/skills/migrate-nanoclaw/diagnostics.md +51 -0
- package/.claude/skills/qodo-pr-resolver/SKILL.md +326 -0
- package/.claude/skills/qodo-pr-resolver/resources/providers.md +329 -0
- package/.claude/skills/update-nanoclaw/SKILL.md +243 -0
- package/.claude/skills/update-nanoclaw/diagnostics.md +48 -0
- package/.claude/skills/update-skills/SKILL.md +130 -0
- package/.claude/skills/use-native-credential-proxy/SKILL.md +167 -0
- package/.claude/skills/x-integration/SKILL.md +417 -0
- package/.claude/skills/x-integration/agent.ts +243 -0
- package/.claude/skills/x-integration/host.ts +155 -0
- package/.claude/skills/x-integration/lib/browser.ts +148 -0
- package/.claude/skills/x-integration/lib/config.ts +62 -0
- package/.claude/skills/x-integration/scripts/like.ts +56 -0
- package/.claude/skills/x-integration/scripts/post.ts +66 -0
- package/.claude/skills/x-integration/scripts/quote.ts +80 -0
- package/.claude/skills/x-integration/scripts/reply.ts +74 -0
- package/.claude/skills/x-integration/scripts/retweet.ts +62 -0
- package/.claude/skills/x-integration/scripts/setup.ts +87 -0
- package/.github/CODEOWNERS +10 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +18 -0
- package/.github/workflows/bump-version.yml +35 -0
- package/.github/workflows/ci.yml +39 -0
- package/.github/workflows/label-pr.yml +40 -0
- package/.github/workflows/update-tokens.yml +43 -0
- package/.husky/pre-commit +1 -0
- package/.mcp.json +3 -0
- package/.nvmrc +1 -0
- package/.parachute/module.json +14 -0
- package/.prettierrc +4 -0
- package/CHANGELOG.md +215 -0
- package/CLAUDE.md +307 -0
- package/CODE_OF_CONDUCT.md +128 -0
- package/CONTRIBUTING.md +159 -0
- package/CONTRIBUTORS.md +26 -0
- package/LICENSE +21 -0
- package/README.md +190 -0
- package/README_ja.md +194 -0
- package/README_zh.md +194 -0
- package/assets/nanoclaw-favicon.png +0 -0
- package/assets/nanoclaw-icon.png +0 -0
- package/assets/nanoclaw-logo-dark.png +0 -0
- package/assets/nanoclaw-logo.png +0 -0
- package/assets/nanoclaw-profile.jpeg +0 -0
- package/assets/nanoclaw-sales.png +0 -0
- package/assets/social-preview.jpg +0 -0
- package/config-examples/mount-allowlist.json +25 -0
- package/container/.dockerignore +2 -0
- package/container/CLAUDE.md +21 -0
- package/container/Dockerfile +121 -0
- package/container/agent-runner/bun.lock +243 -0
- package/container/agent-runner/package.json +22 -0
- package/container/agent-runner/scripts/sdk-signal-probe.ts +169 -0
- package/container/agent-runner/src/config.ts +55 -0
- package/container/agent-runner/src/db/connection.ts +267 -0
- package/container/agent-runner/src/db/index.ts +20 -0
- package/container/agent-runner/src/db/messages-in.ts +138 -0
- package/container/agent-runner/src/db/messages-out.ts +143 -0
- package/container/agent-runner/src/db/session-routing.ts +30 -0
- package/container/agent-runner/src/db/session-state.test.ts +100 -0
- package/container/agent-runner/src/db/session-state.ts +79 -0
- package/container/agent-runner/src/destinations.ts +135 -0
- package/container/agent-runner/src/formatter.test.ts +167 -0
- package/container/agent-runner/src/formatter.ts +260 -0
- package/container/agent-runner/src/index.ts +110 -0
- package/container/agent-runner/src/integration.test.ts +121 -0
- package/container/agent-runner/src/mcp-tools/agents.instructions.md +26 -0
- package/container/agent-runner/src/mcp-tools/agents.ts +66 -0
- package/container/agent-runner/src/mcp-tools/core.instructions.md +27 -0
- package/container/agent-runner/src/mcp-tools/core.ts +262 -0
- package/container/agent-runner/src/mcp-tools/index.ts +22 -0
- package/container/agent-runner/src/mcp-tools/interactive.instructions.md +22 -0
- package/container/agent-runner/src/mcp-tools/interactive.ts +169 -0
- package/container/agent-runner/src/mcp-tools/scheduling.instructions.md +40 -0
- package/container/agent-runner/src/mcp-tools/scheduling.ts +299 -0
- package/container/agent-runner/src/mcp-tools/self-mod.instructions.md +25 -0
- package/container/agent-runner/src/mcp-tools/self-mod.ts +120 -0
- package/container/agent-runner/src/mcp-tools/server.ts +54 -0
- package/container/agent-runner/src/mcp-tools/types.ts +6 -0
- package/container/agent-runner/src/poll-loop.test.ts +248 -0
- package/container/agent-runner/src/poll-loop.ts +437 -0
- package/container/agent-runner/src/providers/claude.ts +379 -0
- package/container/agent-runner/src/providers/factory.test.ts +19 -0
- package/container/agent-runner/src/providers/factory.ts +13 -0
- package/container/agent-runner/src/providers/index.ts +6 -0
- package/container/agent-runner/src/providers/mock.ts +77 -0
- package/container/agent-runner/src/providers/provider-registry.ts +33 -0
- package/container/agent-runner/src/providers/types.ts +82 -0
- package/container/agent-runner/src/scheduling/task-script.ts +121 -0
- package/container/agent-runner/src/timezone.test.ts +93 -0
- package/container/agent-runner/src/timezone.ts +107 -0
- package/container/agent-runner/tsconfig.json +14 -0
- package/container/build.sh +48 -0
- package/container/entrypoint.sh +16 -0
- package/container/skills/agent-browser/SKILL.md +159 -0
- package/container/skills/frontend-engineer/SKILL.md +157 -0
- package/container/skills/self-customize/SKILL.md +87 -0
- package/container/skills/slack-formatting/SKILL.md +94 -0
- package/container/skills/vercel-cli/SKILL.md +111 -0
- package/container/skills/welcome/SKILL.md +85 -0
- package/docs/APPLE-CONTAINER-NETWORKING.md +90 -0
- package/docs/BRANCH-FORK-MAINTENANCE.md +81 -0
- package/docs/README.md +25 -0
- package/docs/SDK_DEEP_DIVE.md +643 -0
- package/docs/SECURITY.md +162 -0
- package/docs/agent-runner-details.md +749 -0
- package/docs/api-details.md +365 -0
- package/docs/architecture-diagram.html +422 -0
- package/docs/architecture-diagram.md +215 -0
- package/docs/architecture.md +751 -0
- package/docs/audit/2026-04-30-channel-endpoint-audit.md +36 -0
- package/docs/build-and-runtime.md +80 -0
- package/docs/cross-mount-stress/README.md +112 -0
- package/docs/cross-mount-stress/container-writer-retry.mjs +55 -0
- package/docs/cross-mount-stress/container-writer-slow.mjs +42 -0
- package/docs/cross-mount-stress/container-writer.mjs +47 -0
- package/docs/cross-mount-stress/host-writer-retry.mjs +55 -0
- package/docs/cross-mount-stress/host-writer-slow.mjs +43 -0
- package/docs/cross-mount-stress/host-writer.mjs +47 -0
- package/docs/db-central.md +316 -0
- package/docs/db-session.md +183 -0
- package/docs/db.md +119 -0
- package/docs/design/2026-04-29-vault-management-ui.md +231 -0
- package/docs/design/2026-04-30-channel-wiring-rework.md +234 -0
- package/docs/design/2026-05-01-channel-wiring-approvals-deep-dive.md +272 -0
- package/docs/design/2026-05-02-channel-policy-and-approval-routing.md +250 -0
- package/docs/docker-sandboxes.md +359 -0
- package/docs/isolation-model.md +88 -0
- package/docs/ollama.md +79 -0
- package/docs/parachute-integration.md +109 -0
- package/docs/post-night-rebirth-reflections.md +151 -0
- package/eslint.config.js +32 -0
- package/package.json +54 -0
- package/pnpm-workspace.yaml +8 -0
- package/repo-tokens/README.md +113 -0
- package/repo-tokens/action.yml +186 -0
- package/repo-tokens/badge.svg +23 -0
- package/repo-tokens/examples/green.svg +14 -0
- package/repo-tokens/examples/red.svg +14 -0
- package/repo-tokens/examples/yellow-green.svg +14 -0
- package/repo-tokens/examples/yellow.svg +14 -0
- package/scripts/chat.ts +101 -0
- package/scripts/cleanup-sessions.sh +150 -0
- package/scripts/init-cli-agent.ts +171 -0
- package/scripts/init-first-agent.ts +377 -0
- package/scripts/parachute.ts +158 -0
- package/scripts/run-migrations.ts +105 -0
- package/scripts/sanity-live-poll.ts +95 -0
- package/scripts/seed-discord.ts +79 -0
- package/scripts/test-v2-agent.ts +106 -0
- package/scripts/test-v2-channel-e2e.ts +265 -0
- package/scripts/test-v2-host.ts +184 -0
- package/src/channels/adapter.ts +214 -0
- package/src/channels/ask-question.ts +46 -0
- package/src/channels/channel-registry.test.ts +421 -0
- package/src/channels/channel-registry.ts +313 -0
- package/src/channels/chat-sdk-bridge.test.ts +84 -0
- package/src/channels/chat-sdk-bridge.ts +652 -0
- package/src/channels/cli.ts +276 -0
- package/src/channels/discord.ts +90 -0
- package/src/channels/index.ts +17 -0
- package/src/channels/telegram-markdown-sanitize.test.ts +78 -0
- package/src/channels/telegram-markdown-sanitize.ts +55 -0
- package/src/channels/telegram-pairing.test.ts +254 -0
- package/src/channels/telegram-pairing.ts +339 -0
- package/src/channels/telegram.ts +279 -0
- package/src/channels/trust-hint.test.ts +48 -0
- package/src/channels/trust-hint.ts +75 -0
- package/src/claude-md-compose.migrate.test.ts +64 -0
- package/src/claude-md-compose.ts +205 -0
- package/src/command-gate.ts +63 -0
- package/src/config.test.ts +93 -0
- package/src/config.ts +108 -0
- package/src/container-config.ts +167 -0
- package/src/container-runner.test.ts +32 -0
- package/src/container-runner.ts +576 -0
- package/src/container-runtime.test.ts +169 -0
- package/src/container-runtime.ts +92 -0
- package/src/db/_bun-sqlite-shim.ts +88 -0
- package/src/db/agent-activity.test.ts +155 -0
- package/src/db/agent-activity.ts +121 -0
- package/src/db/agent-groups.ts +77 -0
- package/src/db/connection.migrate.test.ts +143 -0
- package/src/db/connection.ts +224 -0
- package/src/db/db-v2.test.ts +440 -0
- package/src/db/dropped-messages.ts +44 -0
- package/src/db/index.ts +40 -0
- package/src/db/messaging-groups.ts +252 -0
- package/src/db/migrations/001-initial.ts +112 -0
- package/src/db/migrations/002-chat-sdk-state.ts +36 -0
- package/src/db/migrations/008-dropped-messages.ts +27 -0
- package/src/db/migrations/009-drop-pending-credentials.ts +13 -0
- package/src/db/migrations/010-engage-modes.ts +103 -0
- package/src/db/migrations/011-pending-sender-approvals.ts +40 -0
- package/src/db/migrations/012-channel-registration.ts +48 -0
- package/src/db/migrations/013-approval-render-metadata.ts +27 -0
- package/src/db/migrations/014-secrets.ts +44 -0
- package/src/db/migrations/015-secrets-drop-host-pattern.ts +18 -0
- package/src/db/migrations/016-secret-assignments.ts +30 -0
- package/src/db/migrations/017-agent-activity.ts +40 -0
- package/src/db/migrations/018-oauth-app-configs.ts +34 -0
- package/src/db/migrations/019-oauth-app-connections.ts +48 -0
- package/src/db/migrations/020-agent-app-connections.ts +28 -0
- package/src/db/migrations/021-pending-oauth-states.ts +35 -0
- package/src/db/migrations/022-app-connections-provider.ts +25 -0
- package/src/db/migrations/023-agent-group-secret-mode.test.ts +124 -0
- package/src/db/migrations/023-agent-group-secret-mode.ts +65 -0
- package/src/db/migrations/024-collapse-approvals.test.ts +249 -0
- package/src/db/migrations/024-collapse-approvals.ts +182 -0
- package/src/db/migrations/025-secret-mode-check.test.ts +155 -0
- package/src/db/migrations/025-secret-mode-check.ts +49 -0
- package/src/db/migrations/026-user-dms-bot-id.test.ts +116 -0
- package/src/db/migrations/026-user-dms-bot-id.ts +54 -0
- package/src/db/migrations/027-provider-credentials.ts +41 -0
- package/src/db/migrations/_test-helpers.ts +41 -0
- package/src/db/migrations/index.ts +127 -0
- package/src/db/migrations/module-agent-to-agent-destinations.ts +84 -0
- package/src/db/migrations/module-approvals-pending-approvals.ts +42 -0
- package/src/db/migrations/module-approvals-title-options.ts +40 -0
- package/src/db/schema.ts +258 -0
- package/src/db/session-db.test.ts +93 -0
- package/src/db/session-db.ts +325 -0
- package/src/db/sessions.ts +241 -0
- package/src/delivery.test.ts +148 -0
- package/src/delivery.ts +445 -0
- package/src/env.ts +74 -0
- package/src/group-folder.test.ts +35 -0
- package/src/group-folder.ts +44 -0
- package/src/group-init.ts +92 -0
- package/src/host-core.test.ts +456 -0
- package/src/host-sweep.test.ts +146 -0
- package/src/host-sweep.ts +287 -0
- package/src/index.ts +227 -0
- package/src/install-slug.ts +33 -0
- package/src/log.test.ts +81 -0
- package/src/log.ts +117 -0
- package/src/mcp/http.ts +72 -0
- package/src/mcp/server.ts +92 -0
- package/src/mcp/stdio.ts +51 -0
- package/src/mcp/tools/activity.ts +88 -0
- package/src/mcp/tools/agent-groups.ts +183 -0
- package/src/mcp/tools/approvals.ts +122 -0
- package/src/mcp/tools/channels.ts +199 -0
- package/src/mcp/tools/index.ts +27 -0
- package/src/mcp/tools/oauth.ts +48 -0
- package/src/mcp/tools/secrets.ts +169 -0
- package/src/mcp/tools/sessions.ts +135 -0
- package/src/mcp/types.ts +51 -0
- package/src/modules/agent-to-agent/agent-route.test.ts +46 -0
- package/src/modules/agent-to-agent/agent-route.ts +223 -0
- package/src/modules/agent-to-agent/create-agent.ts +127 -0
- package/src/modules/agent-to-agent/db/agent-destinations.ts +135 -0
- package/src/modules/agent-to-agent/index.ts +22 -0
- package/src/modules/agent-to-agent/write-destinations.ts +59 -0
- package/src/modules/approvals/agent.md +45 -0
- package/src/modules/approvals/index.ts +21 -0
- package/src/modules/approvals/picks.test.ts +291 -0
- package/src/modules/approvals/primitive.ts +279 -0
- package/src/modules/approvals/project.md +27 -0
- package/src/modules/approvals/response-handler.ts +87 -0
- package/src/modules/index.ts +24 -0
- package/src/modules/interactive/agent.md +21 -0
- package/src/modules/interactive/index.ts +69 -0
- package/src/modules/interactive/project.md +12 -0
- package/src/modules/mount-security/index.ts +448 -0
- package/src/modules/mount-security/migrate.test.ts +91 -0
- package/src/modules/permissions/access.ts +28 -0
- package/src/modules/permissions/channel-approval.test.ts +389 -0
- package/src/modules/permissions/channel-approval.ts +188 -0
- package/src/modules/permissions/db/agent-group-members.ts +44 -0
- package/src/modules/permissions/db/pending-channel-approvals.test.ts +86 -0
- package/src/modules/permissions/db/pending-channel-approvals.ts +66 -0
- package/src/modules/permissions/db/pending-sender-approvals.ts +60 -0
- package/src/modules/permissions/db/user-dms.ts +58 -0
- package/src/modules/permissions/db/user-roles.ts +85 -0
- package/src/modules/permissions/db/users.ts +38 -0
- package/src/modules/permissions/index.ts +421 -0
- package/src/modules/permissions/permissions.test.ts +358 -0
- package/src/modules/permissions/sender-approval.test.ts +470 -0
- package/src/modules/permissions/sender-approval.ts +165 -0
- package/src/modules/permissions/user-dm.ts +200 -0
- package/src/modules/provider-credentials/db.ts +121 -0
- package/src/modules/provider-credentials/index.ts +12 -0
- package/src/modules/provider-credentials/spawn.test.ts +206 -0
- package/src/modules/provider-credentials/spawn.ts +114 -0
- package/src/modules/scheduling/actions.ts +113 -0
- package/src/modules/scheduling/db.test.ts +282 -0
- package/src/modules/scheduling/db.ts +148 -0
- package/src/modules/scheduling/index.ts +34 -0
- package/src/modules/scheduling/recurrence.test.ts +98 -0
- package/src/modules/scheduling/recurrence.ts +54 -0
- package/src/modules/self-mod/agent.md +30 -0
- package/src/modules/self-mod/apply.ts +85 -0
- package/src/modules/self-mod/index.ts +30 -0
- package/src/modules/self-mod/project.md +39 -0
- package/src/modules/self-mod/request.ts +91 -0
- package/src/modules/typing/index.ts +165 -0
- package/src/oauth/agent-app-connections.ts +103 -0
- package/src/oauth/app-configs.test.ts +64 -0
- package/src/oauth/app-configs.ts +114 -0
- package/src/oauth/app-connections.test.ts +109 -0
- package/src/oauth/app-connections.ts +178 -0
- package/src/oauth/crypto.ts +56 -0
- package/src/oauth/flow.ts +104 -0
- package/src/oauth/providers/google.test.ts +38 -0
- package/src/oauth/providers/google.ts +46 -0
- package/src/oauth/providers/index.ts +48 -0
- package/src/oauth/state-store.test.ts +54 -0
- package/src/oauth/state-store.ts +93 -0
- package/src/parachute/README.md +27 -0
- package/src/parachute/create-agent.test.ts +83 -0
- package/src/parachute/create-agent.ts +122 -0
- package/src/parachute/group-status.test.ts +165 -0
- package/src/parachute/group-status.ts +136 -0
- package/src/parachute/types.ts +41 -0
- package/src/parachute/vault-mcp.test.ts +251 -0
- package/src/parachute/vault-mcp.ts +232 -0
- package/src/platform-id.test.ts +104 -0
- package/src/platform-id.ts +109 -0
- package/src/providers/index.ts +6 -0
- package/src/providers/provider-container-registry.ts +58 -0
- package/src/response-registry.ts +45 -0
- package/src/router.ts +530 -0
- package/src/secrets/crypto.test.ts +45 -0
- package/src/secrets/crypto.ts +55 -0
- package/src/secrets/index.ts +355 -0
- package/src/secrets/master-key.ts +70 -0
- package/src/secrets/secrets.test.ts +354 -0
- package/src/session-manager.migrate.test.ts +59 -0
- package/src/session-manager.ts +433 -0
- package/src/startup-bootstrap.test.ts +226 -0
- package/src/startup-bootstrap.ts +207 -0
- package/src/state-sqlite.ts +182 -0
- package/src/timezone.test.ts +64 -0
- package/src/timezone.ts +37 -0
- package/src/types.ts +230 -0
- package/src/web/auth.test.ts +335 -0
- package/src/web/auth.ts +214 -0
- package/src/web/discord-validate.test.ts +77 -0
- package/src/web/discord-validate.ts +88 -0
- package/src/web/hub-discovery.test.ts +98 -0
- package/src/web/hub-discovery.ts +69 -0
- package/src/web/routes/activity.ts +106 -0
- package/src/web/routes/agent-provider.test.ts +282 -0
- package/src/web/routes/agent-provider.ts +309 -0
- package/src/web/routes/approvals.ts +185 -0
- package/src/web/routes/apps.ts +434 -0
- package/src/web/routes/channels-mg-detail.test.ts +324 -0
- package/src/web/routes/channels-mga-detail.test.ts +425 -0
- package/src/web/routes/channels.ts +489 -0
- package/src/web/routes/oauth-providers.ts +42 -0
- package/src/web/routes/secrets.test.ts +175 -0
- package/src/web/routes/secrets.ts +282 -0
- package/src/web/routes/sessions.ts +123 -0
- package/src/web/routes/settings.test.ts +106 -0
- package/src/web/routes/settings.ts +247 -0
- package/src/web/routes/setup-status.ts +205 -0
- package/src/web/routes/vaults.test.ts +389 -0
- package/src/web/routes/vaults.ts +225 -0
- package/src/web/server-version.test.ts +16 -0
- package/src/web/server.ts +1003 -0
- package/src/web/services-manifest.test.ts +120 -0
- package/src/web/services-manifest.ts +61 -0
- package/src/web/static-serve.test.ts +255 -0
- package/src/web/static-serve.ts +104 -0
- package/src/web/telegram-validate.test.ts +116 -0
- package/src/web/telegram-validate.ts +107 -0
- package/src/web/vault-proxy.test.ts +214 -0
- package/src/web/vault-proxy.ts +120 -0
- package/src/web/wire-channel.ts +181 -0
- package/src/webhook-server.ts +134 -0
- package/tsconfig.json +21 -0
- package/vitest.config.ts +18 -0
- package/web/README.md +63 -0
- package/web/ui/index.html +13 -0
- package/web/ui/package.json +35 -0
- package/web/ui/pnpm-lock.yaml +2164 -0
- package/web/ui/scripts/verify-base.mjs +31 -0
- package/web/ui/src/App.tsx +88 -0
- package/web/ui/src/components/ActivityFeed.tsx +444 -0
- package/web/ui/src/components/AgentGroupPicker.tsx +263 -0
- package/web/ui/src/components/AgentProviderCards.tsx +220 -0
- package/web/ui/src/components/CredentialForm.tsx +214 -0
- package/web/ui/src/components/ScopeGrants.tsx +74 -0
- package/web/ui/src/components/StatusDot.tsx +43 -0
- package/web/ui/src/components/VaultPicker.tsx +127 -0
- package/web/ui/src/components/setup/AdapterInstallStep.tsx +178 -0
- package/web/ui/src/components/setup/AgentGroupStep.tsx +43 -0
- package/web/ui/src/components/setup/ChannelPickStep.tsx +74 -0
- package/web/ui/src/components/setup/DoneStep.tsx +49 -0
- package/web/ui/src/components/setup/PrereqStep.tsx +129 -0
- package/web/ui/src/components/setup/TestConnectionStep.tsx +108 -0
- package/web/ui/src/components/setup/TestMessageStep.tsx +104 -0
- package/web/ui/src/components/setup/WireChannelStep.tsx +166 -0
- package/web/ui/src/components/setup/types.ts +105 -0
- package/web/ui/src/lib/api.test.ts +410 -0
- package/web/ui/src/lib/api.ts +1210 -0
- package/web/ui/src/lib/auth.test.ts +139 -0
- package/web/ui/src/lib/auth.ts +348 -0
- package/web/ui/src/lib/channel-adapters.ts +136 -0
- package/web/ui/src/main.tsx +19 -0
- package/web/ui/src/routes/ApprovalsList.tsx +294 -0
- package/web/ui/src/routes/Apps.tsx +613 -0
- package/web/ui/src/routes/ChannelWireDetail.test.tsx +233 -0
- package/web/ui/src/routes/ChannelWireDetail.tsx +403 -0
- package/web/ui/src/routes/ChannelsList.tsx +158 -0
- package/web/ui/src/routes/GroupDetail.tsx +755 -0
- package/web/ui/src/routes/GroupList.tsx +187 -0
- package/web/ui/src/routes/MessagingGroupDetail.test.tsx +233 -0
- package/web/ui/src/routes/MessagingGroupDetail.tsx +306 -0
- package/web/ui/src/routes/NewGroupWizard.tsx +390 -0
- package/web/ui/src/routes/OAuthCallback.tsx +56 -0
- package/web/ui/src/routes/SecretsList.tsx +921 -0
- package/web/ui/src/routes/SessionsList.tsx +220 -0
- package/web/ui/src/routes/SettingsAgentProvider.tsx +109 -0
- package/web/ui/src/routes/SettingsApprovals.tsx +234 -0
- package/web/ui/src/routes/SetupWizard.tsx +219 -0
- package/web/ui/src/routes/VaultDetail.test.tsx +361 -0
- package/web/ui/src/routes/VaultDetail.tsx +960 -0
- package/web/ui/src/routes/VaultsList.tsx +295 -0
- package/web/ui/src/routes/WireChannelPage.tsx +413 -0
- package/web/ui/src/styles.css +608 -0
- package/web/ui/src/test/setup.ts +23 -0
- package/web/ui/src/vite-env.d.ts +10 -0
- package/web/ui/tsconfig.json +20 -0
- package/web/ui/vite.config.ts +34 -0
- package/web/ui/vitest.config.ts +25 -0
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: x-integration
|
|
3
|
+
description: X (Twitter) integration for NanoClaw. Post tweets, like, reply, retweet, and quote. Use for setup, testing, or troubleshooting X functionality. Triggers on "setup x", "x integration", "twitter", "post tweet", "tweet".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# X (Twitter) Integration
|
|
7
|
+
|
|
8
|
+
Browser automation for X interactions via WhatsApp.
|
|
9
|
+
|
|
10
|
+
> **Compatibility:** NanoClaw v1.0.0. Directory structure may change in future versions.
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
| Action | Tool | Description |
|
|
15
|
+
|--------|------|-------------|
|
|
16
|
+
| Post | `x_post` | Publish new tweets |
|
|
17
|
+
| Like | `x_like` | Like any tweet |
|
|
18
|
+
| Reply | `x_reply` | Reply to tweets |
|
|
19
|
+
| Retweet | `x_retweet` | Retweet without comment |
|
|
20
|
+
| Quote | `x_quote` | Quote tweet with comment |
|
|
21
|
+
|
|
22
|
+
## Prerequisites
|
|
23
|
+
|
|
24
|
+
Before using this skill, ensure:
|
|
25
|
+
|
|
26
|
+
1. **NanoClaw is installed and running** - WhatsApp connected, service active
|
|
27
|
+
2. **Dependencies installed**:
|
|
28
|
+
```bash
|
|
29
|
+
pnpm ls playwright dotenv-cli || pnpm install playwright dotenv-cli
|
|
30
|
+
```
|
|
31
|
+
3. **CHROME_PATH configured** in `.env` (if Chrome is not at default location):
|
|
32
|
+
```bash
|
|
33
|
+
# Find your Chrome path
|
|
34
|
+
mdfind "kMDItemCFBundleIdentifier == 'com.google.Chrome'" 2>/dev/null | head -1
|
|
35
|
+
# Add to .env
|
|
36
|
+
CHROME_PATH=/path/to/Google Chrome.app/Contents/MacOS/Google Chrome
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# 1. Setup authentication (interactive)
|
|
43
|
+
pnpm exec dotenv -e .env -- pnpm exec tsx .claude/skills/x-integration/scripts/setup.ts
|
|
44
|
+
# Verify: data/x-auth.json should exist after successful login
|
|
45
|
+
|
|
46
|
+
# 2. Rebuild container to include skill
|
|
47
|
+
./container/build.sh
|
|
48
|
+
# Verify: Output shows "COPY .claude/skills/x-integration/agent.ts"
|
|
49
|
+
|
|
50
|
+
# 3. Rebuild host and restart service
|
|
51
|
+
pnpm run build
|
|
52
|
+
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
|
|
53
|
+
# Linux: systemctl --user restart nanoclaw
|
|
54
|
+
# Verify: launchctl list | grep nanoclaw (macOS) or systemctl --user status nanoclaw (Linux)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Configuration
|
|
58
|
+
|
|
59
|
+
### Environment Variables
|
|
60
|
+
|
|
61
|
+
| Variable | Default | Description |
|
|
62
|
+
|----------|---------|-------------|
|
|
63
|
+
| `CHROME_PATH` | `/Applications/Google Chrome.app/Contents/MacOS/Google Chrome` | Chrome executable path |
|
|
64
|
+
| `NANOCLAW_ROOT` | `process.cwd()` | Project root directory |
|
|
65
|
+
| `LOG_LEVEL` | `info` | Logging level (debug, info, warn, error) |
|
|
66
|
+
|
|
67
|
+
Set in `.env` file (loaded via `dotenv-cli` at runtime):
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# .env
|
|
71
|
+
CHROME_PATH=/Applications/Google Chrome.app/Contents/MacOS/Google Chrome
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Configuration File
|
|
75
|
+
|
|
76
|
+
Edit `lib/config.ts` to modify defaults:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
export const config = {
|
|
80
|
+
// Browser viewport
|
|
81
|
+
viewport: { width: 1280, height: 800 },
|
|
82
|
+
|
|
83
|
+
// Timeouts (milliseconds)
|
|
84
|
+
timeouts: {
|
|
85
|
+
navigation: 30000, // Page navigation
|
|
86
|
+
elementWait: 5000, // Wait for element
|
|
87
|
+
afterClick: 1000, // Delay after click
|
|
88
|
+
afterFill: 1000, // Delay after form fill
|
|
89
|
+
afterSubmit: 3000, // Delay after submit
|
|
90
|
+
pageLoad: 3000, // Initial page load
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
// Tweet limits
|
|
94
|
+
limits: {
|
|
95
|
+
tweetMaxLength: 280,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Data Directories
|
|
101
|
+
|
|
102
|
+
Paths relative to project root:
|
|
103
|
+
|
|
104
|
+
| Path | Purpose | Git |
|
|
105
|
+
|------|---------|-----|
|
|
106
|
+
| `data/x-browser-profile/` | Chrome profile with X session | Ignored |
|
|
107
|
+
| `data/x-auth.json` | Auth state marker | Ignored |
|
|
108
|
+
| `logs/nanoclaw.log` | Service logs (contains X operation logs) | Ignored |
|
|
109
|
+
|
|
110
|
+
## Architecture
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
114
|
+
│ Container (Linux VM) │
|
|
115
|
+
│ └── agent.ts → MCP tool definitions (x_post, etc.) │
|
|
116
|
+
│ └── Writes IPC request to /workspace/ipc/tasks/ │
|
|
117
|
+
└──────────────────────┬──────────────────────────────────────┘
|
|
118
|
+
│ IPC (file system)
|
|
119
|
+
▼
|
|
120
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
121
|
+
│ Host (macOS) │
|
|
122
|
+
│ └── src/ipc.ts → processTaskIpc() │
|
|
123
|
+
│ └── host.ts → handleXIpc() │
|
|
124
|
+
│ └── spawn subprocess → scripts/*.ts │
|
|
125
|
+
│ └── Playwright → Chrome → X Website │
|
|
126
|
+
└─────────────────────────────────────────────────────────────┘
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Why This Design?
|
|
130
|
+
|
|
131
|
+
- **API is expensive** - X official API requires paid subscription ($100+/month) for posting
|
|
132
|
+
- **Bot browsers get blocked** - X detects and bans headless browsers and common automation fingerprints
|
|
133
|
+
- **Must use user's real browser** - Reuses the user's actual Chrome on Host with real browser fingerprint to avoid detection
|
|
134
|
+
- **One-time authorization** - User logs in manually once, session persists in Chrome profile for future use
|
|
135
|
+
|
|
136
|
+
### File Structure
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
.claude/skills/x-integration/
|
|
140
|
+
├── SKILL.md # This documentation
|
|
141
|
+
├── host.ts # Host-side IPC handler
|
|
142
|
+
├── agent.ts # Container-side MCP tool definitions
|
|
143
|
+
├── lib/
|
|
144
|
+
│ ├── config.ts # Centralized configuration
|
|
145
|
+
│ └── browser.ts # Playwright utilities
|
|
146
|
+
└── scripts/
|
|
147
|
+
├── setup.ts # Interactive login
|
|
148
|
+
├── post.ts # Post tweet
|
|
149
|
+
├── like.ts # Like tweet
|
|
150
|
+
├── reply.ts # Reply to tweet
|
|
151
|
+
├── retweet.ts # Retweet
|
|
152
|
+
└── quote.ts # Quote tweet
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Integration Points
|
|
156
|
+
|
|
157
|
+
To integrate this skill into NanoClaw, make the following modifications:
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
**1. Host side: `src/ipc.ts`**
|
|
162
|
+
|
|
163
|
+
Add import after other local imports:
|
|
164
|
+
```typescript
|
|
165
|
+
import { handleXIpc } from '../.claude/skills/x-integration/host.js';
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Modify `processTaskIpc` function's switch statement default case:
|
|
169
|
+
```typescript
|
|
170
|
+
// Find:
|
|
171
|
+
default:
|
|
172
|
+
logger.warn({ type: data.type }, 'Unknown IPC task type');
|
|
173
|
+
|
|
174
|
+
// Replace with:
|
|
175
|
+
default:
|
|
176
|
+
const handled = await handleXIpc(data, sourceGroup, isMain, DATA_DIR);
|
|
177
|
+
if (!handled) {
|
|
178
|
+
logger.warn({ type: data.type }, 'Unknown IPC task type');
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
**2. Container side: `container/agent-runner/src/ipc-mcp.ts`**
|
|
185
|
+
|
|
186
|
+
Add import after `cron-parser` import:
|
|
187
|
+
```typescript
|
|
188
|
+
// @ts-ignore - Copied during Docker build from .claude/skills/x-integration/
|
|
189
|
+
import { createXTools } from './skills/x-integration/agent.js';
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Add to the end of tools array (before the closing `]`):
|
|
193
|
+
```typescript
|
|
194
|
+
...createXTools({ groupFolder, isMain })
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
**3. Build script: `container/build.sh`**
|
|
200
|
+
|
|
201
|
+
Change build context from `container/` to project root (required to access `.claude/skills/`):
|
|
202
|
+
```bash
|
|
203
|
+
# Find:
|
|
204
|
+
docker build -t "${IMAGE_NAME}:${TAG}" .
|
|
205
|
+
|
|
206
|
+
# Replace with:
|
|
207
|
+
cd "$SCRIPT_DIR/.."
|
|
208
|
+
docker build -t "${IMAGE_NAME}:${TAG}" -f container/Dockerfile .
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
**4. Dockerfile: `container/Dockerfile`**
|
|
214
|
+
|
|
215
|
+
First, update the build context paths (required to access `.claude/skills/` from project root):
|
|
216
|
+
```dockerfile
|
|
217
|
+
# Find:
|
|
218
|
+
COPY agent-runner/package*.json ./
|
|
219
|
+
...
|
|
220
|
+
COPY agent-runner/ ./
|
|
221
|
+
|
|
222
|
+
# Replace with:
|
|
223
|
+
COPY container/agent-runner/package*.json ./
|
|
224
|
+
...
|
|
225
|
+
COPY container/agent-runner/ ./
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Then add COPY line after `COPY container/agent-runner/ ./` and before `RUN pnpm run build`:
|
|
229
|
+
```dockerfile
|
|
230
|
+
# Copy skill MCP tools
|
|
231
|
+
COPY .claude/skills/x-integration/agent.ts ./src/skills/x-integration/
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Setup
|
|
235
|
+
|
|
236
|
+
All paths below are relative to project root (`NANOCLAW_ROOT`).
|
|
237
|
+
|
|
238
|
+
### 1. Check Chrome Path
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
# Check if Chrome exists at configured path
|
|
242
|
+
cat .env | grep CHROME_PATH
|
|
243
|
+
ls -la "$(grep CHROME_PATH .env | cut -d= -f2)" 2>/dev/null || \
|
|
244
|
+
echo "Chrome not found - update CHROME_PATH in .env"
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### 2. Run Authentication
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
pnpm exec dotenv -e .env -- pnpm exec tsx .claude/skills/x-integration/scripts/setup.ts
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
This opens Chrome for manual X login. Session saved to `data/x-browser-profile/`.
|
|
254
|
+
|
|
255
|
+
**Verify success:**
|
|
256
|
+
```bash
|
|
257
|
+
cat data/x-auth.json # Should show {"authenticated": true, ...}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### 3. Rebuild Container
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
./container/build.sh
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**Verify success:**
|
|
267
|
+
```bash
|
|
268
|
+
./container/build.sh 2>&1 | grep -i "agent.ts" # Should show COPY line
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### 4. Restart Service
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
pnpm run build
|
|
275
|
+
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
|
|
276
|
+
# Linux: systemctl --user restart nanoclaw
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**Verify success:**
|
|
280
|
+
```bash
|
|
281
|
+
launchctl list | grep nanoclaw # macOS — should show PID and exit code 0 or -
|
|
282
|
+
# Linux: systemctl --user status nanoclaw
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## Usage via WhatsApp
|
|
286
|
+
|
|
287
|
+
Replace `@Assistant` with your configured trigger name (`ASSISTANT_NAME` in `.env`):
|
|
288
|
+
|
|
289
|
+
```
|
|
290
|
+
@Assistant post a tweet: Hello world!
|
|
291
|
+
|
|
292
|
+
@Assistant like this tweet https://x.com/user/status/123
|
|
293
|
+
|
|
294
|
+
@Assistant reply to https://x.com/user/status/123 with: Great post!
|
|
295
|
+
|
|
296
|
+
@Assistant retweet https://x.com/user/status/123
|
|
297
|
+
|
|
298
|
+
@Assistant quote https://x.com/user/status/123 with comment: Interesting
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**Note:** Only the main group can use X tools. Other groups will receive an error.
|
|
302
|
+
|
|
303
|
+
## Testing
|
|
304
|
+
|
|
305
|
+
Scripts require environment variables from `.env`. Use `dotenv-cli` to load them:
|
|
306
|
+
|
|
307
|
+
### Check Authentication Status
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
# Check if auth file exists and is valid
|
|
311
|
+
cat data/x-auth.json 2>/dev/null && echo "Auth configured" || echo "Auth not configured"
|
|
312
|
+
|
|
313
|
+
# Check if browser profile exists
|
|
314
|
+
ls -la data/x-browser-profile/ 2>/dev/null | head -5
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Re-authenticate (if expired)
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
pnpm exec dotenv -e .env -- pnpm exec tsx .claude/skills/x-integration/scripts/setup.ts
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Test Post (will actually post)
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
echo '{"content":"Test tweet - please ignore"}' | pnpm exec dotenv -e .env -- pnpm exec tsx .claude/skills/x-integration/scripts/post.ts
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Test Like
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
echo '{"tweetUrl":"https://x.com/user/status/123"}' | pnpm exec dotenv -e .env -- pnpm exec tsx .claude/skills/x-integration/scripts/like.ts
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Or export `CHROME_PATH` manually before running:
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
export CHROME_PATH="/path/to/chrome"
|
|
339
|
+
echo '{"content":"Test"}' | pnpm exec tsx .claude/skills/x-integration/scripts/post.ts
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Troubleshooting
|
|
343
|
+
|
|
344
|
+
### Authentication Expired
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
pnpm exec dotenv -e .env -- pnpm exec tsx .claude/skills/x-integration/scripts/setup.ts
|
|
348
|
+
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
|
|
349
|
+
# Linux: systemctl --user restart nanoclaw
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Browser Lock Files
|
|
353
|
+
|
|
354
|
+
If Chrome fails to launch:
|
|
355
|
+
|
|
356
|
+
```bash
|
|
357
|
+
rm -f data/x-browser-profile/SingletonLock
|
|
358
|
+
rm -f data/x-browser-profile/SingletonSocket
|
|
359
|
+
rm -f data/x-browser-profile/SingletonCookie
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Check Logs
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
# Host logs (relative to project root)
|
|
366
|
+
grep -i "x_post\|x_like\|x_reply\|handleXIpc" logs/nanoclaw.log | tail -20
|
|
367
|
+
|
|
368
|
+
# Script errors
|
|
369
|
+
grep -i "error\|failed" logs/nanoclaw.log | tail -20
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Script Timeout
|
|
373
|
+
|
|
374
|
+
Default timeout is 2 minutes (120s). Increase in `host.ts`:
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
const timer = setTimeout(() => {
|
|
378
|
+
proc.kill('SIGTERM');
|
|
379
|
+
resolve({ success: false, message: 'Script timed out (120s)' });
|
|
380
|
+
}, 120000); // ← Increase this value
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### X UI Selector Changes
|
|
384
|
+
|
|
385
|
+
If X updates their UI, selectors in scripts may break. Current selectors:
|
|
386
|
+
|
|
387
|
+
| Element | Selector |
|
|
388
|
+
|---------|----------|
|
|
389
|
+
| Tweet input | `[data-testid="tweetTextarea_0"]` |
|
|
390
|
+
| Post button | `[data-testid="tweetButtonInline"]` |
|
|
391
|
+
| Reply button | `[data-testid="reply"]` |
|
|
392
|
+
| Like | `[data-testid="like"]` |
|
|
393
|
+
| Unlike | `[data-testid="unlike"]` |
|
|
394
|
+
| Retweet | `[data-testid="retweet"]` |
|
|
395
|
+
| Unretweet | `[data-testid="unretweet"]` |
|
|
396
|
+
| Confirm retweet | `[data-testid="retweetConfirm"]` |
|
|
397
|
+
| Modal dialog | `[role="dialog"][aria-modal="true"]` |
|
|
398
|
+
| Modal submit | `[data-testid="tweetButton"]` |
|
|
399
|
+
|
|
400
|
+
### Container Build Issues
|
|
401
|
+
|
|
402
|
+
If MCP tools not found in container:
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
# Verify build copies skill
|
|
406
|
+
./container/build.sh 2>&1 | grep -i skill
|
|
407
|
+
|
|
408
|
+
# Check container has the file
|
|
409
|
+
docker run nanoclaw-agent ls -la /app/src/skills/
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
## Security
|
|
413
|
+
|
|
414
|
+
- `data/x-browser-profile/` - Contains X session cookies (in `.gitignore`)
|
|
415
|
+
- `data/x-auth.json` - Auth state marker (in `.gitignore`)
|
|
416
|
+
- Only main group can use X tools (enforced in `agent.ts` and `host.ts`)
|
|
417
|
+
- Scripts run as subprocesses with limited environment
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* X Integration - MCP Tool Definitions (Agent/Container Side)
|
|
3
|
+
*
|
|
4
|
+
* These tools run inside the container and communicate with the host via IPC.
|
|
5
|
+
* The host-side implementation is in host.ts.
|
|
6
|
+
*
|
|
7
|
+
* Note: This file is compiled in the container, not on the host.
|
|
8
|
+
* The @ts-ignore is needed because the SDK is only available in the container.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// @ts-ignore - SDK available in container environment only
|
|
12
|
+
import { tool } from '@anthropic-ai/claude-agent-sdk';
|
|
13
|
+
import { z } from 'zod';
|
|
14
|
+
import fs from 'fs';
|
|
15
|
+
import path from 'path';
|
|
16
|
+
|
|
17
|
+
// IPC directories (inside container)
|
|
18
|
+
const IPC_DIR = '/workspace/ipc';
|
|
19
|
+
const TASKS_DIR = path.join(IPC_DIR, 'tasks');
|
|
20
|
+
const RESULTS_DIR = path.join(IPC_DIR, 'x_results');
|
|
21
|
+
|
|
22
|
+
function writeIpcFile(dir: string, data: object): string {
|
|
23
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
24
|
+
const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}.json`;
|
|
25
|
+
const filepath = path.join(dir, filename);
|
|
26
|
+
const tempPath = `${filepath}.tmp`;
|
|
27
|
+
fs.writeFileSync(tempPath, JSON.stringify(data, null, 2));
|
|
28
|
+
fs.renameSync(tempPath, filepath);
|
|
29
|
+
return filename;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function waitForResult(requestId: string, maxWait = 60000): Promise<{ success: boolean; message: string }> {
|
|
33
|
+
const resultFile = path.join(RESULTS_DIR, `${requestId}.json`);
|
|
34
|
+
const pollInterval = 1000;
|
|
35
|
+
let elapsed = 0;
|
|
36
|
+
|
|
37
|
+
while (elapsed < maxWait) {
|
|
38
|
+
if (fs.existsSync(resultFile)) {
|
|
39
|
+
try {
|
|
40
|
+
const result = JSON.parse(fs.readFileSync(resultFile, 'utf-8'));
|
|
41
|
+
fs.unlinkSync(resultFile);
|
|
42
|
+
return result;
|
|
43
|
+
} catch (err) {
|
|
44
|
+
return { success: false, message: `Failed to read result: ${err}` };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
await new Promise(resolve => setTimeout(resolve, pollInterval));
|
|
48
|
+
elapsed += pollInterval;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return { success: false, message: 'Request timed out' };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface SkillToolsContext {
|
|
55
|
+
groupFolder: string;
|
|
56
|
+
isMain: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Create X integration MCP tools
|
|
61
|
+
*/
|
|
62
|
+
export function createXTools(ctx: SkillToolsContext) {
|
|
63
|
+
const { groupFolder, isMain } = ctx;
|
|
64
|
+
|
|
65
|
+
return [
|
|
66
|
+
tool(
|
|
67
|
+
'x_post',
|
|
68
|
+
`Post a tweet to X (Twitter). Main group only.
|
|
69
|
+
|
|
70
|
+
The host machine will execute the browser automation to post the tweet.
|
|
71
|
+
Make sure the content is appropriate and within X's character limit (280 chars for text).`,
|
|
72
|
+
{
|
|
73
|
+
content: z.string().max(280).describe('The tweet content to post (max 280 characters)')
|
|
74
|
+
},
|
|
75
|
+
async (args: { content: string }) => {
|
|
76
|
+
if (!isMain) {
|
|
77
|
+
return {
|
|
78
|
+
content: [{ type: 'text', text: 'Only the main group can post tweets.' }],
|
|
79
|
+
isError: true
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (args.content.length > 280) {
|
|
84
|
+
return {
|
|
85
|
+
content: [{ type: 'text', text: `Tweet exceeds 280 character limit (current: ${args.content.length})` }],
|
|
86
|
+
isError: true
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const requestId = `xpost-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
91
|
+
writeIpcFile(TASKS_DIR, {
|
|
92
|
+
type: 'x_post',
|
|
93
|
+
requestId,
|
|
94
|
+
content: args.content,
|
|
95
|
+
groupFolder,
|
|
96
|
+
timestamp: new Date().toISOString()
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const result = await waitForResult(requestId);
|
|
100
|
+
return {
|
|
101
|
+
content: [{ type: 'text', text: result.message }],
|
|
102
|
+
isError: !result.success
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
),
|
|
106
|
+
|
|
107
|
+
tool(
|
|
108
|
+
'x_like',
|
|
109
|
+
`Like a tweet on X (Twitter). Main group only.
|
|
110
|
+
|
|
111
|
+
Provide the tweet URL or tweet ID to like.`,
|
|
112
|
+
{
|
|
113
|
+
tweet_url: z.string().describe('The tweet URL (e.g., https://x.com/user/status/123) or tweet ID')
|
|
114
|
+
},
|
|
115
|
+
async (args: { tweet_url: string }) => {
|
|
116
|
+
if (!isMain) {
|
|
117
|
+
return {
|
|
118
|
+
content: [{ type: 'text', text: 'Only the main group can interact with X.' }],
|
|
119
|
+
isError: true
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const requestId = `xlike-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
124
|
+
writeIpcFile(TASKS_DIR, {
|
|
125
|
+
type: 'x_like',
|
|
126
|
+
requestId,
|
|
127
|
+
tweetUrl: args.tweet_url,
|
|
128
|
+
groupFolder,
|
|
129
|
+
timestamp: new Date().toISOString()
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const result = await waitForResult(requestId);
|
|
133
|
+
return {
|
|
134
|
+
content: [{ type: 'text', text: result.message }],
|
|
135
|
+
isError: !result.success
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
),
|
|
139
|
+
|
|
140
|
+
tool(
|
|
141
|
+
'x_reply',
|
|
142
|
+
`Reply to a tweet on X (Twitter). Main group only.
|
|
143
|
+
|
|
144
|
+
Provide the tweet URL and your reply content.`,
|
|
145
|
+
{
|
|
146
|
+
tweet_url: z.string().describe('The tweet URL (e.g., https://x.com/user/status/123) or tweet ID'),
|
|
147
|
+
content: z.string().max(280).describe('The reply content (max 280 characters)')
|
|
148
|
+
},
|
|
149
|
+
async (args: { tweet_url: string; content: string }) => {
|
|
150
|
+
if (!isMain) {
|
|
151
|
+
return {
|
|
152
|
+
content: [{ type: 'text', text: 'Only the main group can interact with X.' }],
|
|
153
|
+
isError: true
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const requestId = `xreply-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
158
|
+
writeIpcFile(TASKS_DIR, {
|
|
159
|
+
type: 'x_reply',
|
|
160
|
+
requestId,
|
|
161
|
+
tweetUrl: args.tweet_url,
|
|
162
|
+
content: args.content,
|
|
163
|
+
groupFolder,
|
|
164
|
+
timestamp: new Date().toISOString()
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
const result = await waitForResult(requestId);
|
|
168
|
+
return {
|
|
169
|
+
content: [{ type: 'text', text: result.message }],
|
|
170
|
+
isError: !result.success
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
),
|
|
174
|
+
|
|
175
|
+
tool(
|
|
176
|
+
'x_retweet',
|
|
177
|
+
`Retweet a tweet on X (Twitter). Main group only.
|
|
178
|
+
|
|
179
|
+
Provide the tweet URL to retweet.`,
|
|
180
|
+
{
|
|
181
|
+
tweet_url: z.string().describe('The tweet URL (e.g., https://x.com/user/status/123) or tweet ID')
|
|
182
|
+
},
|
|
183
|
+
async (args: { tweet_url: string }) => {
|
|
184
|
+
if (!isMain) {
|
|
185
|
+
return {
|
|
186
|
+
content: [{ type: 'text', text: 'Only the main group can interact with X.' }],
|
|
187
|
+
isError: true
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const requestId = `xretweet-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
192
|
+
writeIpcFile(TASKS_DIR, {
|
|
193
|
+
type: 'x_retweet',
|
|
194
|
+
requestId,
|
|
195
|
+
tweetUrl: args.tweet_url,
|
|
196
|
+
groupFolder,
|
|
197
|
+
timestamp: new Date().toISOString()
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const result = await waitForResult(requestId);
|
|
201
|
+
return {
|
|
202
|
+
content: [{ type: 'text', text: result.message }],
|
|
203
|
+
isError: !result.success
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
),
|
|
207
|
+
|
|
208
|
+
tool(
|
|
209
|
+
'x_quote',
|
|
210
|
+
`Quote tweet on X (Twitter). Main group only.
|
|
211
|
+
|
|
212
|
+
Retweet with your own comment added.`,
|
|
213
|
+
{
|
|
214
|
+
tweet_url: z.string().describe('The tweet URL (e.g., https://x.com/user/status/123) or tweet ID'),
|
|
215
|
+
comment: z.string().max(280).describe('Your comment for the quote tweet (max 280 characters)')
|
|
216
|
+
},
|
|
217
|
+
async (args: { tweet_url: string; comment: string }) => {
|
|
218
|
+
if (!isMain) {
|
|
219
|
+
return {
|
|
220
|
+
content: [{ type: 'text', text: 'Only the main group can interact with X.' }],
|
|
221
|
+
isError: true
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const requestId = `xquote-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
226
|
+
writeIpcFile(TASKS_DIR, {
|
|
227
|
+
type: 'x_quote',
|
|
228
|
+
requestId,
|
|
229
|
+
tweetUrl: args.tweet_url,
|
|
230
|
+
comment: args.comment,
|
|
231
|
+
groupFolder,
|
|
232
|
+
timestamp: new Date().toISOString()
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const result = await waitForResult(requestId);
|
|
236
|
+
return {
|
|
237
|
+
content: [{ type: 'text', text: result.message }],
|
|
238
|
+
isError: !result.success
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
)
|
|
242
|
+
];
|
|
243
|
+
}
|