durar-ai 2026.4.4
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/CHANGELOG.md +5497 -0
- package/LICENSE +21 -0
- package/README.md +614 -0
- package/assets/avatar-placeholder.svg +19 -0
- package/assets/chrome-extension/icons/icon128.png +0 -0
- package/assets/chrome-extension/icons/icon16.png +0 -0
- package/assets/chrome-extension/icons/icon32.png +0 -0
- package/assets/chrome-extension/icons/icon48.png +0 -0
- package/assets/dmg-background-small.png +0 -0
- package/assets/dmg-background.png +0 -0
- package/docs/.i18n/README.md +72 -0
- package/docs/.i18n/ar-navigation.json +18 -0
- package/docs/.i18n/de-navigation.json +18 -0
- package/docs/.i18n/es-navigation.json +18 -0
- package/docs/.i18n/fr-navigation.json +18 -0
- package/docs/.i18n/glossary.ar.json +5 -0
- package/docs/.i18n/glossary.de.json +5 -0
- package/docs/.i18n/glossary.es.json +5 -0
- package/docs/.i18n/glossary.fr.json +5 -0
- package/docs/.i18n/glossary.id.json +5 -0
- package/docs/.i18n/glossary.it.json +5 -0
- package/docs/.i18n/glossary.ja-JP.json +14 -0
- package/docs/.i18n/glossary.ko.json +5 -0
- package/docs/.i18n/glossary.pl.json +5 -0
- package/docs/.i18n/glossary.pt-BR.json +5 -0
- package/docs/.i18n/glossary.tr.json +5 -0
- package/docs/.i18n/glossary.zh-CN.json +358 -0
- package/docs/.i18n/id-navigation.json +18 -0
- package/docs/.i18n/it-navigation.json +18 -0
- package/docs/.i18n/ja-navigation.json +18 -0
- package/docs/.i18n/ko-navigation.json +18 -0
- package/docs/.i18n/pl-navigation.json +18 -0
- package/docs/.i18n/pt-BR-navigation.json +18 -0
- package/docs/.i18n/tr-navigation.json +18 -0
- package/docs/.i18n/zh-Hans-navigation.json +544 -0
- package/docs/assets/install-script.svg +1 -0
- package/docs/assets/macos-onboarding/01-macos-warning.jpeg +0 -0
- package/docs/assets/macos-onboarding/02-local-networks.jpeg +0 -0
- package/docs/assets/macos-onboarding/03-security-notice.png +0 -0
- package/docs/assets/macos-onboarding/04-choose-gateway.png +0 -0
- package/docs/assets/macos-onboarding/05-permissions.png +0 -0
- package/docs/assets/openclaw-logo-text-dark.png +0 -0
- package/docs/assets/openclaw-logo-text-dark.svg +418 -0
- package/docs/assets/openclaw-logo-text.png +0 -0
- package/docs/assets/openclaw-logo-text.svg +418 -0
- package/docs/assets/pixel-lobster.svg +60 -0
- package/docs/assets/showcase/agents-ui.jpg +0 -0
- package/docs/assets/showcase/bambu-cli.png +0 -0
- package/docs/assets/showcase/codexmonitor.png +0 -0
- package/docs/assets/showcase/gohome-grafana.png +0 -0
- package/docs/assets/showcase/ios-testflight.jpg +0 -0
- package/docs/assets/showcase/oura-health.png +0 -0
- package/docs/assets/showcase/padel-cli.svg +11 -0
- package/docs/assets/showcase/padel-screenshot.jpg +0 -0
- package/docs/assets/showcase/papla-tts.jpg +0 -0
- package/docs/assets/showcase/pr-review-telegram.jpg +0 -0
- package/docs/assets/showcase/roborock-screenshot.jpg +0 -0
- package/docs/assets/showcase/roborock-status.svg +13 -0
- package/docs/assets/showcase/roof-camera-sky.jpg +0 -0
- package/docs/assets/showcase/snag.png +0 -0
- package/docs/assets/showcase/tesco-shop.jpg +0 -0
- package/docs/assets/showcase/wienerlinien.png +0 -0
- package/docs/assets/showcase/wine-cellar-skill.jpg +0 -0
- package/docs/assets/showcase/winix-air-purifier.jpg +0 -0
- package/docs/assets/showcase/xuezh-pronunciation.jpeg +0 -0
- package/docs/assets/sponsors/blacksmith-light.svg +14 -0
- package/docs/assets/sponsors/blacksmith.svg +14 -0
- package/docs/assets/sponsors/convex-light.svg +16 -0
- package/docs/assets/sponsors/convex.svg +16 -0
- package/docs/assets/sponsors/github-light.svg +3 -0
- package/docs/assets/sponsors/github.svg +3 -0
- package/docs/assets/sponsors/nvidia-dark.svg +9 -0
- package/docs/assets/sponsors/nvidia.svg +9 -0
- package/docs/assets/sponsors/openai-light.svg +3 -0
- package/docs/assets/sponsors/openai.svg +3 -0
- package/docs/assets/sponsors/vercel-light.svg +5 -0
- package/docs/assets/sponsors/vercel.svg +5 -0
- package/docs/auth-credential-semantics.md +80 -0
- package/docs/automation/auth-monitoring.md +8 -0
- package/docs/automation/clawflow.md +8 -0
- package/docs/automation/cron-jobs.md +410 -0
- package/docs/automation/cron-vs-heartbeat.md +8 -0
- package/docs/automation/gmail-pubsub.md +8 -0
- package/docs/automation/hooks.md +303 -0
- package/docs/automation/index.md +115 -0
- package/docs/automation/poll.md +8 -0
- package/docs/automation/standing-orders.md +254 -0
- package/docs/automation/taskflow.md +82 -0
- package/docs/automation/tasks.md +323 -0
- package/docs/automation/troubleshooting.md +8 -0
- package/docs/automation/webhook.md +8 -0
- package/docs/brave-search.md +103 -0
- package/docs/channels/bluebubbles.md +435 -0
- package/docs/channels/broadcast-groups.md +442 -0
- package/docs/channels/channel-routing.md +139 -0
- package/docs/channels/discord.md +1254 -0
- package/docs/channels/feishu.md +793 -0
- package/docs/channels/googlechat.md +270 -0
- package/docs/channels/group-messages.md +84 -0
- package/docs/channels/groups.md +410 -0
- package/docs/channels/imessage.md +427 -0
- package/docs/channels/index.md +50 -0
- package/docs/channels/irc.md +252 -0
- package/docs/channels/line.md +225 -0
- package/docs/channels/location.md +56 -0
- package/docs/channels/matrix.md +869 -0
- package/docs/channels/mattermost.md +472 -0
- package/docs/channels/msteams.md +805 -0
- package/docs/channels/nextcloud-talk.md +149 -0
- package/docs/channels/nostr.md +252 -0
- package/docs/channels/pairing.md +129 -0
- package/docs/channels/qqbot.md +193 -0
- package/docs/channels/signal.md +337 -0
- package/docs/channels/slack.md +681 -0
- package/docs/channels/synology-chat.md +185 -0
- package/docs/channels/telegram.md +1072 -0
- package/docs/channels/tlon.md +290 -0
- package/docs/channels/troubleshooting.md +133 -0
- package/docs/channels/twitch.md +394 -0
- package/docs/channels/whatsapp.md +488 -0
- package/docs/channels/zalo.md +254 -0
- package/docs/channels/zalouser.md +195 -0
- package/docs/ci.md +66 -0
- package/docs/cli/acp.md +316 -0
- package/docs/cli/agent.md +57 -0
- package/docs/cli/agents.md +220 -0
- package/docs/cli/approvals.md +136 -0
- package/docs/cli/backup.md +84 -0
- package/docs/cli/browser.md +233 -0
- package/docs/cli/channels.md +131 -0
- package/docs/cli/clawbot.md +21 -0
- package/docs/cli/completion.md +35 -0
- package/docs/cli/config.md +353 -0
- package/docs/cli/configure.md +70 -0
- package/docs/cli/cron.md +167 -0
- package/docs/cli/daemon.md +57 -0
- package/docs/cli/dashboard.md +22 -0
- package/docs/cli/devices.md +171 -0
- package/docs/cli/directory.md +63 -0
- package/docs/cli/dns.md +48 -0
- package/docs/cli/docs.md +28 -0
- package/docs/cli/doctor.md +63 -0
- package/docs/cli/flows.md +18 -0
- package/docs/cli/gateway.md +307 -0
- package/docs/cli/health.md +36 -0
- package/docs/cli/hooks.md +337 -0
- package/docs/cli/index.md +1836 -0
- package/docs/cli/logs.md +59 -0
- package/docs/cli/mcp.md +505 -0
- package/docs/cli/memory.md +139 -0
- package/docs/cli/message.md +300 -0
- package/docs/cli/models.md +136 -0
- package/docs/cli/node.md +137 -0
- package/docs/cli/nodes.md +66 -0
- package/docs/cli/onboard.md +171 -0
- package/docs/cli/pairing.md +65 -0
- package/docs/cli/plugins.md +305 -0
- package/docs/cli/qr.md +52 -0
- package/docs/cli/reset.md +35 -0
- package/docs/cli/sandbox.md +197 -0
- package/docs/cli/secrets.md +197 -0
- package/docs/cli/security.md +86 -0
- package/docs/cli/sessions.md +113 -0
- package/docs/cli/setup.md +45 -0
- package/docs/cli/skills.md +59 -0
- package/docs/cli/status.md +35 -0
- package/docs/cli/system.md +71 -0
- package/docs/cli/tui.md +30 -0
- package/docs/cli/uninstall.md +39 -0
- package/docs/cli/update.md +113 -0
- package/docs/cli/voicecall.md +34 -0
- package/docs/cli/webhooks.md +91 -0
- package/docs/concepts/agent-loop.md +168 -0
- package/docs/concepts/agent-workspace.md +246 -0
- package/docs/concepts/agent.md +129 -0
- package/docs/concepts/architecture.md +156 -0
- package/docs/concepts/compaction.md +122 -0
- package/docs/concepts/context-engine.md +274 -0
- package/docs/concepts/context.md +179 -0
- package/docs/concepts/delegate-architecture.md +307 -0
- package/docs/concepts/dreaming.md +173 -0
- package/docs/concepts/features.md +76 -0
- package/docs/concepts/markdown-formatting.md +130 -0
- package/docs/concepts/memory-builtin.md +105 -0
- package/docs/concepts/memory-honcho.md +140 -0
- package/docs/concepts/memory-qmd.md +163 -0
- package/docs/concepts/memory-search.md +141 -0
- package/docs/concepts/memory.md +121 -0
- package/docs/concepts/messages.md +161 -0
- package/docs/concepts/model-failover.md +349 -0
- package/docs/concepts/model-providers.md +799 -0
- package/docs/concepts/models.md +255 -0
- package/docs/concepts/multi-agent.md +615 -0
- package/docs/concepts/oauth.md +225 -0
- package/docs/concepts/presence.md +102 -0
- package/docs/concepts/queue.md +89 -0
- package/docs/concepts/retry.md +69 -0
- package/docs/concepts/session-pruning.md +92 -0
- package/docs/concepts/session-tool.md +141 -0
- package/docs/concepts/session.md +116 -0
- package/docs/concepts/soul.md +110 -0
- package/docs/concepts/streaming.md +161 -0
- package/docs/concepts/system-prompt.md +182 -0
- package/docs/concepts/timezone.md +97 -0
- package/docs/concepts/typebox.md +307 -0
- package/docs/concepts/typing-indicators.md +69 -0
- package/docs/concepts/usage-tracking.md +59 -0
- package/docs/date-time.md +128 -0
- package/docs/debug/node-issue.md +85 -0
- package/docs/diagnostics/flags.md +91 -0
- package/docs/docs.json +1601 -0
- package/docs/gateway/authentication.md +218 -0
- package/docs/gateway/background-process.md +131 -0
- package/docs/gateway/bonjour.md +179 -0
- package/docs/gateway/bridge-protocol.md +89 -0
- package/docs/gateway/cli-backends.md +310 -0
- package/docs/gateway/configuration-examples.md +631 -0
- package/docs/gateway/configuration-reference.md +3618 -0
- package/docs/gateway/configuration.md +698 -0
- package/docs/gateway/discovery.md +141 -0
- package/docs/gateway/doctor.md +494 -0
- package/docs/gateway/gateway-lock.md +37 -0
- package/docs/gateway/health.md +61 -0
- package/docs/gateway/heartbeat.md +443 -0
- package/docs/gateway/index.md +367 -0
- package/docs/gateway/local-models.md +163 -0
- package/docs/gateway/logging.md +113 -0
- package/docs/gateway/multiple-gateways.md +120 -0
- package/docs/gateway/network-model.md +25 -0
- package/docs/gateway/openai-http-api.md +280 -0
- package/docs/gateway/openresponses-http-api.md +340 -0
- package/docs/gateway/openshell.md +307 -0
- package/docs/gateway/pairing.md +138 -0
- package/docs/gateway/protocol.md +588 -0
- package/docs/gateway/remote-gateway-readme.md +164 -0
- package/docs/gateway/remote.md +251 -0
- package/docs/gateway/sandbox-vs-tool-policy-vs-elevated.md +141 -0
- package/docs/gateway/sandboxing.md +473 -0
- package/docs/gateway/secrets-plan-contract.md +116 -0
- package/docs/gateway/secrets.md +541 -0
- package/docs/gateway/security/index.md +1362 -0
- package/docs/gateway/tailscale.md +136 -0
- package/docs/gateway/tools-invoke-http-api.md +161 -0
- package/docs/gateway/troubleshooting.md +451 -0
- package/docs/gateway/trusted-proxy-auth.md +399 -0
- package/docs/help/debugging.md +168 -0
- package/docs/help/environment.md +165 -0
- package/docs/help/faq.md +3244 -0
- package/docs/help/index.md +28 -0
- package/docs/help/scripts.md +27 -0
- package/docs/help/testing.md +640 -0
- package/docs/help/troubleshooting.md +372 -0
- package/docs/images/configure-model-picker-unsearchable.png +0 -0
- package/docs/images/feishu-step2-create-app.png +0 -0
- package/docs/images/feishu-step3-credentials.png +0 -0
- package/docs/images/feishu-step4-permissions.png +0 -0
- package/docs/images/feishu-step5-bot-capability.png +0 -0
- package/docs/images/feishu-step6-event-subscription.png +0 -0
- package/docs/images/feishu-verification-token.png +0 -0
- package/docs/images/groups-flow.svg +52 -0
- package/docs/images/mobile-ui-screenshot.png +0 -0
- package/docs/index.md +196 -0
- package/docs/install/ansible.md +230 -0
- package/docs/install/azure.md +311 -0
- package/docs/install/bun.md +55 -0
- package/docs/install/clawdock.md +106 -0
- package/docs/install/development-channels.md +131 -0
- package/docs/install/digitalocean.md +129 -0
- package/docs/install/docker-vm-runtime.md +142 -0
- package/docs/install/docker.md +412 -0
- package/docs/install/exe-dev.md +133 -0
- package/docs/install/fly.md +504 -0
- package/docs/install/gcp.md +412 -0
- package/docs/install/hetzner.md +259 -0
- package/docs/install/index.md +212 -0
- package/docs/install/installer.md +443 -0
- package/docs/install/kubernetes.md +192 -0
- package/docs/install/macos-vm.md +281 -0
- package/docs/install/migrating-matrix.md +349 -0
- package/docs/install/migrating.md +112 -0
- package/docs/install/nix.md +89 -0
- package/docs/install/node.md +144 -0
- package/docs/install/northflank.mdx +42 -0
- package/docs/install/oracle.md +158 -0
- package/docs/install/podman.md +210 -0
- package/docs/install/railway.mdx +90 -0
- package/docs/install/raspberry-pi.md +159 -0
- package/docs/install/render.mdx +165 -0
- package/docs/install/uninstall.md +128 -0
- package/docs/install/updating.md +142 -0
- package/docs/logging.md +389 -0
- package/docs/nav-tabs-underline.js +100 -0
- package/docs/network.md +69 -0
- package/docs/nodes/audio.md +191 -0
- package/docs/nodes/camera.md +162 -0
- package/docs/nodes/images.md +73 -0
- package/docs/nodes/index.md +408 -0
- package/docs/nodes/location-command.md +98 -0
- package/docs/nodes/media-understanding.md +432 -0
- package/docs/nodes/talk.md +92 -0
- package/docs/nodes/troubleshooting.md +123 -0
- package/docs/nodes/voicewake.md +66 -0
- package/docs/perplexity.md +181 -0
- package/docs/pi-dev.md +80 -0
- package/docs/pi.md +570 -0
- package/docs/platforms/android.md +244 -0
- package/docs/platforms/digitalocean.md +266 -0
- package/docs/platforms/index.md +55 -0
- package/docs/platforms/ios.md +223 -0
- package/docs/platforms/linux.md +100 -0
- package/docs/platforms/mac/bundled-gateway.md +75 -0
- package/docs/platforms/mac/canvas.md +125 -0
- package/docs/platforms/mac/child-process.md +69 -0
- package/docs/platforms/mac/dev-setup.md +107 -0
- package/docs/platforms/mac/health.md +34 -0
- package/docs/platforms/mac/icon.md +31 -0
- package/docs/platforms/mac/logging.md +57 -0
- package/docs/platforms/mac/menu-bar.md +81 -0
- package/docs/platforms/mac/peekaboo.md +65 -0
- package/docs/platforms/mac/permissions.md +50 -0
- package/docs/platforms/mac/remote.md +84 -0
- package/docs/platforms/mac/signing.md +47 -0
- package/docs/platforms/mac/skills.md +40 -0
- package/docs/platforms/mac/voice-overlay.md +60 -0
- package/docs/platforms/mac/voicewake.md +67 -0
- package/docs/platforms/mac/webchat.md +51 -0
- package/docs/platforms/mac/xpc.md +61 -0
- package/docs/platforms/macos.md +229 -0
- package/docs/platforms/oracle.md +305 -0
- package/docs/platforms/raspberry-pi.md +420 -0
- package/docs/platforms/windows.md +241 -0
- package/docs/plugins/agent-tools.md +10 -0
- package/docs/plugins/architecture.md +1609 -0
- package/docs/plugins/building-extensions.md +10 -0
- package/docs/plugins/building-plugins.md +319 -0
- package/docs/plugins/bundles.md +292 -0
- package/docs/plugins/community.md +149 -0
- package/docs/plugins/manifest.md +412 -0
- package/docs/plugins/sdk-channel-plugins.md +508 -0
- package/docs/plugins/sdk-entrypoints.md +210 -0
- package/docs/plugins/sdk-migration.md +359 -0
- package/docs/plugins/sdk-overview.md +475 -0
- package/docs/plugins/sdk-provider-plugins.md +712 -0
- package/docs/plugins/sdk-runtime.md +381 -0
- package/docs/plugins/sdk-setup.md +516 -0
- package/docs/plugins/sdk-testing.md +263 -0
- package/docs/plugins/voice-call.md +466 -0
- package/docs/plugins/zalouser.md +78 -0
- package/docs/prose.md +134 -0
- package/docs/providers/anthropic.md +402 -0
- package/docs/providers/bedrock-mantle.md +91 -0
- package/docs/providers/bedrock.md +273 -0
- package/docs/providers/chutes.md +103 -0
- package/docs/providers/claude-max-api-proxy.md +163 -0
- package/docs/providers/cloudflare-ai-gateway.md +71 -0
- package/docs/providers/deepgram.md +93 -0
- package/docs/providers/deepseek.md +53 -0
- package/docs/providers/fireworks.md +69 -0
- package/docs/providers/github-copilot.md +80 -0
- package/docs/providers/glm.md +68 -0
- package/docs/providers/google.md +149 -0
- package/docs/providers/groq.md +105 -0
- package/docs/providers/huggingface.md +193 -0
- package/docs/providers/index.md +81 -0
- package/docs/providers/kilocode.md +89 -0
- package/docs/providers/litellm.md +159 -0
- package/docs/providers/minimax.md +281 -0
- package/docs/providers/mistral.md +68 -0
- package/docs/providers/models.md +56 -0
- package/docs/providers/moonshot.md +224 -0
- package/docs/providers/nvidia.md +58 -0
- package/docs/providers/ollama.md +379 -0
- package/docs/providers/openai.md +472 -0
- package/docs/providers/opencode-go.md +45 -0
- package/docs/providers/opencode.md +68 -0
- package/docs/providers/openrouter.md +59 -0
- package/docs/providers/perplexity-provider.md +62 -0
- package/docs/providers/qianfan.md +90 -0
- package/docs/providers/qwen.md +128 -0
- package/docs/providers/qwen_modelstudio.md +137 -0
- package/docs/providers/sglang.md +115 -0
- package/docs/providers/stepfun.md +152 -0
- package/docs/providers/synthetic.md +101 -0
- package/docs/providers/together.md +70 -0
- package/docs/providers/venice.md +282 -0
- package/docs/providers/vercel-ai-gateway.md +60 -0
- package/docs/providers/vllm.md +103 -0
- package/docs/providers/volcengine.md +94 -0
- package/docs/providers/xai.md +94 -0
- package/docs/providers/xiaomi.md +89 -0
- package/docs/providers/zai.md +75 -0
- package/docs/reference/AGENTS.default.md +126 -0
- package/docs/reference/RELEASING.md +138 -0
- package/docs/reference/api-usage-costs.md +198 -0
- package/docs/reference/credits.md +30 -0
- package/docs/reference/device-models.md +47 -0
- package/docs/reference/memory-config.md +421 -0
- package/docs/reference/prompt-caching.md +344 -0
- package/docs/reference/rpc.md +43 -0
- package/docs/reference/secretref-credential-surface.md +148 -0
- package/docs/reference/secretref-user-supplied-credentials-matrix.json +607 -0
- package/docs/reference/session-management-compaction.md +352 -0
- package/docs/reference/templates/AGENTS.dev.md +84 -0
- package/docs/reference/templates/AGENTS.md +219 -0
- package/docs/reference/templates/BOOT.md +12 -0
- package/docs/reference/templates/BOOTSTRAP.md +62 -0
- package/docs/reference/templates/CLAUDE.md +1 -0
- package/docs/reference/templates/HEARTBEAT.md +14 -0
- package/docs/reference/templates/IDENTITY.dev.md +48 -0
- package/docs/reference/templates/IDENTITY.md +30 -0
- package/docs/reference/templates/SOUL.dev.md +77 -0
- package/docs/reference/templates/SOUL.md +45 -0
- package/docs/reference/templates/TOOLS.dev.md +25 -0
- package/docs/reference/templates/TOOLS.md +47 -0
- package/docs/reference/templates/USER.dev.md +19 -0
- package/docs/reference/templates/USER.md +24 -0
- package/docs/reference/test.md +119 -0
- package/docs/reference/token-use.md +197 -0
- package/docs/reference/transcript-hygiene.md +151 -0
- package/docs/reference/wizard.md +245 -0
- package/docs/security/CONTRIBUTING-THREAT-MODEL.md +98 -0
- package/docs/security/THREAT-MODEL-ATLAS.md +608 -0
- package/docs/security/formal-verification.md +167 -0
- package/docs/snippets/plugin-publish/minimal-openclaw.plugin.json +9 -0
- package/docs/snippets/plugin-publish/minimal-package.json +16 -0
- package/docs/start/bootstrapping.md +41 -0
- package/docs/start/docs-directory.md +67 -0
- package/docs/start/getting-started.md +148 -0
- package/docs/start/hubs.md +199 -0
- package/docs/start/lore.md +219 -0
- package/docs/start/onboarding-overview.md +69 -0
- package/docs/start/onboarding.md +92 -0
- package/docs/start/openclaw.md +225 -0
- package/docs/start/quickstart.md +22 -0
- package/docs/start/setup.md +172 -0
- package/docs/start/showcase.md +418 -0
- package/docs/start/wizard-cli-automation.md +233 -0
- package/docs/start/wizard-cli-reference.md +324 -0
- package/docs/start/wizard.md +127 -0
- package/docs/style.css +37 -0
- package/docs/tools/acp-agents.md +837 -0
- package/docs/tools/agent-send.md +100 -0
- package/docs/tools/apply-patch.md +52 -0
- package/docs/tools/brave-search.md +107 -0
- package/docs/tools/browser-linux-troubleshooting.md +145 -0
- package/docs/tools/browser-login.md +73 -0
- package/docs/tools/browser-wsl2-windows-remote-cdp-troubleshooting.md +221 -0
- package/docs/tools/browser.md +890 -0
- package/docs/tools/btw.md +142 -0
- package/docs/tools/capability-cookbook.md +119 -0
- package/docs/tools/clawhub.md +348 -0
- package/docs/tools/code-execution.md +90 -0
- package/docs/tools/creating-skills.md +119 -0
- package/docs/tools/diffs.md +434 -0
- package/docs/tools/duckduckgo-search.md +102 -0
- package/docs/tools/elevated.md +116 -0
- package/docs/tools/exa-search.md +127 -0
- package/docs/tools/exec-approvals.md +635 -0
- package/docs/tools/exec.md +237 -0
- package/docs/tools/firecrawl.md +147 -0
- package/docs/tools/gemini-search.md +98 -0
- package/docs/tools/grok-search.md +102 -0
- package/docs/tools/image-generation.md +139 -0
- package/docs/tools/index.md +174 -0
- package/docs/tools/kimi-search.md +98 -0
- package/docs/tools/llm-task.md +119 -0
- package/docs/tools/lobster.md +348 -0
- package/docs/tools/loop-detection.md +100 -0
- package/docs/tools/minimax-search.md +99 -0
- package/docs/tools/multi-agent-sandbox-tools.md +373 -0
- package/docs/tools/ollama-search.md +100 -0
- package/docs/tools/pdf.md +176 -0
- package/docs/tools/perplexity-search.md +185 -0
- package/docs/tools/plugin.md +348 -0
- package/docs/tools/reactions.md +78 -0
- package/docs/tools/searxng-search.md +132 -0
- package/docs/tools/skills-config.md +133 -0
- package/docs/tools/skills.md +377 -0
- package/docs/tools/slash-commands.md +322 -0
- package/docs/tools/subagents.md +341 -0
- package/docs/tools/tavily.md +129 -0
- package/docs/tools/thinking.md +102 -0
- package/docs/tools/tts.md +452 -0
- package/docs/tools/web-fetch.md +159 -0
- package/docs/tools/web.md +417 -0
- package/docs/tts.md +452 -0
- package/docs/vps.md +115 -0
- package/docs/web/control-ui.md +318 -0
- package/docs/web/dashboard.md +93 -0
- package/docs/web/index.md +126 -0
- package/docs/web/tui.md +176 -0
- package/docs/web/webchat.md +77 -0
- package/docs/whatsapp-openclaw-ai-zh.jpg +0 -0
- package/docs/whatsapp-openclaw.jpg +0 -0
- package/durar.mjs +180 -0
- package/package.json +1259 -0
- package/scripts/npm-runner.mjs +111 -0
- package/scripts/postinstall-bundled-plugins.mjs +188 -0
- package/skills/1password/SKILL.md +70 -0
- package/skills/1password/references/cli-examples.md +29 -0
- package/skills/1password/references/get-started.md +17 -0
- package/skills/apple-notes/SKILL.md +77 -0
- package/skills/apple-reminders/SKILL.md +118 -0
- package/skills/bear-notes/SKILL.md +107 -0
- package/skills/blogwatcher/SKILL.md +69 -0
- package/skills/blucli/SKILL.md +47 -0
- package/skills/bluebubbles/SKILL.md +131 -0
- package/skills/camsnap/SKILL.md +45 -0
- package/skills/canvas/SKILL.md +199 -0
- package/skills/clawhub/SKILL.md +77 -0
- package/skills/coding-agent/SKILL.md +316 -0
- package/skills/discord/SKILL.md +197 -0
- package/skills/eightctl/SKILL.md +50 -0
- package/skills/gemini/SKILL.md +43 -0
- package/skills/gh-issues/SKILL.md +885 -0
- package/skills/gifgrep/SKILL.md +79 -0
- package/skills/github/SKILL.md +163 -0
- package/skills/gog/SKILL.md +116 -0
- package/skills/goplaces/SKILL.md +52 -0
- package/skills/healthcheck/SKILL.md +245 -0
- package/skills/himalaya/SKILL.md +257 -0
- package/skills/himalaya/references/configuration.md +184 -0
- package/skills/himalaya/references/message-composition.md +199 -0
- package/skills/imsg/SKILL.md +122 -0
- package/skills/mcporter/SKILL.md +61 -0
- package/skills/model-usage/SKILL.md +69 -0
- package/skills/model-usage/references/codexbar-cli.md +33 -0
- package/skills/model-usage/scripts/model_usage.py +320 -0
- package/skills/model-usage/scripts/test_model_usage.py +40 -0
- package/skills/nano-pdf/SKILL.md +38 -0
- package/skills/node-connect/SKILL.md +142 -0
- package/skills/notion/SKILL.md +174 -0
- package/skills/obsidian/SKILL.md +81 -0
- package/skills/openai-whisper/SKILL.md +38 -0
- package/skills/openai-whisper-api/SKILL.md +62 -0
- package/skills/openai-whisper-api/scripts/transcribe.sh +88 -0
- package/skills/openhue/SKILL.md +112 -0
- package/skills/oracle/SKILL.md +125 -0
- package/skills/ordercli/SKILL.md +78 -0
- package/skills/peekaboo/SKILL.md +190 -0
- package/skills/sag/SKILL.md +87 -0
- package/skills/session-logs/SKILL.md +151 -0
- package/skills/sherpa-onnx-tts/SKILL.md +109 -0
- package/skills/sherpa-onnx-tts/bin/sherpa-onnx-tts +178 -0
- package/skills/skill-creator/SKILL.md +372 -0
- package/skills/skill-creator/license.txt +202 -0
- package/skills/skill-creator/scripts/init_skill.py +378 -0
- package/skills/skill-creator/scripts/package_skill.py +139 -0
- package/skills/skill-creator/scripts/quick_validate.py +159 -0
- package/skills/skill-creator/scripts/test_package_skill.py +160 -0
- package/skills/skill-creator/scripts/test_quick_validate.py +72 -0
- package/skills/slack/SKILL.md +144 -0
- package/skills/songsee/SKILL.md +49 -0
- package/skills/sonoscli/SKILL.md +65 -0
- package/skills/spotify-player/SKILL.md +64 -0
- package/skills/summarize/SKILL.md +87 -0
- package/skills/taskflow/SKILL.md +149 -0
- package/skills/taskflow/examples/inbox-triage.lobster +33 -0
- package/skills/taskflow/examples/pr-intake.lobster +32 -0
- package/skills/taskflow-inbox-triage/SKILL.md +119 -0
- package/skills/things-mac/SKILL.md +86 -0
- package/skills/tmux/SKILL.md +170 -0
- package/skills/tmux/scripts/find-sessions.sh +112 -0
- package/skills/tmux/scripts/wait-for-text.sh +83 -0
- package/skills/trello/SKILL.md +108 -0
- package/skills/video-frames/SKILL.md +46 -0
- package/skills/video-frames/scripts/frame.sh +81 -0
- package/skills/voice-call/SKILL.md +45 -0
- package/skills/wacli/SKILL.md +72 -0
- package/skills/weather/SKILL.md +129 -0
- package/skills/xurl/SKILL.md +461 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Skill Packager - Creates a distributable .skill file of a skill folder
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
python utils/package_skill.py <path/to/skill-folder> [output-directory]
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
python utils/package_skill.py skills/public/my-skill
|
|
10
|
+
python utils/package_skill.py skills/public/my-skill ./dist
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import sys
|
|
14
|
+
import zipfile
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
from quick_validate import validate_skill
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _is_within(path: Path, root: Path) -> bool:
|
|
21
|
+
try:
|
|
22
|
+
path.relative_to(root)
|
|
23
|
+
return True
|
|
24
|
+
except ValueError:
|
|
25
|
+
return False
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def package_skill(skill_path, output_dir=None):
|
|
29
|
+
"""
|
|
30
|
+
Package a skill folder into a .skill file.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
skill_path: Path to the skill folder
|
|
34
|
+
output_dir: Optional output directory for the .skill file (defaults to current directory)
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Path to the created .skill file, or None if error
|
|
38
|
+
"""
|
|
39
|
+
skill_path = Path(skill_path).resolve()
|
|
40
|
+
|
|
41
|
+
# Validate skill folder exists
|
|
42
|
+
if not skill_path.exists():
|
|
43
|
+
print(f"[ERROR] Skill folder not found: {skill_path}")
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
if not skill_path.is_dir():
|
|
47
|
+
print(f"[ERROR] Path is not a directory: {skill_path}")
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
# Validate SKILL.md exists
|
|
51
|
+
skill_md = skill_path / "SKILL.md"
|
|
52
|
+
if not skill_md.exists():
|
|
53
|
+
print(f"[ERROR] SKILL.md not found in {skill_path}")
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
# Run validation before packaging
|
|
57
|
+
print("Validating skill...")
|
|
58
|
+
valid, message = validate_skill(skill_path)
|
|
59
|
+
if not valid:
|
|
60
|
+
print(f"[ERROR] Validation failed: {message}")
|
|
61
|
+
print(" Please fix the validation errors before packaging.")
|
|
62
|
+
return None
|
|
63
|
+
print(f"[OK] {message}\n")
|
|
64
|
+
|
|
65
|
+
# Determine output location
|
|
66
|
+
skill_name = skill_path.name
|
|
67
|
+
if output_dir:
|
|
68
|
+
output_path = Path(output_dir).resolve()
|
|
69
|
+
output_path.mkdir(parents=True, exist_ok=True)
|
|
70
|
+
else:
|
|
71
|
+
output_path = Path.cwd()
|
|
72
|
+
|
|
73
|
+
skill_filename = output_path / f"{skill_name}.skill"
|
|
74
|
+
|
|
75
|
+
EXCLUDED_DIRS = {".git", ".svn", ".hg", "__pycache__", "node_modules"}
|
|
76
|
+
|
|
77
|
+
# Create the .skill file (zip format)
|
|
78
|
+
try:
|
|
79
|
+
with zipfile.ZipFile(skill_filename, "w", zipfile.ZIP_DEFLATED) as zipf:
|
|
80
|
+
# Walk through the skill directory
|
|
81
|
+
for file_path in skill_path.rglob("*"):
|
|
82
|
+
# Security: never follow or package symlinks.
|
|
83
|
+
if file_path.is_symlink():
|
|
84
|
+
print(f"[WARN] Skipping symlink: {file_path}")
|
|
85
|
+
continue
|
|
86
|
+
|
|
87
|
+
rel_parts = file_path.relative_to(skill_path).parts
|
|
88
|
+
if any(part in EXCLUDED_DIRS for part in rel_parts):
|
|
89
|
+
continue
|
|
90
|
+
|
|
91
|
+
if file_path.is_file():
|
|
92
|
+
resolved_file = file_path.resolve()
|
|
93
|
+
if not _is_within(resolved_file, skill_path):
|
|
94
|
+
print(f"[ERROR] File escapes skill root: {file_path}")
|
|
95
|
+
return None
|
|
96
|
+
# If output lives under skill_path, avoid writing archive into itself.
|
|
97
|
+
if resolved_file == skill_filename.resolve():
|
|
98
|
+
print(f"[WARN] Skipping output archive: {file_path}")
|
|
99
|
+
continue
|
|
100
|
+
|
|
101
|
+
# Calculate the relative path within the zip.
|
|
102
|
+
arcname = Path(skill_name) / file_path.relative_to(skill_path)
|
|
103
|
+
zipf.write(file_path, arcname)
|
|
104
|
+
print(f" Added: {arcname}")
|
|
105
|
+
|
|
106
|
+
print(f"\n[OK] Successfully packaged skill to: {skill_filename}")
|
|
107
|
+
return skill_filename
|
|
108
|
+
|
|
109
|
+
except Exception as e:
|
|
110
|
+
print(f"[ERROR] Error creating .skill file: {e}")
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def main():
|
|
115
|
+
if len(sys.argv) < 2:
|
|
116
|
+
print("Usage: python utils/package_skill.py <path/to/skill-folder> [output-directory]")
|
|
117
|
+
print("\nExample:")
|
|
118
|
+
print(" python utils/package_skill.py skills/public/my-skill")
|
|
119
|
+
print(" python utils/package_skill.py skills/public/my-skill ./dist")
|
|
120
|
+
sys.exit(1)
|
|
121
|
+
|
|
122
|
+
skill_path = sys.argv[1]
|
|
123
|
+
output_dir = sys.argv[2] if len(sys.argv) > 2 else None
|
|
124
|
+
|
|
125
|
+
print(f"Packaging skill: {skill_path}")
|
|
126
|
+
if output_dir:
|
|
127
|
+
print(f" Output directory: {output_dir}")
|
|
128
|
+
print()
|
|
129
|
+
|
|
130
|
+
result = package_skill(skill_path, output_dir)
|
|
131
|
+
|
|
132
|
+
if result:
|
|
133
|
+
sys.exit(0)
|
|
134
|
+
else:
|
|
135
|
+
sys.exit(1)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
if __name__ == "__main__":
|
|
139
|
+
main()
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Quick validation script for skills - minimal version
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import re
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
import yaml
|
|
13
|
+
except ModuleNotFoundError:
|
|
14
|
+
yaml = None
|
|
15
|
+
|
|
16
|
+
MAX_SKILL_NAME_LENGTH = 64
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _extract_frontmatter(content: str) -> Optional[str]:
|
|
20
|
+
lines = content.splitlines()
|
|
21
|
+
if not lines or lines[0].strip() != "---":
|
|
22
|
+
return None
|
|
23
|
+
for i in range(1, len(lines)):
|
|
24
|
+
if lines[i].strip() == "---":
|
|
25
|
+
return "\n".join(lines[1:i])
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _parse_simple_frontmatter(frontmatter_text: str) -> Optional[dict[str, str]]:
|
|
30
|
+
"""
|
|
31
|
+
Minimal fallback parser used when PyYAML is unavailable.
|
|
32
|
+
Supports simple `key: value` mappings used by SKILL.md frontmatter.
|
|
33
|
+
"""
|
|
34
|
+
parsed: dict[str, str] = {}
|
|
35
|
+
current_key: Optional[str] = None
|
|
36
|
+
for raw_line in frontmatter_text.splitlines():
|
|
37
|
+
stripped = raw_line.strip()
|
|
38
|
+
if not stripped or stripped.startswith("#"):
|
|
39
|
+
continue
|
|
40
|
+
|
|
41
|
+
is_indented = raw_line[:1].isspace()
|
|
42
|
+
if is_indented:
|
|
43
|
+
if current_key is None:
|
|
44
|
+
return None
|
|
45
|
+
current_value = parsed[current_key]
|
|
46
|
+
parsed[current_key] = (
|
|
47
|
+
f"{current_value}\n{stripped}" if current_value else stripped
|
|
48
|
+
)
|
|
49
|
+
continue
|
|
50
|
+
|
|
51
|
+
if ":" not in stripped:
|
|
52
|
+
return None
|
|
53
|
+
key, value = stripped.split(":", 1)
|
|
54
|
+
key = key.strip()
|
|
55
|
+
value = value.strip()
|
|
56
|
+
if not key:
|
|
57
|
+
return None
|
|
58
|
+
if (value.startswith('"') and value.endswith('"')) or (
|
|
59
|
+
value.startswith("'") and value.endswith("'")
|
|
60
|
+
):
|
|
61
|
+
value = value[1:-1]
|
|
62
|
+
parsed[key] = value
|
|
63
|
+
current_key = key
|
|
64
|
+
return parsed
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def validate_skill(skill_path):
|
|
68
|
+
"""Basic validation of a skill"""
|
|
69
|
+
skill_path = Path(skill_path)
|
|
70
|
+
|
|
71
|
+
skill_md = skill_path / "SKILL.md"
|
|
72
|
+
if not skill_md.exists():
|
|
73
|
+
return False, "SKILL.md not found"
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
content = skill_md.read_text(encoding="utf-8")
|
|
77
|
+
except OSError as e:
|
|
78
|
+
return False, f"Could not read SKILL.md: {e}"
|
|
79
|
+
|
|
80
|
+
frontmatter_text = _extract_frontmatter(content)
|
|
81
|
+
if frontmatter_text is None:
|
|
82
|
+
return False, "Invalid frontmatter format"
|
|
83
|
+
if yaml is not None:
|
|
84
|
+
try:
|
|
85
|
+
frontmatter = yaml.safe_load(frontmatter_text)
|
|
86
|
+
if not isinstance(frontmatter, dict):
|
|
87
|
+
return False, "Frontmatter must be a YAML dictionary"
|
|
88
|
+
except yaml.YAMLError as e:
|
|
89
|
+
return False, f"Invalid YAML in frontmatter: {e}"
|
|
90
|
+
else:
|
|
91
|
+
frontmatter = _parse_simple_frontmatter(frontmatter_text)
|
|
92
|
+
if frontmatter is None:
|
|
93
|
+
return (
|
|
94
|
+
False,
|
|
95
|
+
"Invalid YAML in frontmatter: unsupported syntax without PyYAML installed",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
allowed_properties = {"name", "description", "license", "allowed-tools", "metadata"}
|
|
99
|
+
|
|
100
|
+
unexpected_keys = set(frontmatter.keys()) - allowed_properties
|
|
101
|
+
if unexpected_keys:
|
|
102
|
+
allowed = ", ".join(sorted(allowed_properties))
|
|
103
|
+
unexpected = ", ".join(sorted(unexpected_keys))
|
|
104
|
+
return (
|
|
105
|
+
False,
|
|
106
|
+
f"Unexpected key(s) in SKILL.md frontmatter: {unexpected}. Allowed properties are: {allowed}",
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
if "name" not in frontmatter:
|
|
110
|
+
return False, "Missing 'name' in frontmatter"
|
|
111
|
+
if "description" not in frontmatter:
|
|
112
|
+
return False, "Missing 'description' in frontmatter"
|
|
113
|
+
|
|
114
|
+
name = frontmatter.get("name", "")
|
|
115
|
+
if not isinstance(name, str):
|
|
116
|
+
return False, f"Name must be a string, got {type(name).__name__}"
|
|
117
|
+
name = name.strip()
|
|
118
|
+
if name:
|
|
119
|
+
if not re.match(r"^[a-z0-9-]+$", name):
|
|
120
|
+
return (
|
|
121
|
+
False,
|
|
122
|
+
f"Name '{name}' should be hyphen-case (lowercase letters, digits, and hyphens only)",
|
|
123
|
+
)
|
|
124
|
+
if name.startswith("-") or name.endswith("-") or "--" in name:
|
|
125
|
+
return (
|
|
126
|
+
False,
|
|
127
|
+
f"Name '{name}' cannot start/end with hyphen or contain consecutive hyphens",
|
|
128
|
+
)
|
|
129
|
+
if len(name) > MAX_SKILL_NAME_LENGTH:
|
|
130
|
+
return (
|
|
131
|
+
False,
|
|
132
|
+
f"Name is too long ({len(name)} characters). "
|
|
133
|
+
f"Maximum is {MAX_SKILL_NAME_LENGTH} characters.",
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
description = frontmatter.get("description", "")
|
|
137
|
+
if not isinstance(description, str):
|
|
138
|
+
return False, f"Description must be a string, got {type(description).__name__}"
|
|
139
|
+
description = description.strip()
|
|
140
|
+
if description:
|
|
141
|
+
if "<" in description or ">" in description:
|
|
142
|
+
return False, "Description cannot contain angle brackets (< or >)"
|
|
143
|
+
if len(description) > 1024:
|
|
144
|
+
return (
|
|
145
|
+
False,
|
|
146
|
+
f"Description is too long ({len(description)} characters). Maximum is 1024 characters.",
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
return True, "Skill is valid!"
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
if __name__ == "__main__":
|
|
153
|
+
if len(sys.argv) != 2:
|
|
154
|
+
print("Usage: python quick_validate.py <skill_directory>")
|
|
155
|
+
sys.exit(1)
|
|
156
|
+
|
|
157
|
+
valid, message = validate_skill(sys.argv[1])
|
|
158
|
+
print(message)
|
|
159
|
+
sys.exit(0 if valid else 1)
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Regression tests for skill packaging security behavior.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import sys
|
|
7
|
+
import tempfile
|
|
8
|
+
import types
|
|
9
|
+
import zipfile
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from unittest import TestCase, main
|
|
12
|
+
from unittest.mock import patch
|
|
13
|
+
|
|
14
|
+
SCRIPT_DIR = Path(__file__).resolve().parent
|
|
15
|
+
if str(SCRIPT_DIR) not in sys.path:
|
|
16
|
+
sys.path.insert(0, str(SCRIPT_DIR))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
fake_quick_validate = types.ModuleType("quick_validate")
|
|
20
|
+
fake_quick_validate.validate_skill = lambda _path: (True, "Skill is valid!")
|
|
21
|
+
original_quick_validate = sys.modules.get("quick_validate")
|
|
22
|
+
sys.modules["quick_validate"] = fake_quick_validate
|
|
23
|
+
|
|
24
|
+
import package_skill as package_skill_module
|
|
25
|
+
from package_skill import package_skill
|
|
26
|
+
|
|
27
|
+
if original_quick_validate is not None:
|
|
28
|
+
sys.modules["quick_validate"] = original_quick_validate
|
|
29
|
+
else:
|
|
30
|
+
sys.modules.pop("quick_validate", None)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class TestPackageSkillSecurity(TestCase):
|
|
34
|
+
def setUp(self):
|
|
35
|
+
self.temp_dir = Path(tempfile.mkdtemp(prefix="test_skill_"))
|
|
36
|
+
|
|
37
|
+
def tearDown(self):
|
|
38
|
+
import shutil
|
|
39
|
+
|
|
40
|
+
if self.temp_dir.exists():
|
|
41
|
+
shutil.rmtree(self.temp_dir)
|
|
42
|
+
|
|
43
|
+
def create_skill(self, name="test-skill"):
|
|
44
|
+
skill_dir = self.temp_dir / name
|
|
45
|
+
skill_dir.mkdir(parents=True, exist_ok=True)
|
|
46
|
+
(skill_dir / "SKILL.md").write_text("---\nname: test-skill\ndescription: test\n---\n")
|
|
47
|
+
(skill_dir / "script.py").write_text("print('ok')\n")
|
|
48
|
+
return skill_dir
|
|
49
|
+
|
|
50
|
+
def test_packages_normal_files(self):
|
|
51
|
+
skill_dir = self.create_skill("normal-skill")
|
|
52
|
+
out_dir = self.temp_dir / "out"
|
|
53
|
+
out_dir.mkdir()
|
|
54
|
+
|
|
55
|
+
result = package_skill(str(skill_dir), str(out_dir))
|
|
56
|
+
|
|
57
|
+
self.assertIsNotNone(result)
|
|
58
|
+
skill_file = out_dir / "normal-skill.skill"
|
|
59
|
+
self.assertTrue(skill_file.exists())
|
|
60
|
+
with zipfile.ZipFile(skill_file, "r") as archive:
|
|
61
|
+
names = set(archive.namelist())
|
|
62
|
+
self.assertIn("normal-skill/SKILL.md", names)
|
|
63
|
+
self.assertIn("normal-skill/script.py", names)
|
|
64
|
+
|
|
65
|
+
def test_skips_symlink_to_external_file(self):
|
|
66
|
+
skill_dir = self.create_skill("symlink-file-skill")
|
|
67
|
+
outside = self.temp_dir / "outside-secret.txt"
|
|
68
|
+
outside.write_text("super-secret\n")
|
|
69
|
+
link = skill_dir / "loot.txt"
|
|
70
|
+
out_dir = self.temp_dir / "out"
|
|
71
|
+
out_dir.mkdir()
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
link.symlink_to(outside)
|
|
75
|
+
except (OSError, NotImplementedError):
|
|
76
|
+
self.skipTest("symlink unsupported on this platform")
|
|
77
|
+
|
|
78
|
+
result = package_skill(str(skill_dir), str(out_dir))
|
|
79
|
+
self.assertIsNotNone(result)
|
|
80
|
+
skill_file = out_dir / "symlink-file-skill.skill"
|
|
81
|
+
self.assertTrue(skill_file.exists())
|
|
82
|
+
with zipfile.ZipFile(skill_file, "r") as archive:
|
|
83
|
+
names = set(archive.namelist())
|
|
84
|
+
self.assertIn("symlink-file-skill/SKILL.md", names)
|
|
85
|
+
self.assertIn("symlink-file-skill/script.py", names)
|
|
86
|
+
self.assertNotIn("symlink-file-skill/loot.txt", names)
|
|
87
|
+
|
|
88
|
+
def test_skips_symlink_directory(self):
|
|
89
|
+
skill_dir = self.create_skill("symlink-dir-skill")
|
|
90
|
+
outside_dir = self.temp_dir / "outside"
|
|
91
|
+
outside_dir.mkdir()
|
|
92
|
+
(outside_dir / "secret.txt").write_text("secret\n")
|
|
93
|
+
link = skill_dir / "docs"
|
|
94
|
+
out_dir = self.temp_dir / "out"
|
|
95
|
+
out_dir.mkdir()
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
link.symlink_to(outside_dir, target_is_directory=True)
|
|
99
|
+
except (OSError, NotImplementedError):
|
|
100
|
+
self.skipTest("symlink unsupported on this platform")
|
|
101
|
+
|
|
102
|
+
result = package_skill(str(skill_dir), str(out_dir))
|
|
103
|
+
self.assertIsNotNone(result)
|
|
104
|
+
skill_file = out_dir / "symlink-dir-skill.skill"
|
|
105
|
+
with zipfile.ZipFile(skill_file, "r") as archive:
|
|
106
|
+
names = set(archive.namelist())
|
|
107
|
+
self.assertIn("symlink-dir-skill/SKILL.md", names)
|
|
108
|
+
self.assertIn("symlink-dir-skill/script.py", names)
|
|
109
|
+
self.assertNotIn("symlink-dir-skill/docs/secret.txt", names)
|
|
110
|
+
|
|
111
|
+
def test_rejects_resolved_path_outside_skill_root(self):
|
|
112
|
+
skill_dir = self.create_skill("escape-skill")
|
|
113
|
+
out_dir = self.temp_dir / "out"
|
|
114
|
+
out_dir.mkdir()
|
|
115
|
+
|
|
116
|
+
original_within = package_skill_module._is_within
|
|
117
|
+
|
|
118
|
+
def fake_is_within(path_obj: Path, root: Path):
|
|
119
|
+
if path_obj.name == "script.py":
|
|
120
|
+
return False
|
|
121
|
+
return original_within(path_obj, root)
|
|
122
|
+
|
|
123
|
+
with patch.object(package_skill_module, "_is_within", fake_is_within):
|
|
124
|
+
result = package_skill(str(skill_dir), str(out_dir))
|
|
125
|
+
|
|
126
|
+
self.assertIsNone(result)
|
|
127
|
+
|
|
128
|
+
def test_allows_nested_regular_files(self):
|
|
129
|
+
skill_dir = self.create_skill("nested-skill")
|
|
130
|
+
nested = skill_dir / "lib" / "helpers"
|
|
131
|
+
nested.mkdir(parents=True, exist_ok=True)
|
|
132
|
+
(nested / "util.py").write_text("def run():\n return 1\n")
|
|
133
|
+
out_dir = self.temp_dir / "out"
|
|
134
|
+
out_dir.mkdir()
|
|
135
|
+
|
|
136
|
+
result = package_skill(str(skill_dir), str(out_dir))
|
|
137
|
+
|
|
138
|
+
self.assertIsNotNone(result)
|
|
139
|
+
skill_file = out_dir / "nested-skill.skill"
|
|
140
|
+
with zipfile.ZipFile(skill_file, "r") as archive:
|
|
141
|
+
names = set(archive.namelist())
|
|
142
|
+
self.assertIn("nested-skill/lib/helpers/util.py", names)
|
|
143
|
+
|
|
144
|
+
def test_skips_output_archive_when_output_dir_is_skill_dir(self):
|
|
145
|
+
skill_dir = self.create_skill("self-output-skill")
|
|
146
|
+
|
|
147
|
+
result = package_skill(str(skill_dir), str(skill_dir))
|
|
148
|
+
|
|
149
|
+
self.assertIsNotNone(result)
|
|
150
|
+
skill_file = skill_dir / "self-output-skill.skill"
|
|
151
|
+
self.assertTrue(skill_file.exists())
|
|
152
|
+
with zipfile.ZipFile(skill_file, "r") as archive:
|
|
153
|
+
names = set(archive.namelist())
|
|
154
|
+
self.assertIn("self-output-skill/SKILL.md", names)
|
|
155
|
+
self.assertIn("self-output-skill/script.py", names)
|
|
156
|
+
self.assertNotIn("self-output-skill/self-output-skill.skill", names)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
if __name__ == "__main__":
|
|
160
|
+
main()
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Regression tests for quick skill validation.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import tempfile
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from unittest import TestCase, main
|
|
9
|
+
|
|
10
|
+
import quick_validate
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestQuickValidate(TestCase):
|
|
14
|
+
def setUp(self):
|
|
15
|
+
self.temp_dir = Path(tempfile.mkdtemp(prefix="test_quick_validate_"))
|
|
16
|
+
|
|
17
|
+
def tearDown(self):
|
|
18
|
+
import shutil
|
|
19
|
+
|
|
20
|
+
if self.temp_dir.exists():
|
|
21
|
+
shutil.rmtree(self.temp_dir)
|
|
22
|
+
|
|
23
|
+
def test_accepts_crlf_frontmatter(self):
|
|
24
|
+
skill_dir = self.temp_dir / "crlf-skill"
|
|
25
|
+
skill_dir.mkdir(parents=True, exist_ok=True)
|
|
26
|
+
content = "---\r\nname: crlf-skill\r\ndescription: ok\r\n---\r\n# Skill\r\n"
|
|
27
|
+
(skill_dir / "SKILL.md").write_text(content, encoding="utf-8")
|
|
28
|
+
|
|
29
|
+
valid, message = quick_validate.validate_skill(skill_dir)
|
|
30
|
+
|
|
31
|
+
self.assertTrue(valid, message)
|
|
32
|
+
|
|
33
|
+
def test_rejects_missing_frontmatter_closing_fence(self):
|
|
34
|
+
skill_dir = self.temp_dir / "bad-skill"
|
|
35
|
+
skill_dir.mkdir(parents=True, exist_ok=True)
|
|
36
|
+
content = "---\nname: bad-skill\ndescription: missing end\n# no closing fence\n"
|
|
37
|
+
(skill_dir / "SKILL.md").write_text(content, encoding="utf-8")
|
|
38
|
+
|
|
39
|
+
valid, message = quick_validate.validate_skill(skill_dir)
|
|
40
|
+
|
|
41
|
+
self.assertFalse(valid)
|
|
42
|
+
self.assertEqual(message, "Invalid frontmatter format")
|
|
43
|
+
|
|
44
|
+
def test_fallback_parser_handles_multiline_frontmatter_without_pyyaml(self):
|
|
45
|
+
skill_dir = self.temp_dir / "multiline-skill"
|
|
46
|
+
skill_dir.mkdir(parents=True, exist_ok=True)
|
|
47
|
+
content = """---
|
|
48
|
+
name: multiline-skill
|
|
49
|
+
description: Works without pyyaml
|
|
50
|
+
allowed-tools:
|
|
51
|
+
- gh
|
|
52
|
+
metadata: |
|
|
53
|
+
{
|
|
54
|
+
"owners": ["team-openclaw"]
|
|
55
|
+
}
|
|
56
|
+
---
|
|
57
|
+
# Skill
|
|
58
|
+
"""
|
|
59
|
+
(skill_dir / "SKILL.md").write_text(content, encoding="utf-8")
|
|
60
|
+
|
|
61
|
+
previous_yaml = quick_validate.yaml
|
|
62
|
+
quick_validate.yaml = None
|
|
63
|
+
try:
|
|
64
|
+
valid, message = quick_validate.validate_skill(skill_dir)
|
|
65
|
+
finally:
|
|
66
|
+
quick_validate.yaml = previous_yaml
|
|
67
|
+
|
|
68
|
+
self.assertTrue(valid, message)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
if __name__ == "__main__":
|
|
72
|
+
main()
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: slack
|
|
3
|
+
description: Use when you need to control Slack from OpenClaw via the slack tool, including reacting to messages or pinning/unpinning items in Slack channels or DMs.
|
|
4
|
+
metadata: { "openclaw": { "emoji": "💬", "requires": { "config": ["channels.slack"] } } }
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Slack Actions
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
Use `slack` to react, manage pins, send/edit/delete messages, and fetch member info. The tool uses the bot token configured for OpenClaw.
|
|
12
|
+
|
|
13
|
+
## Inputs to collect
|
|
14
|
+
|
|
15
|
+
- `channelId` and `messageId` (Slack message timestamp, e.g. `1712023032.1234`).
|
|
16
|
+
- For reactions, an `emoji` (Unicode or `:name:`).
|
|
17
|
+
- For message sends, a `to` target (`channel:<id>` or `user:<id>`) and `content`.
|
|
18
|
+
|
|
19
|
+
Message context lines include `slack message id` and `channel` fields you can reuse directly.
|
|
20
|
+
|
|
21
|
+
## Actions
|
|
22
|
+
|
|
23
|
+
### Action groups
|
|
24
|
+
|
|
25
|
+
| Action group | Default | Notes |
|
|
26
|
+
| ------------ | ------- | ---------------------- |
|
|
27
|
+
| reactions | enabled | React + list reactions |
|
|
28
|
+
| messages | enabled | Read/send/edit/delete |
|
|
29
|
+
| pins | enabled | Pin/unpin/list |
|
|
30
|
+
| memberInfo | enabled | Member info |
|
|
31
|
+
| emojiList | enabled | Custom emoji list |
|
|
32
|
+
|
|
33
|
+
### React to a message
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"action": "react",
|
|
38
|
+
"channelId": "C123",
|
|
39
|
+
"messageId": "1712023032.1234",
|
|
40
|
+
"emoji": "✅"
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### List reactions
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"action": "reactions",
|
|
49
|
+
"channelId": "C123",
|
|
50
|
+
"messageId": "1712023032.1234"
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Send a message
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"action": "sendMessage",
|
|
59
|
+
"to": "channel:C123",
|
|
60
|
+
"content": "Hello from OpenClaw"
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Edit a message
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"action": "editMessage",
|
|
69
|
+
"channelId": "C123",
|
|
70
|
+
"messageId": "1712023032.1234",
|
|
71
|
+
"content": "Updated text"
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Delete a message
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"action": "deleteMessage",
|
|
80
|
+
"channelId": "C123",
|
|
81
|
+
"messageId": "1712023032.1234"
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Read recent messages
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"action": "readMessages",
|
|
90
|
+
"channelId": "C123",
|
|
91
|
+
"limit": 20
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Pin a message
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"action": "pinMessage",
|
|
100
|
+
"channelId": "C123",
|
|
101
|
+
"messageId": "1712023032.1234"
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Unpin a message
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"action": "unpinMessage",
|
|
110
|
+
"channelId": "C123",
|
|
111
|
+
"messageId": "1712023032.1234"
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### List pinned items
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"action": "listPins",
|
|
120
|
+
"channelId": "C123"
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Member info
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"action": "memberInfo",
|
|
129
|
+
"userId": "U123"
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Emoji list
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"action": "emojiList"
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Ideas to try
|
|
142
|
+
|
|
143
|
+
- React with ✅ to mark completed tasks.
|
|
144
|
+
- Pin key decisions or weekly status updates.
|