@openparachute/agent 0.1.1 → 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 -221
- 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 -171
- package/scripts/init-first-agent.ts +0 -377
- 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 -79
- 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/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 -108
- 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 -169
- package/src/container-runtime.ts +0 -92
- 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 -143
- package/src/db/connection.ts +0 -224
- 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 -227
- 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.ts +0 -199
- 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/index.ts +0 -448
- 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 -470
- 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 -355
- package/src/secrets/master-key.ts +0 -70
- package/src/secrets/secrets.test.ts +0 -354
- package/src/session-manager.migrate.test.ts +0 -59
- package/src/session-manager.ts +0 -433
- 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 -230
- 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 -425
- package/src/web/routes/channels.ts +0 -489
- package/src/web/routes/oauth-providers.ts +0 -42
- package/src/web/routes/secrets.test.ts +0 -175
- package/src/web/routes/secrets.ts +0 -282
- 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 -1003
- package/src/web/services-manifest.test.ts +0 -120
- package/src/web/services-manifest.ts +0 -61
- 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 -1210
- package/web/ui/src/lib/auth.test.ts +0 -139
- package/web/ui/src/lib/auth.ts +0 -348
- 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.tsx +0 -755
- 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 -921
- 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 -361
- 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
package/src/secrets/index.ts
DELETED
|
@@ -1,355 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Public API for paraclaw's secret store. Values are AES-256-GCM encrypted
|
|
3
|
-
* in-process before landing in the central DB; decrypted only when injected
|
|
4
|
-
* into per-session containers (`src/container-runner.ts`).
|
|
5
|
-
*
|
|
6
|
-
* Naming: a secret is keyed by `(name, agent_group_id)`. A NULL agent_group_id
|
|
7
|
-
* is global; a non-NULL agent_group_id scopes the secret to that group only.
|
|
8
|
-
*
|
|
9
|
-
* Resolution preference at injection time: agent-scoped secret with that
|
|
10
|
-
* name beats the global one. The host walks both rows and the scoped wins.
|
|
11
|
-
*
|
|
12
|
-
* Injection policy lives on the recipient `agent_groups.secret_mode` row
|
|
13
|
-
* (migration 023): `all` injects every in-scope secret; `selective` injects
|
|
14
|
-
* only those with an explicit `secret_assignments` row pointing to the group.
|
|
15
|
-
*/
|
|
16
|
-
import crypto from 'crypto';
|
|
17
|
-
|
|
18
|
-
import { getDb } from '../db/connection.js';
|
|
19
|
-
import type { Database } from '../db/connection.js';
|
|
20
|
-
import { decryptSecret, deriveKey, encryptSecret } from './crypto.js';
|
|
21
|
-
import { loadOrCreateMasterKey } from './master-key.js';
|
|
22
|
-
|
|
23
|
-
// Domain tag for HKDF-derived secrets-store key. Bumping the version (v2…)
|
|
24
|
-
// would force re-encryption of every row in this table. See crypto.ts.
|
|
25
|
-
//
|
|
26
|
-
// ⚠ The `paraclaw.` prefix is a cryptographic domain separator and must
|
|
27
|
-
// stay frozen across the paraclaw → parachute-agent rename. Renaming it
|
|
28
|
-
// changes the derived key and renders every existing ciphertext row
|
|
29
|
-
// undecryptable. The brand-sweep documentation lives in commit messages
|
|
30
|
-
// and CHANGELOG; the bytes here do not change.
|
|
31
|
-
const SECRETS_INFO = 'paraclaw.secrets.v1';
|
|
32
|
-
|
|
33
|
-
function secretsKey(): Buffer {
|
|
34
|
-
return deriveKey(loadOrCreateMasterKey(), SECRETS_INFO);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export type SecretKind = 'channel-token' | 'api-key' | 'generic';
|
|
38
|
-
export type AssignedMode = 'all' | 'selective';
|
|
39
|
-
|
|
40
|
-
export interface SecretRow {
|
|
41
|
-
id: string;
|
|
42
|
-
name: string;
|
|
43
|
-
kind: SecretKind;
|
|
44
|
-
agent_group_id: string | null;
|
|
45
|
-
created_at: string;
|
|
46
|
-
updated_at: string;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export interface PutSecretOpts {
|
|
50
|
-
kind?: SecretKind;
|
|
51
|
-
agent_group_id?: string | null;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
interface RawRow extends SecretRow {
|
|
55
|
-
value_encrypted: string;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function db(): Database {
|
|
59
|
-
return getDb();
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function nowIso(): string {
|
|
63
|
-
return new Date().toISOString();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** Insert or update a secret. Returns the row's id. */
|
|
67
|
-
export function putSecret(name: string, value: string, opts: PutSecretOpts = {}): string {
|
|
68
|
-
const key = secretsKey();
|
|
69
|
-
const ct = encryptSecret(value, key);
|
|
70
|
-
const agentGroupId = opts.agent_group_id ?? null;
|
|
71
|
-
const kind = opts.kind ?? 'generic';
|
|
72
|
-
|
|
73
|
-
const existing = db()
|
|
74
|
-
.prepare<{ id: string }>(`SELECT id FROM secrets WHERE name = @name AND agent_group_id IS @agent_group_id`)
|
|
75
|
-
.get({ name, agent_group_id: agentGroupId });
|
|
76
|
-
|
|
77
|
-
const now = nowIso();
|
|
78
|
-
if (existing) {
|
|
79
|
-
db()
|
|
80
|
-
.prepare(
|
|
81
|
-
`UPDATE secrets
|
|
82
|
-
SET value_encrypted = @value_encrypted,
|
|
83
|
-
kind = @kind,
|
|
84
|
-
updated_at = @updated_at
|
|
85
|
-
WHERE id = @id`,
|
|
86
|
-
)
|
|
87
|
-
.run({
|
|
88
|
-
id: existing.id,
|
|
89
|
-
value_encrypted: ct,
|
|
90
|
-
kind,
|
|
91
|
-
updated_at: now,
|
|
92
|
-
});
|
|
93
|
-
return existing.id;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const id = crypto.randomUUID();
|
|
97
|
-
db()
|
|
98
|
-
.prepare(
|
|
99
|
-
`INSERT INTO secrets
|
|
100
|
-
(id, name, value_encrypted, kind, agent_group_id, created_at, updated_at)
|
|
101
|
-
VALUES
|
|
102
|
-
(@id, @name, @value_encrypted, @kind, @agent_group_id, @created_at, @updated_at)`,
|
|
103
|
-
)
|
|
104
|
-
.run({
|
|
105
|
-
id,
|
|
106
|
-
name,
|
|
107
|
-
value_encrypted: ct,
|
|
108
|
-
kind,
|
|
109
|
-
agent_group_id: agentGroupId,
|
|
110
|
-
created_at: now,
|
|
111
|
-
updated_at: now,
|
|
112
|
-
});
|
|
113
|
-
return id;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Decrypt and return a secret's plaintext value. Returns undefined if the
|
|
118
|
-
* named secret does not exist for the given scope. Resolution: an
|
|
119
|
-
* agent-scoped secret beats a global one with the same name.
|
|
120
|
-
*/
|
|
121
|
-
export function getSecret(name: string, agentGroupId?: string | null): string | undefined {
|
|
122
|
-
const key = secretsKey();
|
|
123
|
-
const scoped = agentGroupId
|
|
124
|
-
? db()
|
|
125
|
-
.prepare<RawRow>(`SELECT * FROM secrets WHERE name = @name AND agent_group_id = @agent_group_id`)
|
|
126
|
-
.get({ name, agent_group_id: agentGroupId })
|
|
127
|
-
: undefined;
|
|
128
|
-
const row =
|
|
129
|
-
scoped ?? db().prepare<RawRow>(`SELECT * FROM secrets WHERE name = @name AND agent_group_id IS NULL`).get({ name });
|
|
130
|
-
if (!row) return undefined;
|
|
131
|
-
return decryptSecret(row.value_encrypted, key);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/** Names + metadata only — never decrypts. */
|
|
135
|
-
export function listSecrets(agentGroupId?: string | null): SecretRow[] {
|
|
136
|
-
if (agentGroupId === undefined) {
|
|
137
|
-
return db()
|
|
138
|
-
.prepare<SecretRow>(
|
|
139
|
-
`SELECT id, name, kind, agent_group_id, created_at, updated_at
|
|
140
|
-
FROM secrets ORDER BY name`,
|
|
141
|
-
)
|
|
142
|
-
.all();
|
|
143
|
-
}
|
|
144
|
-
if (agentGroupId === null) {
|
|
145
|
-
return db()
|
|
146
|
-
.prepare<SecretRow>(
|
|
147
|
-
`SELECT id, name, kind, agent_group_id, created_at, updated_at
|
|
148
|
-
FROM secrets WHERE agent_group_id IS NULL ORDER BY name`,
|
|
149
|
-
)
|
|
150
|
-
.all();
|
|
151
|
-
}
|
|
152
|
-
return db()
|
|
153
|
-
.prepare<SecretRow>(
|
|
154
|
-
`SELECT id, name, kind, agent_group_id, created_at, updated_at
|
|
155
|
-
FROM secrets
|
|
156
|
-
WHERE agent_group_id = @agent_group_id OR agent_group_id IS NULL
|
|
157
|
-
ORDER BY name`,
|
|
158
|
-
)
|
|
159
|
-
.all({ agent_group_id: agentGroupId });
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export function deleteSecret(id: string): boolean {
|
|
163
|
-
const r = db().prepare(`DELETE FROM secrets WHERE id = @id`).run({ id });
|
|
164
|
-
return r.changes > 0;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Resolve the secrets that should be injected into a session for the given
|
|
169
|
-
* agent group. Returns plaintext values; callers are expected to inject as
|
|
170
|
-
* env vars and never log. Agent-scoped wins over global on name collision.
|
|
171
|
-
*
|
|
172
|
-
* Mode lives on the recipient `agent_groups.secret_mode`:
|
|
173
|
-
* - `all` — inject every in-scope secret (scoped + globals).
|
|
174
|
-
* - `selective` — inject only those with an explicit assignment row
|
|
175
|
-
* pointing to this group. Lets operators stage credentials
|
|
176
|
-
* in the store before any agent gets them and revoke per
|
|
177
|
-
* agent without rotating the value.
|
|
178
|
-
*
|
|
179
|
-
* Unknown agent_group_id is treated as `selective` (the safe default) — the
|
|
180
|
-
* group-level row is the source of truth, so a missing row means we err on
|
|
181
|
-
* the side of withholding.
|
|
182
|
-
*/
|
|
183
|
-
export function resolveInjectableSecrets(agentGroupId: string): Map<string, string> {
|
|
184
|
-
const key = secretsKey();
|
|
185
|
-
const rows = db()
|
|
186
|
-
.prepare<RawRow>(
|
|
187
|
-
`SELECT s.*
|
|
188
|
-
FROM secrets s
|
|
189
|
-
LEFT JOIN secret_assignments a
|
|
190
|
-
ON a.secret_id = s.id
|
|
191
|
-
AND a.agent_group_id = @agent_group_id
|
|
192
|
-
LEFT JOIN agent_groups g
|
|
193
|
-
ON g.id = @agent_group_id
|
|
194
|
-
WHERE (s.agent_group_id = @agent_group_id OR s.agent_group_id IS NULL)
|
|
195
|
-
AND (g.secret_mode = 'all' OR a.secret_id IS NOT NULL)
|
|
196
|
-
ORDER BY s.agent_group_id IS NULL`,
|
|
197
|
-
)
|
|
198
|
-
.all({ agent_group_id: agentGroupId });
|
|
199
|
-
|
|
200
|
-
const out = new Map<string, string>();
|
|
201
|
-
for (const row of rows) {
|
|
202
|
-
if (out.has(row.name)) continue;
|
|
203
|
-
out.set(row.name, decryptSecret(row.value_encrypted, key));
|
|
204
|
-
}
|
|
205
|
-
return out;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// ── Assignments ──
|
|
209
|
-
|
|
210
|
-
export interface SecretAssignment {
|
|
211
|
-
secret_id: string;
|
|
212
|
-
agent_group_id: string;
|
|
213
|
-
created_at: string;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/** All agent_group_ids assigned to this secret (selective-mode allowlist). */
|
|
217
|
-
export function listAssignments(secretId: string): string[] {
|
|
218
|
-
const rows = db()
|
|
219
|
-
.prepare<{ agent_group_id: string }>(
|
|
220
|
-
`SELECT agent_group_id FROM secret_assignments
|
|
221
|
-
WHERE secret_id = @secret_id
|
|
222
|
-
ORDER BY agent_group_id`,
|
|
223
|
-
)
|
|
224
|
-
.all({ secret_id: secretId });
|
|
225
|
-
return rows.map((r) => r.agent_group_id);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Replace the assignment set atomically. Empty array = revoke everything.
|
|
230
|
-
* Throws if the secret doesn't exist; FK ON DELETE CASCADE handles agent
|
|
231
|
-
* groups that vanish. The whole replace runs inside one transaction so
|
|
232
|
-
* the UI's "save" button is all-or-nothing.
|
|
233
|
-
*/
|
|
234
|
-
export function replaceAssignments(secretId: string, agentGroupIds: string[]): void {
|
|
235
|
-
const exists = db().prepare<{ id: string }>(`SELECT id FROM secrets WHERE id = @id`).get({ id: secretId });
|
|
236
|
-
if (!exists) throw new Error(`secret not found: ${secretId}`);
|
|
237
|
-
const now = nowIso();
|
|
238
|
-
db().transaction(() => {
|
|
239
|
-
db().prepare(`DELETE FROM secret_assignments WHERE secret_id = @secret_id`).run({ secret_id: secretId });
|
|
240
|
-
const insert = db().prepare(
|
|
241
|
-
`INSERT INTO secret_assignments (secret_id, agent_group_id, created_at)
|
|
242
|
-
VALUES (@secret_id, @agent_group_id, @created_at)`,
|
|
243
|
-
);
|
|
244
|
-
for (const gid of agentGroupIds) {
|
|
245
|
-
insert.run({ secret_id: secretId, agent_group_id: gid, created_at: now });
|
|
246
|
-
}
|
|
247
|
-
})();
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/** Idempotent — re-adding an existing assignment is a no-op (composite PK). */
|
|
251
|
-
export function addAssignment(secretId: string, agentGroupId: string): boolean {
|
|
252
|
-
const r = db()
|
|
253
|
-
.prepare(
|
|
254
|
-
`INSERT INTO secret_assignments (secret_id, agent_group_id, created_at)
|
|
255
|
-
VALUES (@secret_id, @agent_group_id, @created_at)
|
|
256
|
-
ON CONFLICT (secret_id, agent_group_id) DO NOTHING`,
|
|
257
|
-
)
|
|
258
|
-
.run({ secret_id: secretId, agent_group_id: agentGroupId, created_at: nowIso() });
|
|
259
|
-
return r.changes > 0;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
export function removeAssignment(secretId: string, agentGroupId: string): boolean {
|
|
263
|
-
const r = db()
|
|
264
|
-
.prepare(`DELETE FROM secret_assignments WHERE secret_id = @secret_id AND agent_group_id = @agent_group_id`)
|
|
265
|
-
.run({ secret_id: secretId, agent_group_id: agentGroupId });
|
|
266
|
-
return r.changes > 0;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// ── Staleness detection (Bug B) ──
|
|
270
|
-
|
|
271
|
-
export interface StaleSession {
|
|
272
|
-
sessionId: string;
|
|
273
|
-
agentGroupId: string;
|
|
274
|
-
agentGroupName: string;
|
|
275
|
-
agentGroupFolder: string;
|
|
276
|
-
sessionCreatedAt: string;
|
|
277
|
-
secretUpdatedAt: string;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* Sessions whose container was spawned BEFORE this secret was last updated
|
|
282
|
-
* AND whose agent group would inject the secret. The injection predicate
|
|
283
|
-
* mirrors `resolveInjectableSecrets` for the configurations the UI can
|
|
284
|
-
* actually create:
|
|
285
|
-
* - scoped secret → matches its parent group (`s.agent_group_id = g.id`)
|
|
286
|
-
* - global secret → matches any group with `secret_mode='all'` OR an
|
|
287
|
-
* explicit `secret_assignments` row
|
|
288
|
-
*
|
|
289
|
-
* Note on a subtle asymmetry: `resolveInjectableSecrets` additionally gates
|
|
290
|
-
* scoped secrets through `(secret_mode='all' OR assignment row exists)` on
|
|
291
|
-
* the recipient group. The SQL here accepts the scoped match unconditionally.
|
|
292
|
-
* The asymmetry is benign — the only configs where it would diverge (a
|
|
293
|
-
* scoped secret paired with its parent group in `selective` mode and no
|
|
294
|
-
* assignment row) are unreachable via the UI, which always seeds an
|
|
295
|
-
* assignment row when scoping. If a future code path makes that config
|
|
296
|
-
* reachable, tighten the SQL to add the same gate.
|
|
297
|
-
*
|
|
298
|
-
* The host injects env vars at spawn time only — there is no in-process
|
|
299
|
-
* update path. This helper powers the post-save banner that prompts the
|
|
300
|
-
* operator to restart the specific sessions that need to see the change.
|
|
301
|
-
*
|
|
302
|
-
* Returns empty when the secret doesn't exist (caller handles 404).
|
|
303
|
-
*/
|
|
304
|
-
export function findStaleSessionsForSecret(secretId: string): StaleSession[] {
|
|
305
|
-
const rows = db()
|
|
306
|
-
.prepare<{
|
|
307
|
-
session_id: string;
|
|
308
|
-
agent_group_id: string;
|
|
309
|
-
agent_group_name: string;
|
|
310
|
-
agent_group_folder: string;
|
|
311
|
-
session_created_at: string;
|
|
312
|
-
secret_updated_at: string;
|
|
313
|
-
}>(
|
|
314
|
-
`SELECT
|
|
315
|
-
sess.id AS session_id,
|
|
316
|
-
g.id AS agent_group_id,
|
|
317
|
-
g.name AS agent_group_name,
|
|
318
|
-
g.folder AS agent_group_folder,
|
|
319
|
-
sess.created_at AS session_created_at,
|
|
320
|
-
s.updated_at AS secret_updated_at
|
|
321
|
-
FROM secrets s
|
|
322
|
-
JOIN agent_groups g
|
|
323
|
-
ON s.agent_group_id = g.id
|
|
324
|
-
OR s.agent_group_id IS NULL
|
|
325
|
-
LEFT JOIN secret_assignments a
|
|
326
|
-
ON a.secret_id = s.id AND a.agent_group_id = g.id
|
|
327
|
-
JOIN sessions sess
|
|
328
|
-
ON sess.agent_group_id = g.id
|
|
329
|
-
WHERE s.id = @secret_id
|
|
330
|
-
AND sess.container_status = 'running'
|
|
331
|
-
AND sess.created_at < s.updated_at
|
|
332
|
-
AND (
|
|
333
|
-
s.agent_group_id = g.id
|
|
334
|
-
OR (s.agent_group_id IS NULL
|
|
335
|
-
AND (g.secret_mode = 'all' OR a.secret_id IS NOT NULL))
|
|
336
|
-
)
|
|
337
|
-
ORDER BY sess.created_at DESC`,
|
|
338
|
-
)
|
|
339
|
-
.all({ secret_id: secretId });
|
|
340
|
-
return rows.map((r) => ({
|
|
341
|
-
sessionId: r.session_id,
|
|
342
|
-
agentGroupId: r.agent_group_id,
|
|
343
|
-
agentGroupName: r.agent_group_name,
|
|
344
|
-
agentGroupFolder: r.agent_group_folder,
|
|
345
|
-
sessionCreatedAt: r.session_created_at,
|
|
346
|
-
secretUpdatedAt: r.secret_updated_at,
|
|
347
|
-
}));
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/** Metadata-only single-row read by id. Returns undefined if missing. */
|
|
351
|
-
export function getSecretById(id: string): SecretRow | undefined {
|
|
352
|
-
return db()
|
|
353
|
-
.prepare<SecretRow>(`SELECT id, name, kind, agent_group_id, created_at, updated_at FROM secrets WHERE id = ?`)
|
|
354
|
-
.get(id);
|
|
355
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Master key bootstrap. Stores a 32-byte (256-bit) random key at
|
|
3
|
-
* `~/.parachute/agent/master.key` with mode 0600. Generated on first start;
|
|
4
|
-
* loaded from disk on subsequent starts.
|
|
5
|
-
*
|
|
6
|
-
* The key is never written to logs, never sent over the wire, never put in
|
|
7
|
-
* env vars. Loss of the file = loss of every encrypted secret (no recovery
|
|
8
|
-
* path); rotation requires re-encrypting every row.
|
|
9
|
-
*/
|
|
10
|
-
import crypto from 'crypto';
|
|
11
|
-
import fs from 'fs';
|
|
12
|
-
import path from 'path';
|
|
13
|
-
|
|
14
|
-
import { CENTRAL_DB_DIR } from '../config.js';
|
|
15
|
-
|
|
16
|
-
const KEY_LEN = 32;
|
|
17
|
-
// `master.key` lives next to the central DB so a single backup of
|
|
18
|
-
// `<PARACHUTE_DIR>/agent/` captures both crypto material and DB state, and
|
|
19
|
-
// so `PARACHUTE_HOME` overrides reroute both atoms together — sandboxes
|
|
20
|
-
// that override the home dir get a fresh DB AND a fresh master.key.
|
|
21
|
-
const KEY_DIR = CENTRAL_DB_DIR;
|
|
22
|
-
const KEY_PATH = path.join(KEY_DIR, 'master.key');
|
|
23
|
-
|
|
24
|
-
let cached: Buffer | null = null;
|
|
25
|
-
|
|
26
|
-
export function getMasterKeyPath(): string {
|
|
27
|
-
return KEY_PATH;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function loadOrCreateMasterKey(): Buffer {
|
|
31
|
-
if (cached) return cached;
|
|
32
|
-
|
|
33
|
-
if (fs.existsSync(KEY_PATH)) {
|
|
34
|
-
// Refuse to load a key file that's group/world readable. The file was
|
|
35
|
-
// created with mode 0600; if something has loosened it (chmod, restore
|
|
36
|
-
// from a backup tarball, etc.) we'd rather fail loud than silently keep
|
|
37
|
-
// serving secrets out of a file anyone on the box can read.
|
|
38
|
-
const stat = fs.statSync(KEY_PATH);
|
|
39
|
-
const perm = stat.mode & 0o777;
|
|
40
|
-
if ((stat.mode & 0o077) !== 0) {
|
|
41
|
-
throw new Error(
|
|
42
|
-
`Master key at ${KEY_PATH} has permissive mode 0${perm.toString(8).padStart(3, '0')}; ` +
|
|
43
|
-
`expected 0600. Run: chmod 600 ${KEY_PATH}`,
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
const buf = fs.readFileSync(KEY_PATH);
|
|
47
|
-
if (buf.length !== KEY_LEN) {
|
|
48
|
-
throw new Error(`Master key at ${KEY_PATH} is ${buf.length} bytes; expected ${KEY_LEN}`);
|
|
49
|
-
}
|
|
50
|
-
cached = buf;
|
|
51
|
-
return buf;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
fs.mkdirSync(KEY_DIR, { recursive: true, mode: 0o700 });
|
|
55
|
-
const key = crypto.randomBytes(KEY_LEN);
|
|
56
|
-
fs.writeFileSync(KEY_PATH, key, { mode: 0o600 });
|
|
57
|
-
cached = key;
|
|
58
|
-
return key;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** Test-only: clear the cached key so a different one can be loaded. */
|
|
62
|
-
export function _resetMasterKeyCache(): void {
|
|
63
|
-
cached = null;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** Test-only: install a key without touching disk. */
|
|
67
|
-
export function _setMasterKeyForTest(key: Buffer): void {
|
|
68
|
-
if (key.length !== KEY_LEN) throw new Error(`test key must be ${KEY_LEN} bytes`);
|
|
69
|
-
cached = key;
|
|
70
|
-
}
|