@openparachute/agent 0.1.2 → 0.2.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/.parachute/module.json +124 -8
- package/LICENSE +2 -16
- package/README.md +118 -166
- package/package.json +32 -43
- package/scripts/spawn-agent.ts +371 -0
- package/src/_parked/interactive-spawn.test.ts +324 -0
- package/src/_parked/interactive-spawn.ts +701 -0
- package/src/agent-defs.test.ts +1504 -0
- package/src/agent-defs.ts +1702 -0
- package/src/agent-mcp-config.test.ts +115 -0
- package/src/agent-mcp-config.ts +115 -0
- package/src/agents.test.ts +360 -0
- package/src/agents.ts +379 -0
- package/src/auth.test.ts +46 -0
- package/src/auth.ts +140 -0
- package/src/backends/attached-queue.test.ts +376 -0
- package/src/backends/attached-queue.ts +372 -0
- package/src/backends/programmatic.test.ts +1715 -0
- package/src/backends/programmatic.ts +927 -0
- package/src/backends/registry.test.ts +1494 -0
- package/src/backends/registry.ts +1202 -0
- package/src/backends/stream-json.test.ts +570 -0
- package/src/backends/stream-json.ts +392 -0
- package/src/backends/types.ts +223 -0
- package/src/bridge.ts +417 -0
- package/src/channel-backend-wiring.test.ts +237 -0
- package/src/credentials.test.ts +274 -0
- package/src/credentials.ts +380 -0
- package/src/cron.test.ts +342 -0
- package/src/cron.ts +380 -0
- package/src/daemon-agent-def-api.test.ts +166 -0
- package/src/daemon-agent-defs-api.test.ts +953 -0
- package/src/daemon-agent-env-api.test.ts +338 -0
- package/src/daemon-attached-queue-store.test.ts +65 -0
- package/src/daemon-config-api.test.ts +962 -0
- package/src/daemon-jobs-api.test.ts +271 -0
- package/src/daemon-vault-chat.test.ts +250 -0
- package/src/daemon.test.ts +746 -0
- package/src/daemon.ts +3314 -0
- package/src/def-vaults.test.ts +136 -0
- package/src/def-vaults.ts +165 -0
- package/src/delivery-state.test.ts +110 -0
- package/src/delivery-state.ts +154 -0
- package/src/effective-env.test.ts +114 -0
- package/src/effective-env.ts +184 -0
- package/src/env-compat.ts +39 -0
- package/src/grants.test.ts +638 -0
- package/src/grants.ts +675 -0
- package/src/hub-jwt.test.ts +161 -0
- package/src/hub-jwt.ts +182 -0
- package/src/jobs.test.ts +245 -0
- package/src/jobs.ts +266 -0
- package/src/mcp-http.test.ts +265 -0
- package/src/mcp-http.ts +771 -0
- package/src/mint-token.test.ts +152 -0
- package/src/mint-token.ts +139 -0
- package/src/module-manifest.test.ts +158 -0
- package/src/oauth-discovery.ts +134 -0
- package/src/programmatic-wiring.test.ts +838 -0
- package/src/registry.test.ts +227 -0
- package/src/registry.ts +228 -0
- package/src/resolve-port.test.ts +64 -0
- package/src/routing.test.ts +184 -0
- package/src/routing.ts +76 -0
- package/src/runner.test.ts +506 -0
- package/src/runner.ts +255 -0
- package/src/sandbox/config.test.ts +150 -0
- package/src/sandbox/config.ts +102 -0
- package/src/sandbox/egress.test.ts +113 -0
- package/src/sandbox/egress.ts +123 -0
- package/src/sandbox/index.ts +180 -0
- package/src/sandbox/live-seatbelt.test.ts +277 -0
- package/src/sandbox/mounts.test.ts +154 -0
- package/src/sandbox/mounts.ts +133 -0
- package/src/sandbox/sandbox.test.ts +168 -0
- package/src/sandbox/types.ts +382 -0
- package/src/services-manifest.test.ts +106 -0
- package/src/services-manifest.ts +95 -0
- package/src/spa-serve.test.ts +116 -0
- package/src/spa-serve.ts +116 -0
- package/src/spawn-agent-cli.test.ts +172 -0
- package/src/spawn-agent.test.ts +1218 -0
- package/src/spawn-agent.ts +569 -0
- package/src/spawn-deps.test.ts +54 -0
- package/src/spawn-deps.ts +166 -0
- package/src/telegram/api.ts +153 -0
- package/src/terminal-assets.test.ts +50 -0
- package/src/terminal-assets.ts +79 -0
- package/src/terminal-ui.ts +305 -0
- package/src/terminal.test.ts +530 -0
- package/src/terminal.ts +458 -0
- package/src/transport.ts +270 -0
- package/src/transports/http-ui.test.ts +455 -0
- package/src/transports/http-ui.ts +201 -0
- package/src/transports/telegram.test.ts +174 -0
- package/src/transports/telegram.ts +426 -0
- package/src/transports/vault.test.ts +2011 -0
- package/src/transports/vault.ts +1790 -0
- package/src/ui-kit.test.ts +178 -0
- package/src/ui-kit.ts +402 -0
- package/tsconfig.json +8 -14
- package/web/ui/tsconfig.json +2 -1
- package/.claude/scheduled_tasks.lock +0 -1
- package/.claude/settings.json +0 -5
- package/.claude/skills/add-atomic-chat-tool/SKILL.md +0 -243
- package/.claude/skills/add-atomic-chat-tool/atomic-chat-mcp-stdio.ts +0 -229
- package/.claude/skills/add-codex/SKILL.md +0 -161
- package/.claude/skills/add-dashboard/SKILL.md +0 -138
- package/.claude/skills/add-dashboard/resources/dashboard-pusher.ts +0 -495
- package/.claude/skills/add-emacs/SKILL.md +0 -296
- package/.claude/skills/add-gcal-tool/SKILL.md +0 -210
- package/.claude/skills/add-gchat/REMOVE.md +0 -6
- package/.claude/skills/add-gchat/SKILL.md +0 -92
- package/.claude/skills/add-gchat/VERIFY.md +0 -3
- package/.claude/skills/add-github/REMOVE.md +0 -6
- package/.claude/skills/add-github/SKILL.md +0 -148
- package/.claude/skills/add-github/VERIFY.md +0 -3
- package/.claude/skills/add-gmail-tool/SKILL.md +0 -229
- package/.claude/skills/add-imessage/REMOVE.md +0 -6
- package/.claude/skills/add-imessage/SKILL.md +0 -113
- package/.claude/skills/add-imessage/VERIFY.md +0 -3
- package/.claude/skills/add-karpathy-llm-wiki/SKILL.md +0 -110
- package/.claude/skills/add-karpathy-llm-wiki/llm-wiki.md +0 -75
- package/.claude/skills/add-linear/REMOVE.md +0 -6
- package/.claude/skills/add-linear/SKILL.md +0 -168
- package/.claude/skills/add-linear/VERIFY.md +0 -3
- package/.claude/skills/add-macos-statusbar/SKILL.md +0 -133
- package/.claude/skills/add-macos-statusbar/add/src/statusbar.swift +0 -147
- package/.claude/skills/add-matrix/REMOVE.md +0 -6
- package/.claude/skills/add-matrix/SKILL.md +0 -148
- package/.claude/skills/add-matrix/VERIFY.md +0 -3
- package/.claude/skills/add-ollama-provider/SKILL.md +0 -179
- package/.claude/skills/add-ollama-tool/SKILL.md +0 -193
- package/.claude/skills/add-opencode/SKILL.md +0 -229
- package/.claude/skills/add-parallel/SKILL.md +0 -290
- package/.claude/skills/add-resend/REMOVE.md +0 -6
- package/.claude/skills/add-resend/SKILL.md +0 -93
- package/.claude/skills/add-resend/VERIFY.md +0 -3
- package/.claude/skills/add-signal/REMOVE.md +0 -13
- package/.claude/skills/add-signal/SKILL.md +0 -318
- package/.claude/skills/add-signal/VERIFY.md +0 -5
- package/.claude/skills/add-slack/REMOVE.md +0 -6
- package/.claude/skills/add-slack/SKILL.md +0 -112
- package/.claude/skills/add-slack/VERIFY.md +0 -3
- package/.claude/skills/add-teams/REMOVE.md +0 -6
- package/.claude/skills/add-teams/SKILL.md +0 -207
- package/.claude/skills/add-teams/VERIFY.md +0 -3
- package/.claude/skills/add-vercel/SKILL.md +0 -147
- package/.claude/skills/add-vercel/container-skills/vercel-cli/SKILL.md +0 -103
- package/.claude/skills/add-webex/REMOVE.md +0 -6
- package/.claude/skills/add-webex/SKILL.md +0 -88
- package/.claude/skills/add-webex/VERIFY.md +0 -3
- package/.claude/skills/add-wechat/REMOVE.md +0 -49
- package/.claude/skills/add-wechat/SKILL.md +0 -170
- package/.claude/skills/add-wechat/scripts/wire-dm.ts +0 -172
- package/.claude/skills/add-whatsapp/SKILL.md +0 -264
- package/.claude/skills/add-whatsapp-cloud/REMOVE.md +0 -6
- package/.claude/skills/add-whatsapp-cloud/SKILL.md +0 -95
- package/.claude/skills/add-whatsapp-cloud/VERIFY.md +0 -3
- package/.claude/skills/claw/SKILL.md +0 -131
- package/.claude/skills/claw/scripts/claw +0 -374
- package/.claude/skills/convert-to-apple-container/SKILL.md +0 -212
- package/.claude/skills/customize/SKILL.md +0 -110
- package/.claude/skills/debug/SKILL.md +0 -349
- package/.claude/skills/get-qodo-rules/SKILL.md +0 -122
- package/.claude/skills/get-qodo-rules/references/output-format.md +0 -41
- package/.claude/skills/get-qodo-rules/references/pagination.md +0 -33
- package/.claude/skills/get-qodo-rules/references/repository-scope.md +0 -26
- package/.claude/skills/init-first-agent/SKILL.md +0 -120
- package/.claude/skills/init-onecli/SKILL.md +0 -270
- package/.claude/skills/manage-channels/SKILL.md +0 -87
- package/.claude/skills/manage-mounts/SKILL.md +0 -47
- package/.claude/skills/migrate-from-openclaw/MIGRATE_CRONS.md +0 -100
- package/.claude/skills/migrate-from-openclaw/SKILL.md +0 -447
- package/.claude/skills/migrate-from-openclaw/scripts/discover-openclaw.ts +0 -734
- package/.claude/skills/migrate-from-openclaw/scripts/extract-channel-credentials.ts +0 -476
- package/.claude/skills/migrate-nanoclaw/SKILL.md +0 -484
- package/.claude/skills/migrate-nanoclaw/diagnostics.md +0 -51
- package/.claude/skills/qodo-pr-resolver/SKILL.md +0 -326
- package/.claude/skills/qodo-pr-resolver/resources/providers.md +0 -329
- package/.claude/skills/update-nanoclaw/SKILL.md +0 -243
- package/.claude/skills/update-nanoclaw/diagnostics.md +0 -48
- package/.claude/skills/update-skills/SKILL.md +0 -130
- package/.claude/skills/use-native-credential-proxy/SKILL.md +0 -167
- package/.claude/skills/x-integration/SKILL.md +0 -417
- package/.claude/skills/x-integration/agent.ts +0 -243
- package/.claude/skills/x-integration/host.ts +0 -155
- package/.claude/skills/x-integration/lib/browser.ts +0 -148
- package/.claude/skills/x-integration/lib/config.ts +0 -62
- package/.claude/skills/x-integration/scripts/like.ts +0 -56
- package/.claude/skills/x-integration/scripts/post.ts +0 -66
- package/.claude/skills/x-integration/scripts/quote.ts +0 -80
- package/.claude/skills/x-integration/scripts/reply.ts +0 -74
- package/.claude/skills/x-integration/scripts/retweet.ts +0 -62
- package/.claude/skills/x-integration/scripts/setup.ts +0 -87
- package/.github/CODEOWNERS +0 -10
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -18
- package/.github/workflows/bump-version.yml +0 -35
- package/.github/workflows/ci.yml +0 -39
- package/.github/workflows/label-pr.yml +0 -40
- package/.github/workflows/update-tokens.yml +0 -43
- package/.husky/pre-commit +0 -1
- package/.mcp.json +0 -3
- package/.nvmrc +0 -1
- package/.prettierrc +0 -4
- package/CHANGELOG.md +0 -263
- package/CLAUDE.md +0 -307
- package/CODE_OF_CONDUCT.md +0 -128
- package/CONTRIBUTING.md +0 -159
- package/CONTRIBUTORS.md +0 -26
- package/LICENSE-NANOCLAW-MIT +0 -21
- package/README_ja.md +0 -194
- package/README_zh.md +0 -194
- 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 +0 -25
- package/container/.dockerignore +0 -2
- package/container/CLAUDE.md +0 -21
- package/container/Dockerfile +0 -121
- package/container/agent-runner/bun.lock +0 -243
- package/container/agent-runner/package.json +0 -22
- package/container/agent-runner/scripts/sdk-signal-probe.ts +0 -169
- package/container/agent-runner/src/config.ts +0 -55
- package/container/agent-runner/src/db/connection.ts +0 -267
- package/container/agent-runner/src/db/index.ts +0 -20
- package/container/agent-runner/src/db/messages-in.ts +0 -138
- package/container/agent-runner/src/db/messages-out.ts +0 -143
- package/container/agent-runner/src/db/session-routing.ts +0 -30
- package/container/agent-runner/src/db/session-state.test.ts +0 -100
- package/container/agent-runner/src/db/session-state.ts +0 -79
- package/container/agent-runner/src/destinations.ts +0 -135
- package/container/agent-runner/src/formatter.test.ts +0 -167
- package/container/agent-runner/src/formatter.ts +0 -260
- package/container/agent-runner/src/index.ts +0 -110
- package/container/agent-runner/src/integration.test.ts +0 -121
- package/container/agent-runner/src/mcp-tools/agents.instructions.md +0 -26
- package/container/agent-runner/src/mcp-tools/agents.ts +0 -66
- package/container/agent-runner/src/mcp-tools/core.instructions.md +0 -27
- package/container/agent-runner/src/mcp-tools/core.ts +0 -262
- package/container/agent-runner/src/mcp-tools/index.ts +0 -22
- package/container/agent-runner/src/mcp-tools/interactive.instructions.md +0 -22
- package/container/agent-runner/src/mcp-tools/interactive.ts +0 -169
- package/container/agent-runner/src/mcp-tools/scheduling.instructions.md +0 -40
- package/container/agent-runner/src/mcp-tools/scheduling.ts +0 -299
- package/container/agent-runner/src/mcp-tools/self-mod.instructions.md +0 -25
- package/container/agent-runner/src/mcp-tools/self-mod.ts +0 -120
- package/container/agent-runner/src/mcp-tools/server.ts +0 -54
- package/container/agent-runner/src/mcp-tools/types.ts +0 -6
- package/container/agent-runner/src/poll-loop.test.ts +0 -248
- package/container/agent-runner/src/poll-loop.ts +0 -437
- package/container/agent-runner/src/providers/claude.ts +0 -379
- package/container/agent-runner/src/providers/factory.test.ts +0 -19
- package/container/agent-runner/src/providers/factory.ts +0 -13
- package/container/agent-runner/src/providers/index.ts +0 -6
- package/container/agent-runner/src/providers/mock.ts +0 -77
- package/container/agent-runner/src/providers/provider-registry.ts +0 -33
- package/container/agent-runner/src/providers/types.ts +0 -82
- package/container/agent-runner/src/scheduling/task-script.ts +0 -121
- package/container/agent-runner/src/timezone.test.ts +0 -93
- package/container/agent-runner/src/timezone.ts +0 -107
- package/container/agent-runner/tsconfig.json +0 -14
- package/container/build.sh +0 -48
- package/container/entrypoint.sh +0 -16
- package/container/skills/agent-browser/SKILL.md +0 -159
- package/container/skills/frontend-engineer/SKILL.md +0 -157
- package/container/skills/self-customize/SKILL.md +0 -87
- package/container/skills/slack-formatting/SKILL.md +0 -94
- package/container/skills/vercel-cli/SKILL.md +0 -111
- package/container/skills/welcome/SKILL.md +0 -85
- package/docs/APPLE-CONTAINER-NETWORKING.md +0 -90
- package/docs/BRANCH-FORK-MAINTENANCE.md +0 -81
- package/docs/README.md +0 -25
- package/docs/SDK_DEEP_DIVE.md +0 -643
- package/docs/SECURITY.md +0 -162
- package/docs/agent-runner-details.md +0 -749
- package/docs/api-details.md +0 -365
- package/docs/architecture-diagram.html +0 -422
- package/docs/architecture-diagram.md +0 -215
- package/docs/architecture.md +0 -751
- package/docs/audit/2026-04-30-channel-endpoint-audit.md +0 -36
- package/docs/build-and-runtime.md +0 -80
- package/docs/cross-mount-stress/README.md +0 -112
- package/docs/cross-mount-stress/container-writer-retry.mjs +0 -55
- package/docs/cross-mount-stress/container-writer-slow.mjs +0 -42
- package/docs/cross-mount-stress/container-writer.mjs +0 -47
- package/docs/cross-mount-stress/host-writer-retry.mjs +0 -55
- package/docs/cross-mount-stress/host-writer-slow.mjs +0 -43
- package/docs/cross-mount-stress/host-writer.mjs +0 -47
- package/docs/db-central.md +0 -316
- package/docs/db-session.md +0 -183
- package/docs/db.md +0 -119
- package/docs/design/2026-04-29-vault-management-ui.md +0 -231
- package/docs/design/2026-04-30-channel-wiring-rework.md +0 -234
- package/docs/design/2026-05-01-channel-wiring-approvals-deep-dive.md +0 -272
- package/docs/design/2026-05-02-channel-policy-and-approval-routing.md +0 -250
- package/docs/docker-sandboxes.md +0 -359
- package/docs/isolation-model.md +0 -88
- package/docs/ollama.md +0 -79
- package/docs/parachute-integration.md +0 -109
- package/docs/post-night-rebirth-reflections.md +0 -151
- package/eslint.config.js +0 -32
- package/pnpm-workspace.yaml +0 -8
- package/repo-tokens/README.md +0 -113
- package/repo-tokens/action.yml +0 -186
- package/repo-tokens/badge.svg +0 -23
- package/repo-tokens/examples/green.svg +0 -14
- package/repo-tokens/examples/red.svg +0 -14
- package/repo-tokens/examples/yellow-green.svg +0 -14
- package/repo-tokens/examples/yellow.svg +0 -14
- package/scripts/chat.ts +0 -101
- package/scripts/cleanup-sessions.sh +0 -150
- package/scripts/init-cli-agent.ts +0 -172
- package/scripts/init-first-agent.ts +0 -378
- package/scripts/parachute.ts +0 -158
- package/scripts/run-migrations.ts +0 -105
- package/scripts/sanity-live-poll.ts +0 -95
- package/scripts/seed-discord.ts +0 -80
- package/scripts/test-v2-agent.ts +0 -106
- package/scripts/test-v2-channel-e2e.ts +0 -265
- package/scripts/test-v2-host.ts +0 -184
- package/src/channels/adapter.ts +0 -214
- package/src/channels/api-translator.test.ts +0 -306
- package/src/channels/api-translator.ts +0 -214
- package/src/channels/ask-question.ts +0 -46
- package/src/channels/channel-registry.test.ts +0 -421
- package/src/channels/channel-registry.ts +0 -313
- package/src/channels/chat-sdk-bridge.test.ts +0 -84
- package/src/channels/chat-sdk-bridge.ts +0 -652
- package/src/channels/cli.ts +0 -276
- package/src/channels/discord.ts +0 -90
- package/src/channels/index.ts +0 -17
- package/src/channels/telegram-markdown-sanitize.test.ts +0 -78
- package/src/channels/telegram-markdown-sanitize.ts +0 -55
- package/src/channels/telegram-pairing.test.ts +0 -254
- package/src/channels/telegram-pairing.ts +0 -339
- package/src/channels/telegram.ts +0 -279
- package/src/channels/trust-hint.test.ts +0 -48
- package/src/channels/trust-hint.ts +0 -75
- package/src/claude-md-compose.migrate.test.ts +0 -64
- package/src/claude-md-compose.ts +0 -205
- package/src/command-gate.ts +0 -63
- package/src/config.test.ts +0 -93
- package/src/config.ts +0 -128
- package/src/container-config.ts +0 -167
- package/src/container-runner.test.ts +0 -32
- package/src/container-runner.ts +0 -576
- package/src/container-runtime.test.ts +0 -269
- package/src/container-runtime.ts +0 -167
- package/src/db/_bun-sqlite-shim.ts +0 -88
- package/src/db/agent-activity.test.ts +0 -155
- package/src/db/agent-activity.ts +0 -121
- package/src/db/agent-groups.ts +0 -77
- package/src/db/connection.migrate.test.ts +0 -176
- package/src/db/connection.ts +0 -259
- package/src/db/db-v2.test.ts +0 -440
- package/src/db/dropped-messages.ts +0 -44
- package/src/db/index.ts +0 -40
- package/src/db/messaging-groups.ts +0 -252
- package/src/db/migrations/001-initial.ts +0 -112
- package/src/db/migrations/002-chat-sdk-state.ts +0 -36
- package/src/db/migrations/008-dropped-messages.ts +0 -27
- package/src/db/migrations/009-drop-pending-credentials.ts +0 -13
- package/src/db/migrations/010-engage-modes.ts +0 -103
- package/src/db/migrations/011-pending-sender-approvals.ts +0 -40
- package/src/db/migrations/012-channel-registration.ts +0 -48
- package/src/db/migrations/013-approval-render-metadata.ts +0 -27
- package/src/db/migrations/014-secrets.ts +0 -44
- package/src/db/migrations/015-secrets-drop-host-pattern.ts +0 -18
- package/src/db/migrations/016-secret-assignments.ts +0 -30
- package/src/db/migrations/017-agent-activity.ts +0 -40
- package/src/db/migrations/018-oauth-app-configs.ts +0 -34
- package/src/db/migrations/019-oauth-app-connections.ts +0 -48
- package/src/db/migrations/020-agent-app-connections.ts +0 -28
- package/src/db/migrations/021-pending-oauth-states.ts +0 -35
- package/src/db/migrations/022-app-connections-provider.ts +0 -25
- package/src/db/migrations/023-agent-group-secret-mode.test.ts +0 -124
- package/src/db/migrations/023-agent-group-secret-mode.ts +0 -65
- package/src/db/migrations/024-collapse-approvals.test.ts +0 -249
- package/src/db/migrations/024-collapse-approvals.ts +0 -182
- package/src/db/migrations/025-secret-mode-check.test.ts +0 -155
- package/src/db/migrations/025-secret-mode-check.ts +0 -49
- package/src/db/migrations/026-user-dms-bot-id.test.ts +0 -116
- package/src/db/migrations/026-user-dms-bot-id.ts +0 -54
- package/src/db/migrations/027-provider-credentials.ts +0 -41
- package/src/db/migrations/_test-helpers.ts +0 -41
- package/src/db/migrations/index.ts +0 -127
- package/src/db/migrations/module-agent-to-agent-destinations.ts +0 -84
- package/src/db/migrations/module-approvals-pending-approvals.ts +0 -42
- package/src/db/migrations/module-approvals-title-options.ts +0 -40
- package/src/db/schema.ts +0 -258
- package/src/db/session-db.test.ts +0 -93
- package/src/db/session-db.ts +0 -325
- package/src/db/sessions.ts +0 -241
- package/src/delivery.test.ts +0 -148
- package/src/delivery.ts +0 -445
- package/src/env.ts +0 -74
- package/src/group-folder.test.ts +0 -35
- package/src/group-folder.ts +0 -44
- package/src/group-init.ts +0 -92
- package/src/host-core.test.ts +0 -456
- package/src/host-sweep.test.ts +0 -146
- package/src/host-sweep.ts +0 -287
- package/src/index.ts +0 -232
- package/src/install-slug.ts +0 -33
- package/src/log.test.ts +0 -81
- package/src/log.ts +0 -117
- package/src/mcp/http.ts +0 -72
- package/src/mcp/server.ts +0 -92
- package/src/mcp/stdio.ts +0 -51
- package/src/mcp/tools/activity.ts +0 -88
- package/src/mcp/tools/agent-groups.ts +0 -183
- package/src/mcp/tools/approvals.ts +0 -122
- package/src/mcp/tools/channels.test.ts +0 -126
- package/src/mcp/tools/channels.ts +0 -134
- package/src/mcp/tools/index.ts +0 -27
- package/src/mcp/tools/oauth.ts +0 -48
- package/src/mcp/tools/secrets.ts +0 -169
- package/src/mcp/tools/sessions.ts +0 -135
- package/src/mcp/types.ts +0 -51
- package/src/modules/agent-to-agent/agent-route.test.ts +0 -46
- package/src/modules/agent-to-agent/agent-route.ts +0 -223
- package/src/modules/agent-to-agent/create-agent.ts +0 -127
- package/src/modules/agent-to-agent/db/agent-destinations.ts +0 -135
- package/src/modules/agent-to-agent/index.ts +0 -22
- package/src/modules/agent-to-agent/write-destinations.ts +0 -59
- package/src/modules/approvals/agent.md +0 -45
- package/src/modules/approvals/index.ts +0 -21
- package/src/modules/approvals/picks.test.ts +0 -291
- package/src/modules/approvals/primitive.ts +0 -279
- package/src/modules/approvals/project.md +0 -27
- package/src/modules/approvals/response-handler.ts +0 -87
- package/src/modules/index.ts +0 -24
- package/src/modules/interactive/agent.md +0 -21
- package/src/modules/interactive/index.ts +0 -69
- package/src/modules/interactive/project.md +0 -12
- package/src/modules/mount-security/expand-path.test.ts +0 -82
- package/src/modules/mount-security/index.ts +0 -459
- package/src/modules/mount-security/migrate.test.ts +0 -91
- package/src/modules/permissions/access.ts +0 -28
- package/src/modules/permissions/channel-approval.test.ts +0 -389
- package/src/modules/permissions/channel-approval.ts +0 -188
- package/src/modules/permissions/db/agent-group-members.ts +0 -44
- package/src/modules/permissions/db/pending-channel-approvals.test.ts +0 -86
- package/src/modules/permissions/db/pending-channel-approvals.ts +0 -66
- package/src/modules/permissions/db/pending-sender-approvals.ts +0 -60
- package/src/modules/permissions/db/user-dms.ts +0 -58
- package/src/modules/permissions/db/user-roles.ts +0 -85
- package/src/modules/permissions/db/users.ts +0 -38
- package/src/modules/permissions/index.ts +0 -421
- package/src/modules/permissions/permissions.test.ts +0 -358
- package/src/modules/permissions/sender-approval.test.ts +0 -641
- package/src/modules/permissions/sender-approval.ts +0 -165
- package/src/modules/permissions/user-dm.ts +0 -200
- package/src/modules/provider-credentials/db.ts +0 -121
- package/src/modules/provider-credentials/index.ts +0 -12
- package/src/modules/provider-credentials/spawn.test.ts +0 -206
- package/src/modules/provider-credentials/spawn.ts +0 -114
- package/src/modules/scheduling/actions.ts +0 -113
- package/src/modules/scheduling/db.test.ts +0 -282
- package/src/modules/scheduling/db.ts +0 -148
- package/src/modules/scheduling/index.ts +0 -34
- package/src/modules/scheduling/recurrence.test.ts +0 -98
- package/src/modules/scheduling/recurrence.ts +0 -54
- package/src/modules/self-mod/agent.md +0 -30
- package/src/modules/self-mod/apply.ts +0 -85
- package/src/modules/self-mod/index.ts +0 -30
- package/src/modules/self-mod/project.md +0 -39
- package/src/modules/self-mod/request.ts +0 -91
- package/src/modules/typing/index.ts +0 -165
- package/src/oauth/agent-app-connections.ts +0 -103
- package/src/oauth/app-configs.test.ts +0 -64
- package/src/oauth/app-configs.ts +0 -114
- package/src/oauth/app-connections.test.ts +0 -109
- package/src/oauth/app-connections.ts +0 -178
- package/src/oauth/crypto.ts +0 -56
- package/src/oauth/flow.ts +0 -104
- package/src/oauth/providers/google.test.ts +0 -38
- package/src/oauth/providers/google.ts +0 -46
- package/src/oauth/providers/index.ts +0 -48
- package/src/oauth/state-store.test.ts +0 -54
- package/src/oauth/state-store.ts +0 -93
- package/src/parachute/README.md +0 -27
- package/src/parachute/create-agent.test.ts +0 -83
- package/src/parachute/create-agent.ts +0 -122
- package/src/parachute/group-status.test.ts +0 -165
- package/src/parachute/group-status.ts +0 -136
- package/src/parachute/types.ts +0 -41
- package/src/parachute/vault-mcp.test.ts +0 -251
- package/src/parachute/vault-mcp.ts +0 -232
- package/src/platform-id.test.ts +0 -104
- package/src/platform-id.ts +0 -109
- package/src/providers/index.ts +0 -6
- package/src/providers/provider-container-registry.ts +0 -58
- package/src/response-registry.ts +0 -45
- package/src/router.ts +0 -530
- package/src/secrets/crypto.test.ts +0 -45
- package/src/secrets/crypto.ts +0 -55
- package/src/secrets/index.ts +0 -461
- package/src/secrets/master-key.ts +0 -70
- package/src/secrets/secrets.test.ts +0 -651
- package/src/session-manager.attachments.test.ts +0 -171
- package/src/session-manager.dup-skip.test.ts +0 -173
- package/src/session-manager.migrate.test.ts +0 -59
- package/src/session-manager.ts +0 -451
- package/src/startup-bootstrap.test.ts +0 -226
- package/src/startup-bootstrap.ts +0 -207
- package/src/state-sqlite.ts +0 -182
- package/src/timezone.test.ts +0 -64
- package/src/timezone.ts +0 -37
- package/src/types.ts +0 -233
- package/src/web/auth.test.ts +0 -335
- package/src/web/auth.ts +0 -214
- package/src/web/discord-validate.test.ts +0 -77
- package/src/web/discord-validate.ts +0 -88
- package/src/web/hub-discovery.test.ts +0 -98
- package/src/web/hub-discovery.ts +0 -69
- package/src/web/routes/activity.ts +0 -106
- package/src/web/routes/agent-provider.test.ts +0 -282
- package/src/web/routes/agent-provider.ts +0 -309
- package/src/web/routes/approvals.ts +0 -185
- package/src/web/routes/apps.ts +0 -434
- package/src/web/routes/channels-mg-detail.test.ts +0 -324
- package/src/web/routes/channels-mga-detail.test.ts +0 -472
- package/src/web/routes/channels.ts +0 -311
- package/src/web/routes/oauth-providers.ts +0 -42
- package/src/web/routes/secrets.test.ts +0 -220
- package/src/web/routes/secrets.ts +0 -317
- package/src/web/routes/sessions.ts +0 -123
- package/src/web/routes/settings.test.ts +0 -106
- package/src/web/routes/settings.ts +0 -247
- package/src/web/routes/setup-status.ts +0 -205
- package/src/web/routes/vaults.test.ts +0 -389
- package/src/web/routes/vaults.ts +0 -225
- package/src/web/server-version.test.ts +0 -16
- package/src/web/server.ts +0 -1024
- package/src/web/services-manifest.test.ts +0 -148
- package/src/web/services-manifest.ts +0 -66
- package/src/web/static-serve.test.ts +0 -255
- package/src/web/static-serve.ts +0 -104
- package/src/web/telegram-validate.test.ts +0 -116
- package/src/web/telegram-validate.ts +0 -107
- package/src/web/vault-proxy.test.ts +0 -214
- package/src/web/vault-proxy.ts +0 -120
- package/src/web/wire-channel.ts +0 -181
- package/src/webhook-server.ts +0 -134
- package/vitest.config.ts +0 -18
- package/web/README.md +0 -63
- package/web/ui/index.html +0 -13
- package/web/ui/package.json +0 -35
- package/web/ui/pnpm-lock.yaml +0 -2164
- package/web/ui/scripts/verify-base.mjs +0 -31
- package/web/ui/src/App.tsx +0 -88
- package/web/ui/src/components/ActivityFeed.tsx +0 -444
- package/web/ui/src/components/AgentGroupPicker.tsx +0 -263
- package/web/ui/src/components/AgentProviderCards.tsx +0 -220
- package/web/ui/src/components/CredentialForm.tsx +0 -214
- package/web/ui/src/components/ScopeGrants.tsx +0 -74
- package/web/ui/src/components/StatusDot.tsx +0 -43
- package/web/ui/src/components/VaultPicker.tsx +0 -127
- package/web/ui/src/components/setup/AdapterInstallStep.tsx +0 -178
- package/web/ui/src/components/setup/AgentGroupStep.tsx +0 -43
- package/web/ui/src/components/setup/ChannelPickStep.tsx +0 -74
- package/web/ui/src/components/setup/DoneStep.tsx +0 -49
- package/web/ui/src/components/setup/PrereqStep.tsx +0 -129
- package/web/ui/src/components/setup/TestConnectionStep.tsx +0 -108
- package/web/ui/src/components/setup/TestMessageStep.tsx +0 -104
- package/web/ui/src/components/setup/WireChannelStep.tsx +0 -166
- package/web/ui/src/components/setup/types.ts +0 -105
- package/web/ui/src/lib/api.test.ts +0 -410
- package/web/ui/src/lib/api.ts +0 -1248
- package/web/ui/src/lib/auth.test.ts +0 -352
- package/web/ui/src/lib/auth.ts +0 -405
- package/web/ui/src/lib/channel-adapters.ts +0 -136
- package/web/ui/src/main.tsx +0 -19
- package/web/ui/src/routes/ApprovalsList.tsx +0 -294
- package/web/ui/src/routes/Apps.tsx +0 -613
- package/web/ui/src/routes/ChannelWireDetail.test.tsx +0 -233
- package/web/ui/src/routes/ChannelWireDetail.tsx +0 -403
- package/web/ui/src/routes/ChannelsList.tsx +0 -158
- package/web/ui/src/routes/GroupDetail.test.tsx +0 -206
- package/web/ui/src/routes/GroupDetail.tsx +0 -880
- package/web/ui/src/routes/GroupList.tsx +0 -187
- package/web/ui/src/routes/MessagingGroupDetail.test.tsx +0 -233
- package/web/ui/src/routes/MessagingGroupDetail.tsx +0 -306
- package/web/ui/src/routes/NewGroupWizard.tsx +0 -390
- package/web/ui/src/routes/OAuthCallback.tsx +0 -56
- package/web/ui/src/routes/SecretsList.tsx +0 -942
- package/web/ui/src/routes/SessionsList.tsx +0 -220
- package/web/ui/src/routes/SettingsAgentProvider.tsx +0 -109
- package/web/ui/src/routes/SettingsApprovals.tsx +0 -234
- package/web/ui/src/routes/SetupWizard.tsx +0 -219
- package/web/ui/src/routes/VaultDetail.test.tsx +0 -363
- package/web/ui/src/routes/VaultDetail.tsx +0 -960
- package/web/ui/src/routes/VaultsList.tsx +0 -295
- package/web/ui/src/routes/WireChannelPage.tsx +0 -413
- package/web/ui/src/styles.css +0 -608
- package/web/ui/src/test/setup.ts +0 -23
- package/web/ui/src/vite-env.d.ts +0 -10
- package/web/ui/vite.config.ts +0 -34
- package/web/ui/vitest.config.ts +0 -25
|
@@ -1,352 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* auth.ts unit tests. Today's surface is the localStorage / sessionStorage
|
|
3
|
-
* key migration from the paraclaw-era prefix to `parachute-agent.*`. Existing
|
|
4
|
-
* 0.0.x operators have cached discovery + DCR client_id + tokens under the
|
|
5
|
-
* old prefix; without migration their first reload after upgrading to
|
|
6
|
-
* 0.1.0 silently re-runs OAuth + leaves a stale client row on the hub.
|
|
7
|
-
*
|
|
8
|
-
* Tracked in parachute-agent#108. The migration is meant to be called once
|
|
9
|
-
* at SPA bootstrap (in main.tsx) before anything else touches storage.
|
|
10
|
-
*/
|
|
11
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
buildAuthorizeUrl,
|
|
15
|
-
ensureClient,
|
|
16
|
-
migrateLegacyAuthKeys,
|
|
17
|
-
REQUESTED_SCOPES,
|
|
18
|
-
} from './auth.ts';
|
|
19
|
-
|
|
20
|
-
// jsdom's Storage in this vitest config doesn't reliably expose the full
|
|
21
|
-
// Storage prototype methods (the `--localstorage-file` warning at runtime
|
|
22
|
-
// hints the underlying impl is a partial). Install a Map-backed fake on
|
|
23
|
-
// the global so the migration helper sees a real-feeling Storage surface.
|
|
24
|
-
function makeStorageFake(): Storage {
|
|
25
|
-
const m = new Map<string, string>();
|
|
26
|
-
return {
|
|
27
|
-
get length() {
|
|
28
|
-
return m.size;
|
|
29
|
-
},
|
|
30
|
-
key(i: number): string | null {
|
|
31
|
-
return Array.from(m.keys())[i] ?? null;
|
|
32
|
-
},
|
|
33
|
-
getItem(k: string): string | null {
|
|
34
|
-
return m.has(k) ? (m.get(k) as string) : null;
|
|
35
|
-
},
|
|
36
|
-
setItem(k: string, v: string): void {
|
|
37
|
-
m.set(k, String(v));
|
|
38
|
-
},
|
|
39
|
-
removeItem(k: string): void {
|
|
40
|
-
m.delete(k);
|
|
41
|
-
},
|
|
42
|
-
clear(): void {
|
|
43
|
-
m.clear();
|
|
44
|
-
},
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
beforeEach(() => {
|
|
49
|
-
vi.stubGlobal('localStorage', makeStorageFake());
|
|
50
|
-
vi.stubGlobal('sessionStorage', makeStorageFake());
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
describe('migrateLegacyAuthKeys', () => {
|
|
54
|
-
it('migrates the static localStorage keys (discovery + setup wizard)', () => {
|
|
55
|
-
localStorage.setItem('paraclaw.discovery', '{"hubOrigin":"http://hub"}');
|
|
56
|
-
localStorage.setItem('paraclaw.setupWizard.v2', '{"furthestStep":"prereqs"}');
|
|
57
|
-
|
|
58
|
-
migrateLegacyAuthKeys();
|
|
59
|
-
|
|
60
|
-
expect(localStorage.getItem('parachute-agent.discovery')).toBe('{"hubOrigin":"http://hub"}');
|
|
61
|
-
expect(localStorage.getItem('parachute-agent.setupWizard.v2')).toBe('{"furthestStep":"prereqs"}');
|
|
62
|
-
expect(localStorage.getItem('paraclaw.discovery')).toBeNull();
|
|
63
|
-
expect(localStorage.getItem('paraclaw.setupWizard.v2')).toBeNull();
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('migrates the in-flight OAuth flow key from sessionStorage', () => {
|
|
67
|
-
sessionStorage.setItem('paraclaw.flow', '{"verifier":"v","state":"s"}');
|
|
68
|
-
|
|
69
|
-
migrateLegacyAuthKeys();
|
|
70
|
-
|
|
71
|
-
expect(sessionStorage.getItem('parachute-agent.flow')).toBe('{"verifier":"v","state":"s"}');
|
|
72
|
-
expect(sessionStorage.getItem('paraclaw.flow')).toBeNull();
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('migrates per-hub-origin client + tokens keys, preserving the suffix', () => {
|
|
76
|
-
localStorage.setItem('paraclaw.client.http://hub-a', '{"client_id":"a"}');
|
|
77
|
-
localStorage.setItem('paraclaw.client.http://hub-b', '{"client_id":"b"}');
|
|
78
|
-
localStorage.setItem('paraclaw.tokens.http://hub-a', '{"access_token":"t-a"}');
|
|
79
|
-
localStorage.setItem('paraclaw.tokens.http://hub-b', '{"access_token":"t-b"}');
|
|
80
|
-
|
|
81
|
-
migrateLegacyAuthKeys();
|
|
82
|
-
|
|
83
|
-
expect(localStorage.getItem('parachute-agent.client.http://hub-a')).toBe('{"client_id":"a"}');
|
|
84
|
-
expect(localStorage.getItem('parachute-agent.client.http://hub-b')).toBe('{"client_id":"b"}');
|
|
85
|
-
expect(localStorage.getItem('parachute-agent.tokens.http://hub-a')).toBe('{"access_token":"t-a"}');
|
|
86
|
-
expect(localStorage.getItem('parachute-agent.tokens.http://hub-b')).toBe('{"access_token":"t-b"}');
|
|
87
|
-
expect(localStorage.getItem('paraclaw.client.http://hub-a')).toBeNull();
|
|
88
|
-
expect(localStorage.getItem('paraclaw.tokens.http://hub-b')).toBeNull();
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('is a no-op when no legacy keys exist (fresh install)', () => {
|
|
92
|
-
localStorage.setItem('parachute-agent.discovery', '{"hubOrigin":"http://hub"}');
|
|
93
|
-
|
|
94
|
-
migrateLegacyAuthKeys();
|
|
95
|
-
|
|
96
|
-
expect(localStorage.getItem('parachute-agent.discovery')).toBe('{"hubOrigin":"http://hub"}');
|
|
97
|
-
expect(localStorage.length).toBe(1);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('is idempotent — repeated calls leave already-migrated state untouched', () => {
|
|
101
|
-
localStorage.setItem('paraclaw.discovery', '{"hubOrigin":"http://hub"}');
|
|
102
|
-
localStorage.setItem('paraclaw.tokens.http://hub', '{"access_token":"t"}');
|
|
103
|
-
|
|
104
|
-
migrateLegacyAuthKeys();
|
|
105
|
-
migrateLegacyAuthKeys();
|
|
106
|
-
|
|
107
|
-
// Spreading a Storage fake enumerates its method properties, not the
|
|
108
|
-
// stored data — so a snapshot-equality check would pass regardless of
|
|
109
|
-
// behavior. Assert the post-state explicitly: legacy keys gone, new
|
|
110
|
-
// keys hold the original values, and storage holds exactly the two
|
|
111
|
-
// migrated entries (no orphans, no duplicates).
|
|
112
|
-
expect(localStorage.getItem('paraclaw.discovery')).toBeNull();
|
|
113
|
-
expect(localStorage.getItem('paraclaw.tokens.http://hub')).toBeNull();
|
|
114
|
-
expect(localStorage.getItem('parachute-agent.discovery')).toBe('{"hubOrigin":"http://hub"}');
|
|
115
|
-
expect(localStorage.getItem('parachute-agent.tokens.http://hub')).toBe('{"access_token":"t"}');
|
|
116
|
-
expect(localStorage.length).toBe(2);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('drops the stale legacy key when both old and new exist (new wins)', () => {
|
|
120
|
-
// This shape only happens if something wrote the legacy key after a
|
|
121
|
-
// prior migration ran — extremely unlikely in production, but the
|
|
122
|
-
// helper has to be deterministic about which value survives.
|
|
123
|
-
localStorage.setItem('paraclaw.discovery', '{"hubOrigin":"http://stale"}');
|
|
124
|
-
localStorage.setItem('parachute-agent.discovery', '{"hubOrigin":"http://current"}');
|
|
125
|
-
|
|
126
|
-
migrateLegacyAuthKeys();
|
|
127
|
-
|
|
128
|
-
expect(localStorage.getItem('parachute-agent.discovery')).toBe('{"hubOrigin":"http://current"}');
|
|
129
|
-
expect(localStorage.getItem('paraclaw.discovery')).toBeNull();
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('does not touch unrelated paraclaw-prefixed keys outside the OAuth surface', () => {
|
|
133
|
-
// Defensive: if some other module ever uses a `paraclaw.foo` key, the
|
|
134
|
-
// OAuth migration shouldn't sweep it. Only the four documented prefixes
|
|
135
|
-
// and two static keys move.
|
|
136
|
-
localStorage.setItem('paraclaw.unrelated', 'keep-me');
|
|
137
|
-
localStorage.setItem('paraclaw.discovery', '{"hubOrigin":"http://hub"}');
|
|
138
|
-
|
|
139
|
-
migrateLegacyAuthKeys();
|
|
140
|
-
|
|
141
|
-
expect(localStorage.getItem('paraclaw.unrelated')).toBe('keep-me');
|
|
142
|
-
expect(localStorage.getItem('parachute-agent.discovery')).toBe('{"hubOrigin":"http://hub"}');
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Bootstrap-scope narrowing — paraclaw#136. The agent SPA used to request
|
|
148
|
-
* `vault:read vault:write` at bootstrap, but every vault flow already runs
|
|
149
|
-
* the paraclaw#56 re-consent pattern (narrow `vault:<name>:admin` via
|
|
150
|
-
* extraScopes), so the broad bootstrap scopes were dead weight on the
|
|
151
|
-
* consent screen. These tests pin the post-narrowing surface so a future
|
|
152
|
-
* edit can't silently re-add vault scopes to the bootstrap grant.
|
|
153
|
-
*/
|
|
154
|
-
describe('REQUESTED_SCOPES + buildAuthorizeUrl', () => {
|
|
155
|
-
const baseOpts = {
|
|
156
|
-
hubOrigin: 'http://hub.test',
|
|
157
|
-
clientId: 'client-abc',
|
|
158
|
-
redirectUri: 'http://app.test/agent/oauth/callback',
|
|
159
|
-
challenge: 'challenge-xyz',
|
|
160
|
-
state: 'state-123',
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
it('REQUESTED_SCOPES is exactly "agent:admin agent:write" — no vault:* at bootstrap', () => {
|
|
164
|
-
expect(REQUESTED_SCOPES).toBe('agent:admin agent:write');
|
|
165
|
-
expect(REQUESTED_SCOPES).not.toMatch(/vault:/);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
it('builds an authorize URL with only agent:* scopes when extraScopes is empty', () => {
|
|
169
|
-
const u = buildAuthorizeUrl(baseOpts);
|
|
170
|
-
expect(u.searchParams.get('scope')).toBe('agent:admin agent:write');
|
|
171
|
-
// Belt-and-suspenders: the URL string itself must not carry any
|
|
172
|
-
// vault scope, even URL-encoded.
|
|
173
|
-
expect(u.toString()).not.toMatch(/vault(%3A|:)/);
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it('appends a narrow vault:<name>:admin scope when passed in extraScopes', () => {
|
|
177
|
-
const u = buildAuthorizeUrl({ ...baseOpts, extraScopes: ['vault:default:admin'] });
|
|
178
|
-
const scope = u.searchParams.get('scope') ?? '';
|
|
179
|
-
expect(scope.split(' ')).toEqual(['agent:admin', 'agent:write', 'vault:default:admin']);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it('de-dupes extraScopes that are already in REQUESTED_SCOPES', () => {
|
|
183
|
-
const u = buildAuthorizeUrl({
|
|
184
|
-
...baseOpts,
|
|
185
|
-
extraScopes: ['agent:admin', 'vault:foo:admin'],
|
|
186
|
-
});
|
|
187
|
-
const scope = u.searchParams.get('scope') ?? '';
|
|
188
|
-
// agent:admin should appear exactly once even though it was passed
|
|
189
|
-
// again — the consent screen would otherwise show the same scope twice.
|
|
190
|
-
expect(scope.split(' ').filter((s) => s === 'agent:admin')).toHaveLength(1);
|
|
191
|
-
expect(scope.split(' ')).toContain('vault:foo:admin');
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
it('writes the standard PKCE-S256 query params alongside the scope', () => {
|
|
195
|
-
const u = buildAuthorizeUrl(baseOpts);
|
|
196
|
-
expect(u.origin + u.pathname).toBe('http://hub.test/oauth/authorize');
|
|
197
|
-
expect(u.searchParams.get('client_id')).toBe('client-abc');
|
|
198
|
-
expect(u.searchParams.get('redirect_uri')).toBe('http://app.test/agent/oauth/callback');
|
|
199
|
-
expect(u.searchParams.get('response_type')).toBe('code');
|
|
200
|
-
expect(u.searchParams.get('code_challenge')).toBe('challenge-xyz');
|
|
201
|
-
expect(u.searchParams.get('code_challenge_method')).toBe('S256');
|
|
202
|
-
expect(u.searchParams.get('state')).toBe('state-123');
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Regression-pin the OAuth client_name in the `/oauth/register` body —
|
|
208
|
-
* paraclaw#137. The hub's consent screen renders this string verbatim, so
|
|
209
|
-
* it's operator-visible UX, not a free-form internal identifier. The
|
|
210
|
-
* 0.1.0 brand sweep renamed "Paraclaw web UI" → "Parachute Agent web UI"
|
|
211
|
-
* (commit 2a83e77 / PR #112); this test prevents a future rename or copy
|
|
212
|
-
* regression from silently shipping a stale brand on the consent screen.
|
|
213
|
-
*
|
|
214
|
-
* No production behavior change — the existing string literal at line ~166
|
|
215
|
-
* is the only thing this test asserts on.
|
|
216
|
-
*/
|
|
217
|
-
describe('ensureClient — /oauth/register body', () => {
|
|
218
|
-
it('sends client_name "Parachute Agent web UI" on first registration', async () => {
|
|
219
|
-
const fetchSpy = vi.fn().mockResolvedValue({
|
|
220
|
-
ok: true,
|
|
221
|
-
json: async () => ({ client_id: 'returned-client-id' }),
|
|
222
|
-
});
|
|
223
|
-
vi.stubGlobal('fetch', fetchSpy);
|
|
224
|
-
|
|
225
|
-
const id = await ensureClient('http://hub.test');
|
|
226
|
-
|
|
227
|
-
expect(id).toBe('returned-client-id');
|
|
228
|
-
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
|
229
|
-
const [url, init] = fetchSpy.mock.calls[0];
|
|
230
|
-
expect(url).toBe('http://hub.test/oauth/register');
|
|
231
|
-
expect(init.method).toBe('POST');
|
|
232
|
-
const body = JSON.parse(init.body as string);
|
|
233
|
-
expect(body.client_name).toBe('Parachute Agent web UI');
|
|
234
|
-
// Belt: also pin scope + auth method so a future copy edit can't ship
|
|
235
|
-
// a partially-renamed body.
|
|
236
|
-
expect(body.scope).toBe(REQUESTED_SCOPES);
|
|
237
|
-
expect(body.token_endpoint_auth_method).toBe('none');
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
it('reuses the cached client_id when redirect_uri matches the current bootstrap', async () => {
|
|
241
|
-
// Pre-seed a record whose redirect_uri matches what getRedirectUri()
|
|
242
|
-
// computes in this test environment (same logic as the prod helper:
|
|
243
|
-
// origin + BASE_URL + 'oauth/callback'). Cache hit ⇒ no fetch.
|
|
244
|
-
const expectedRedirect = `${window.location.origin}${import.meta.env.BASE_URL}oauth/callback`;
|
|
245
|
-
localStorage.setItem(
|
|
246
|
-
'parachute-agent.client.http://hub.test',
|
|
247
|
-
JSON.stringify({ client_id: 'cached-client-id', redirect_uri: expectedRedirect }),
|
|
248
|
-
);
|
|
249
|
-
const fetchSpy = vi.fn();
|
|
250
|
-
vi.stubGlobal('fetch', fetchSpy);
|
|
251
|
-
|
|
252
|
-
const id = await ensureClient('http://hub.test');
|
|
253
|
-
|
|
254
|
-
expect(id).toBe('cached-client-id');
|
|
255
|
-
expect(fetchSpy).not.toHaveBeenCalled();
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Re-register the OAuth client when the SPA's redirect_uri changes —
|
|
261
|
-
* paraclaw#138. The hub binds each DCR client_id to the redirect_uri it
|
|
262
|
-
* was registered with; if the operator changes the SPA's mount path
|
|
263
|
-
* (e.g. `/claw/` → `/agent/` after the 0.1.0 rename, or any custom
|
|
264
|
-
* `PARACHUTE_AGENT_WEB_MOUNT` change), the cached client_id stops
|
|
265
|
-
* matching and `/oauth/authorize` errors out before the consent screen.
|
|
266
|
-
*
|
|
267
|
-
* Fix: cache the redirect_uri alongside the client_id and treat any
|
|
268
|
-
* mismatch (or a legacy record with no redirect_uri at all) as a cache
|
|
269
|
-
* miss so the SPA registers a fresh client_id under the new path.
|
|
270
|
-
*/
|
|
271
|
-
describe('ensureClient — redirect_uri-aware cache', () => {
|
|
272
|
-
function mockFetchOk(clientId: string) {
|
|
273
|
-
const fetchSpy = vi.fn().mockResolvedValue({
|
|
274
|
-
ok: true,
|
|
275
|
-
json: async () => ({ client_id: clientId }),
|
|
276
|
-
});
|
|
277
|
-
vi.stubGlobal('fetch', fetchSpy);
|
|
278
|
-
return fetchSpy;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
it('re-registers when the cached redirect_uri does not match the current one', async () => {
|
|
282
|
-
// Stale cache from before a mount-path change: redirect_uri points
|
|
283
|
-
// at the old path. Current bootstrap computes a different URI, so
|
|
284
|
-
// the cached client_id is unusable on the hub.
|
|
285
|
-
localStorage.setItem(
|
|
286
|
-
'parachute-agent.client.http://hub.test',
|
|
287
|
-
JSON.stringify({
|
|
288
|
-
client_id: 'stale-client-id',
|
|
289
|
-
redirect_uri: 'http://app.test/claw/oauth/callback',
|
|
290
|
-
}),
|
|
291
|
-
);
|
|
292
|
-
const fetchSpy = mockFetchOk('fresh-client-id');
|
|
293
|
-
|
|
294
|
-
const id = await ensureClient('http://hub.test');
|
|
295
|
-
|
|
296
|
-
expect(id).toBe('fresh-client-id');
|
|
297
|
-
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
|
298
|
-
// The cache must now hold the freshly-registered client_id paired
|
|
299
|
-
// with the *current* redirect_uri, not the stale one — otherwise
|
|
300
|
-
// the next bootstrap would re-register on every page load.
|
|
301
|
-
const updated = JSON.parse(
|
|
302
|
-
localStorage.getItem('parachute-agent.client.http://hub.test') ?? 'null',
|
|
303
|
-
);
|
|
304
|
-
expect(updated.client_id).toBe('fresh-client-id');
|
|
305
|
-
const expectedRedirect = `${window.location.origin}${import.meta.env.BASE_URL}oauth/callback`;
|
|
306
|
-
expect(updated.redirect_uri).toBe(expectedRedirect);
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
it('re-registers a legacy ClientRecord that lacks a redirect_uri field (self-heals)', async () => {
|
|
310
|
-
// Records written before paraclaw#138 had only `{ client_id }`. On
|
|
311
|
-
// the first bootstrap after upgrade we treat the missing-field case
|
|
312
|
-
// as a cache miss and re-register so subsequent loads have the full
|
|
313
|
-
// record. This means existing operators see exactly one extra
|
|
314
|
-
// registration round-trip on first 0.1.x reload.
|
|
315
|
-
localStorage.setItem(
|
|
316
|
-
'parachute-agent.client.http://hub.test',
|
|
317
|
-
JSON.stringify({ client_id: 'legacy-client-id' }),
|
|
318
|
-
);
|
|
319
|
-
const fetchSpy = mockFetchOk('healed-client-id');
|
|
320
|
-
|
|
321
|
-
const id = await ensureClient('http://hub.test');
|
|
322
|
-
|
|
323
|
-
expect(id).toBe('healed-client-id');
|
|
324
|
-
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
|
325
|
-
const healed = JSON.parse(
|
|
326
|
-
localStorage.getItem('parachute-agent.client.http://hub.test') ?? 'null',
|
|
327
|
-
);
|
|
328
|
-
expect(healed.client_id).toBe('healed-client-id');
|
|
329
|
-
expect(typeof healed.redirect_uri).toBe('string');
|
|
330
|
-
expect(healed.redirect_uri.length).toBeGreaterThan(0);
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
it('persists redirect_uri alongside client_id on first registration', async () => {
|
|
334
|
-
// No prior cache. After first registration, the persisted record
|
|
335
|
-
// must include both fields — otherwise subsequent bootstraps would
|
|
336
|
-
// legacy-self-heal on every load and burn a hub /oauth/register
|
|
337
|
-
// call per page reload.
|
|
338
|
-
const fetchSpy = mockFetchOk('first-client-id');
|
|
339
|
-
|
|
340
|
-
await ensureClient('http://hub.test');
|
|
341
|
-
|
|
342
|
-
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
|
343
|
-
const persisted = JSON.parse(
|
|
344
|
-
localStorage.getItem('parachute-agent.client.http://hub.test') ?? 'null',
|
|
345
|
-
);
|
|
346
|
-
expect(persisted).toMatchObject({
|
|
347
|
-
client_id: 'first-client-id',
|
|
348
|
-
});
|
|
349
|
-
const expectedRedirect = `${window.location.origin}${import.meta.env.BASE_URL}oauth/callback`;
|
|
350
|
-
expect(persisted.redirect_uri).toBe(expectedRedirect);
|
|
351
|
-
});
|
|
352
|
-
});
|