lastlight 0.1.15 → 0.2.1
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/README.md +265 -118
- package/agent-context/rules.md +62 -9
- package/agent-context/security.md +69 -0
- package/config/default.yaml +89 -0
- package/deploy/.env.production.example +20 -2
- package/deploy/entrypoint.sh +16 -17
- package/deploy/native/README.md +160 -0
- package/deploy/native/install.sh +110 -0
- package/deploy/native/lastlight.env.example +95 -0
- package/deploy/native/lastlight.service +50 -0
- package/deploy/sandbox-entrypoint.sh +59 -42
- package/dist/admin/auth.d.ts +1 -1
- package/dist/admin/auth.js +3 -1
- package/dist/admin/auth.js.map +1 -1
- package/dist/admin/chat-session-reader.d.ts +11 -14
- package/dist/admin/chat-session-reader.js +27 -51
- package/dist/admin/chat-session-reader.js.map +1 -1
- package/dist/admin/docker.d.ts +37 -0
- package/dist/admin/docker.js +86 -1
- package/dist/admin/docker.js.map +1 -1
- package/dist/admin/index.js +38 -3
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/log-search.test.d.ts +1 -0
- package/dist/admin/log-search.test.js +78 -0
- package/dist/admin/log-search.test.js.map +1 -0
- package/dist/admin/routes.d.ts +52 -3
- package/dist/admin/routes.js +780 -63
- package/dist/admin/routes.js.map +1 -1
- package/dist/admin/routes.test.js +680 -14
- package/dist/admin/routes.test.js.map +1 -1
- package/dist/admin/server-logs.test.d.ts +1 -0
- package/dist/admin/server-logs.test.js +58 -0
- package/dist/admin/server-logs.test.js.map +1 -0
- package/dist/admin/sessions.d.ts +26 -39
- package/dist/admin/sessions.js +86 -310
- package/dist/admin/sessions.js.map +1 -1
- package/dist/admin/sessions.test.d.ts +1 -0
- package/dist/admin/sessions.test.js +78 -0
- package/dist/admin/sessions.test.js.map +1 -0
- package/dist/admin/version.d.ts +15 -0
- package/dist/admin/version.js +73 -0
- package/dist/admin/version.js.map +1 -0
- package/dist/admin/version.test.d.ts +1 -0
- package/dist/admin/version.test.js +71 -0
- package/dist/admin/version.test.js.map +1 -0
- package/dist/cli-config.d.ts +55 -0
- package/dist/cli-config.js +119 -0
- package/dist/cli-config.js.map +1 -0
- package/dist/cli-config.test.d.ts +1 -0
- package/dist/cli-config.test.js +92 -0
- package/dist/cli-config.test.js.map +1 -0
- package/dist/cli-format.d.ts +19 -0
- package/dist/cli-format.js +107 -0
- package/dist/cli-format.js.map +1 -0
- package/dist/cli-server.d.ts +73 -0
- package/dist/cli-server.js +347 -0
- package/dist/cli-server.js.map +1 -0
- package/dist/cli-server.test.d.ts +1 -0
- package/dist/cli-server.test.js +38 -0
- package/dist/cli-server.test.js.map +1 -0
- package/dist/cli-timeline.d.ts +35 -0
- package/dist/cli-timeline.js +373 -0
- package/dist/cli-timeline.js.map +1 -0
- package/dist/cli-timeline.test.d.ts +1 -0
- package/dist/cli-timeline.test.js +104 -0
- package/dist/cli-timeline.test.js.map +1 -0
- package/dist/cli.d.ts +0 -19
- package/dist/cli.js +910 -194
- package/dist/cli.js.map +1 -1
- package/dist/config-overlay.test.d.ts +1 -0
- package/dist/config-overlay.test.js +112 -0
- package/dist/config-overlay.test.js.map +1 -0
- package/dist/config-resolve.d.ts +28 -0
- package/dist/config-resolve.js +47 -0
- package/dist/config-resolve.js.map +1 -0
- package/dist/config-resolve.test.d.ts +1 -0
- package/dist/config-resolve.test.js +68 -0
- package/dist/config-resolve.test.js.map +1 -0
- package/dist/config.d.ts +62 -49
- package/dist/config.js +452 -76
- package/dist/config.js.map +1 -1
- package/dist/config.test.js +201 -32
- package/dist/config.test.js.map +1 -1
- package/dist/connectors/github-webhook.js +83 -5
- package/dist/connectors/github-webhook.js.map +1 -1
- package/dist/connectors/github-webhook.test.d.ts +1 -0
- package/dist/connectors/github-webhook.test.js +156 -0
- package/dist/connectors/github-webhook.test.js.map +1 -0
- package/dist/connectors/messaging/session-manager.d.ts +18 -0
- package/dist/connectors/messaging/session-manager.js +83 -2
- package/dist/connectors/messaging/session-manager.js.map +1 -1
- package/dist/connectors/messaging/session-manager.test.d.ts +1 -0
- package/dist/connectors/messaging/session-manager.test.js +144 -0
- package/dist/connectors/messaging/session-manager.test.js.map +1 -0
- package/dist/connectors/slack/connector.d.ts +9 -0
- package/dist/connectors/slack/connector.js +15 -0
- package/dist/connectors/slack/connector.js.map +1 -1
- package/dist/connectors/slack/mrkdwn.js +81 -0
- package/dist/connectors/slack/mrkdwn.js.map +1 -1
- package/dist/connectors/slack/mrkdwn.test.js +49 -0
- package/dist/connectors/slack/mrkdwn.test.js.map +1 -1
- package/dist/connectors/types.d.ts +1 -1
- package/dist/cron/fanout.d.ts +33 -0
- package/dist/cron/fanout.js +55 -0
- package/dist/cron/fanout.js.map +1 -0
- package/dist/cron/fanout.test.d.ts +1 -0
- package/dist/cron/fanout.test.js +73 -0
- package/dist/cron/fanout.test.js.map +1 -0
- package/dist/cron/jobs.d.ts +5 -0
- package/dist/cron/jobs.js +10 -3
- package/dist/cron/jobs.js.map +1 -1
- package/dist/cron/scheduler.d.ts +12 -0
- package/dist/cron/scheduler.js +27 -1
- package/dist/cron/scheduler.js.map +1 -1
- package/dist/engine/agent-executor.d.ts +158 -0
- package/dist/engine/agent-executor.js +1140 -0
- package/dist/engine/agent-executor.js.map +1 -0
- package/dist/engine/agent-executor.test.d.ts +1 -0
- package/dist/engine/agent-executor.test.js +395 -0
- package/dist/engine/agent-executor.test.js.map +1 -0
- package/dist/engine/chat-runner.d.ts +123 -0
- package/dist/engine/chat-runner.js +379 -0
- package/dist/engine/chat-runner.js.map +1 -0
- package/dist/engine/chat-runner.test.d.ts +1 -0
- package/dist/engine/chat-runner.test.js +104 -0
- package/dist/engine/chat-runner.test.js.map +1 -0
- package/dist/engine/chat-skills.d.ts +45 -0
- package/dist/engine/chat-skills.js +184 -0
- package/dist/engine/chat-skills.js.map +1 -0
- package/dist/engine/chat-skills.test.d.ts +1 -0
- package/dist/engine/chat-skills.test.js +20 -0
- package/dist/engine/chat-skills.test.js.map +1 -0
- package/dist/engine/chat.d.ts +20 -23
- package/dist/engine/chat.js +251 -155
- package/dist/engine/chat.js.map +1 -1
- package/dist/engine/chat.test.d.ts +1 -0
- package/dist/engine/chat.test.js +42 -0
- package/dist/engine/chat.test.js.map +1 -0
- package/dist/engine/classifier.d.ts +35 -2
- package/dist/engine/classifier.js +170 -31
- package/dist/engine/classifier.js.map +1 -1
- package/dist/engine/classifier.test.d.ts +1 -0
- package/dist/engine/classifier.test.js +115 -0
- package/dist/engine/classifier.test.js.map +1 -0
- package/dist/engine/dispatcher.d.ts +60 -0
- package/dist/engine/dispatcher.js +601 -0
- package/dist/engine/dispatcher.js.map +1 -0
- package/dist/engine/dispatcher.test.d.ts +1 -0
- package/dist/engine/dispatcher.test.js +511 -0
- package/dist/engine/dispatcher.test.js.map +1 -0
- package/dist/engine/event-shim.d.ts +117 -0
- package/dist/engine/event-shim.js +435 -0
- package/dist/engine/event-shim.js.map +1 -0
- package/dist/engine/event-shim.test.d.ts +1 -0
- package/dist/engine/event-shim.test.js +194 -0
- package/dist/engine/event-shim.test.js.map +1 -0
- package/dist/engine/git-auth.d.ts +13 -17
- package/dist/engine/git-auth.js +99 -42
- package/dist/engine/git-auth.js.map +1 -1
- package/dist/engine/git-auth.test.d.ts +1 -0
- package/dist/engine/git-auth.test.js +117 -0
- package/dist/engine/git-auth.test.js.map +1 -0
- package/dist/engine/github-app-client.d.ts +7 -0
- package/dist/engine/github-app-client.js +16 -0
- package/dist/engine/github-app-client.js.map +1 -0
- package/dist/engine/github-app-client.test.d.ts +1 -0
- package/dist/engine/github-app-client.test.js +44 -0
- package/dist/engine/github-app-client.test.js.map +1 -0
- package/dist/engine/github-tools.d.ts +12 -0
- package/dist/engine/github-tools.js +261 -0
- package/dist/engine/github-tools.js.map +1 -0
- package/dist/engine/github.d.ts +1027 -43
- package/dist/engine/github.js +153 -14
- package/dist/engine/github.js.map +1 -1
- package/dist/engine/llm.d.ts +47 -0
- package/dist/engine/llm.js +221 -0
- package/dist/engine/llm.js.map +1 -0
- package/dist/engine/llm.test.d.ts +1 -0
- package/dist/engine/llm.test.js +189 -0
- package/dist/engine/llm.test.js.map +1 -0
- package/dist/engine/profiles.d.ts +249 -0
- package/dist/engine/profiles.js +42 -0
- package/dist/engine/profiles.js.map +1 -0
- package/dist/engine/router.d.ts +12 -6
- package/dist/engine/router.js +339 -81
- package/dist/engine/router.js.map +1 -1
- package/dist/engine/router.test.js +428 -78
- package/dist/engine/router.test.js.map +1 -1
- package/dist/engine/screen.d.ts +41 -0
- package/dist/engine/screen.js +93 -0
- package/dist/engine/screen.js.map +1 -0
- package/dist/engine/screen.test.d.ts +1 -0
- package/dist/engine/screen.test.js +82 -0
- package/dist/engine/screen.test.js.map +1 -0
- package/dist/index.js +377 -432
- package/dist/index.js.map +1 -1
- package/dist/managed-repos.d.ts +7 -9
- package/dist/managed-repos.js +12 -15
- package/dist/managed-repos.js.map +1 -1
- package/dist/managed-repos.test.js +24 -19
- package/dist/managed-repos.test.js.map +1 -1
- package/dist/notify/index.d.ts +15 -0
- package/dist/notify/index.js +6 -0
- package/dist/notify/index.js.map +1 -0
- package/dist/notify/model.d.ts +57 -0
- package/dist/notify/model.js +89 -0
- package/dist/notify/model.js.map +1 -0
- package/dist/notify/model.test.d.ts +1 -0
- package/dist/notify/model.test.js +109 -0
- package/dist/notify/model.test.js.map +1 -0
- package/dist/notify/notifier.d.ts +17 -0
- package/dist/notify/notifier.js +87 -0
- package/dist/notify/notifier.js.map +1 -0
- package/dist/notify/notifier.test.d.ts +1 -0
- package/dist/notify/notifier.test.js +100 -0
- package/dist/notify/notifier.test.js.map +1 -0
- package/dist/notify/render.d.ts +21 -0
- package/dist/notify/render.js +59 -0
- package/dist/notify/render.js.map +1 -0
- package/dist/notify/render.test.d.ts +1 -0
- package/dist/notify/render.test.js +65 -0
- package/dist/notify/render.test.js.map +1 -0
- package/dist/notify/transports/github.d.ts +26 -0
- package/dist/notify/transports/github.js +26 -0
- package/dist/notify/transports/github.js.map +1 -0
- package/dist/notify/transports/slack.d.ts +26 -0
- package/dist/notify/transports/slack.js +28 -0
- package/dist/notify/transports/slack.js.map +1 -0
- package/dist/notify/transports.test.d.ts +1 -0
- package/dist/notify/transports.test.js +71 -0
- package/dist/notify/transports.test.js.map +1 -0
- package/dist/notify/types.d.ts +95 -0
- package/dist/notify/types.js +16 -0
- package/dist/notify/types.js.map +1 -0
- package/dist/sandbox/docker-compose.test.d.ts +1 -0
- package/dist/sandbox/docker-compose.test.js +128 -0
- package/dist/sandbox/docker-compose.test.js.map +1 -0
- package/dist/sandbox/docker.d.ts +97 -9
- package/dist/sandbox/docker.js +192 -41
- package/dist/sandbox/docker.js.map +1 -1
- package/dist/sandbox/docker.test.d.ts +1 -0
- package/dist/sandbox/docker.test.js +137 -0
- package/dist/sandbox/docker.test.js.map +1 -0
- package/dist/sandbox/egress-allowlist.d.ts +69 -0
- package/dist/sandbox/egress-allowlist.js +164 -0
- package/dist/sandbox/egress-allowlist.js.map +1 -0
- package/dist/sandbox/egress-allowlist.test.d.ts +1 -0
- package/dist/sandbox/egress-allowlist.test.js +65 -0
- package/dist/sandbox/egress-allowlist.test.js.map +1 -0
- package/dist/sandbox/egress-firewall-config.d.ts +127 -0
- package/dist/sandbox/egress-firewall-config.js +434 -0
- package/dist/sandbox/egress-firewall-config.js.map +1 -0
- package/dist/sandbox/egress-firewall-config.test.d.ts +1 -0
- package/dist/sandbox/egress-firewall-config.test.js +244 -0
- package/dist/sandbox/egress-firewall-config.test.js.map +1 -0
- package/dist/sandbox/images.d.ts +23 -0
- package/dist/sandbox/images.js +55 -0
- package/dist/sandbox/images.js.map +1 -0
- package/dist/sandbox/index.d.ts +82 -3
- package/dist/sandbox/index.js +223 -93
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/index.test.d.ts +1 -0
- package/dist/sandbox/index.test.js +157 -0
- package/dist/sandbox/index.test.js.map +1 -0
- package/dist/session-log.d.ts +63 -0
- package/dist/session-log.js +290 -0
- package/dist/session-log.js.map +1 -0
- package/dist/session-log.test.d.ts +1 -0
- package/dist/session-log.test.js +85 -0
- package/dist/session-log.test.js.map +1 -0
- package/dist/setup.d.ts +30 -1
- package/dist/setup.js +241 -25
- package/dist/setup.js.map +1 -1
- package/dist/setup.test.js +138 -6
- package/dist/setup.test.js.map +1 -1
- package/dist/state/approval-store.d.ts +86 -0
- package/dist/state/approval-store.js +140 -0
- package/dist/state/approval-store.js.map +1 -0
- package/dist/state/build-assets.d.ts +50 -0
- package/dist/state/build-assets.js +154 -0
- package/dist/state/build-assets.js.map +1 -0
- package/dist/state/build-assets.test.d.ts +1 -0
- package/dist/state/build-assets.test.js +106 -0
- package/dist/state/build-assets.test.js.map +1 -0
- package/dist/state/db.d.ts +65 -333
- package/dist/state/db.js +112 -835
- package/dist/state/db.js.map +1 -1
- package/dist/state/db.test.js +127 -91
- package/dist/state/db.test.js.map +1 -1
- package/dist/state/execution-store.d.ts +257 -0
- package/dist/state/execution-store.js +527 -0
- package/dist/state/execution-store.js.map +1 -0
- package/dist/state/migrate.d.ts +15 -0
- package/dist/state/migrate.js +175 -0
- package/dist/state/migrate.js.map +1 -0
- package/dist/state/workflow-run-store.d.ts +195 -0
- package/dist/state/workflow-run-store.js +363 -0
- package/dist/state/workflow-run-store.js.map +1 -0
- package/dist/state/workflow-run-store.test.d.ts +1 -0
- package/dist/state/workflow-run-store.test.js +264 -0
- package/dist/state/workflow-run-store.test.js.map +1 -0
- package/dist/telemetry/index.d.ts +38 -0
- package/dist/telemetry/index.js +253 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/telemetry/index.test.d.ts +1 -0
- package/dist/telemetry/index.test.js +73 -0
- package/dist/telemetry/index.test.js.map +1 -0
- package/dist/telemetry/pi-events.d.ts +12 -0
- package/dist/telemetry/pi-events.js +134 -0
- package/dist/telemetry/pi-events.js.map +1 -0
- package/dist/telemetry/pi-events.test.d.ts +1 -0
- package/dist/telemetry/pi-events.test.js +69 -0
- package/dist/telemetry/pi-events.test.js.map +1 -0
- package/dist/workflows/dag.d.ts +13 -1
- package/dist/workflows/dag.js +7 -3
- package/dist/workflows/dag.js.map +1 -1
- package/dist/workflows/dag.test.js +22 -0
- package/dist/workflows/dag.test.js.map +1 -1
- package/dist/workflows/golden-build.test.d.ts +1 -0
- package/dist/workflows/golden-build.test.js +45 -0
- package/dist/workflows/golden-build.test.js.map +1 -0
- package/dist/workflows/loader-overlay.test.d.ts +1 -0
- package/dist/workflows/loader-overlay.test.js +101 -0
- package/dist/workflows/loader-overlay.test.js.map +1 -0
- package/dist/workflows/loader.d.ts +36 -11
- package/dist/workflows/loader.js +311 -87
- package/dist/workflows/loader.js.map +1 -1
- package/dist/workflows/loader.test.js +114 -10
- package/dist/workflows/loader.test.js.map +1 -1
- package/dist/workflows/loop-eval.js +2 -0
- package/dist/workflows/loop-eval.js.map +1 -1
- package/dist/workflows/loop-eval.test.js +14 -0
- package/dist/workflows/loop-eval.test.js.map +1 -1
- package/dist/workflows/phase-executor.d.ts +145 -0
- package/dist/workflows/phase-executor.js +691 -0
- package/dist/workflows/phase-executor.js.map +1 -0
- package/dist/workflows/phase-executor.test.d.ts +1 -0
- package/dist/workflows/phase-executor.test.js +434 -0
- package/dist/workflows/phase-executor.test.js.map +1 -0
- package/dist/workflows/phase-ref.d.ts +44 -0
- package/dist/workflows/phase-ref.js +78 -0
- package/dist/workflows/phase-ref.js.map +1 -0
- package/dist/workflows/phase-ref.test.d.ts +1 -0
- package/dist/workflows/phase-ref.test.js +24 -0
- package/dist/workflows/phase-ref.test.js.map +1 -0
- package/dist/workflows/resume.d.ts +5 -11
- package/dist/workflows/resume.js +88 -7
- package/dist/workflows/resume.js.map +1 -1
- package/dist/workflows/runner.d.ts +34 -27
- package/dist/workflows/runner.js +252 -930
- package/dist/workflows/runner.js.map +1 -1
- package/dist/workflows/runner.test.js +346 -35
- package/dist/workflows/runner.test.js.map +1 -1
- package/dist/workflows/schema.d.ts +52 -10
- package/dist/workflows/schema.js +147 -7
- package/dist/workflows/schema.js.map +1 -1
- package/dist/workflows/simple.d.ts +67 -3
- package/dist/workflows/simple.js +248 -87
- package/dist/workflows/simple.js.map +1 -1
- package/dist/workflows/simple.test.d.ts +1 -0
- package/dist/workflows/simple.test.js +107 -0
- package/dist/workflows/simple.test.js.map +1 -0
- package/dist/workflows/templates.d.ts +29 -0
- package/dist/workflows/templates.js +42 -4
- package/dist/workflows/templates.js.map +1 -1
- package/dist/workflows/templates.test.js +103 -0
- package/dist/workflows/templates.test.js.map +1 -1
- package/dist/workflows/triggers.d.ts +21 -0
- package/dist/workflows/triggers.js +44 -0
- package/dist/workflows/triggers.js.map +1 -0
- package/dist/workflows/verdict.d.ts +19 -0
- package/dist/workflows/verdict.js +30 -0
- package/dist/workflows/verdict.js.map +1 -0
- package/dist/workflows/verdict.test.d.ts +1 -0
- package/dist/workflows/verdict.test.js +44 -0
- package/dist/workflows/verdict.test.js.map +1 -0
- package/dist/worktree/manager.d.ts +1 -1
- package/dist/worktree/manager.js +14 -14
- package/dist/worktree/manager.js.map +1 -1
- package/dist/worktree/manager.test.d.ts +1 -0
- package/dist/worktree/manager.test.js +87 -0
- package/dist/worktree/manager.test.js.map +1 -0
- package/docker-compose.yml +206 -5
- package/package.json +19 -9
- package/sandbox.Dockerfile +97 -18
- package/skills/README.md +40 -0
- package/skills/browser-qa/SKILL.md +193 -0
- package/skills/browser-qa/scripts/agent-browser.mjs +380 -0
- package/skills/building/SKILL.md +95 -0
- package/skills/chat/SKILL.md +30 -11
- package/skills/code-review/SKILL.md +59 -0
- package/skills/debug-production/SKILL.md +108 -0
- package/skills/demo/SKILL.md +194 -0
- package/skills/demo/scripts/compose-demo.sh +205 -0
- package/skills/issue-answer/SKILL.md +90 -0
- package/skills/issue-comment/SKILL.md +38 -24
- package/skills/issue-triage/SKILL.md +103 -37
- package/skills/issue-triage/references/AGENT-BRIEF.md +61 -0
- package/skills/pr-comment/SKILL.md +64 -0
- package/skills/pr-review/SKILL.md +65 -49
- package/skills/qa-test/SKILL.md +97 -0
- package/skills/repo-health/SKILL.md +48 -45
- package/skills/security-feedback/SKILL.md +173 -0
- package/skills/security-feedback/references/templates.md +77 -0
- package/skills/security-review/SKILL.md +213 -0
- package/skills/security-review/references/issue-format.md +302 -0
- package/skills/verify/SKILL.md +118 -0
- package/workflows/answer.yaml +37 -0
- package/workflows/build.yaml +32 -22
- package/workflows/cron-security.yaml +6 -0
- package/workflows/demo.yaml +55 -0
- package/workflows/explore.yaml +9 -0
- package/workflows/pr-comment.yaml +16 -0
- package/workflows/pr-fix.yaml +3 -0
- package/workflows/pr-review.yaml +4 -1
- package/workflows/prompts/answer.md +67 -0
- package/workflows/prompts/architect.md +24 -7
- package/workflows/prompts/demo.md +100 -0
- package/workflows/prompts/executor.md +29 -20
- package/workflows/prompts/explore-ask.md +51 -22
- package/workflows/prompts/explore-publish.md +2 -2
- package/workflows/prompts/explore-read.md +24 -10
- package/workflows/prompts/explore-synthesize.md +15 -8
- package/workflows/prompts/fix.md +9 -10
- package/workflows/prompts/guardrails.md +26 -8
- package/workflows/prompts/pr-fix.md +6 -7
- package/workflows/prompts/pr.md +11 -10
- package/workflows/prompts/qa-browser.md +95 -0
- package/workflows/prompts/qa-synth.md +44 -0
- package/workflows/prompts/qa-test.md +60 -0
- package/workflows/prompts/re-reviewer.md +8 -9
- package/workflows/prompts/reviewer.md +10 -9
- package/workflows/prompts/verify-browser.md +93 -0
- package/workflows/prompts/verify-synth.md +44 -0
- package/workflows/prompts/verify.md +63 -0
- package/workflows/qa-test.yaml +87 -0
- package/workflows/security-feedback.yaml +25 -0
- package/workflows/security-review.yaml +29 -0
- package/workflows/verify.yaml +85 -0
- package/deploy/mcp-config.tmpl.json +0 -14
- package/dist/cron/rate-limits.d.ts +0 -26
- package/dist/cron/rate-limits.js +0 -193
- package/dist/cron/rate-limits.js.map +0 -1
- package/dist/engine/executor.d.ts +0 -75
- package/dist/engine/executor.js +0 -292
- package/dist/engine/executor.js.map +0 -1
- package/mcp-github-app/package.json +0 -18
- package/mcp-github-app/src/auth.js +0 -66
- package/mcp-github-app/src/github.js +0 -371
- package/mcp-github-app/src/index.js +0 -534
- package/skills/devops/webhook-subscriptions/SKILL.md +0 -180
- package/skills/github/DESCRIPTION.md +0 -3
- package/skills/github/codebase-inspection/SKILL.md +0 -115
- package/skills/github/github-auth/SKILL.md +0 -54
- package/skills/github/github-auth/scripts/gh-env.sh +0 -66
- package/skills/github/github-code-review/SKILL.md +0 -480
- package/skills/github/github-code-review/references/review-output-template.md +0 -74
- package/skills/github/github-issues/SKILL.md +0 -369
- package/skills/github/github-issues/templates/bug-report.md +0 -35
- package/skills/github/github-issues/templates/feature-request.md +0 -31
- package/skills/github/github-pr-workflow/SKILL.md +0 -395
- package/skills/github/github-pr-workflow/references/ci-troubleshooting.md +0 -183
- package/skills/github/github-pr-workflow/references/conventional-commits.md +0 -71
- package/skills/github/github-pr-workflow/templates/pr-body-bugfix.md +0 -35
- package/skills/github/github-pr-workflow/templates/pr-body-feature.md +0 -33
- package/skills/github/github-repo-management/SKILL.md +0 -515
- package/skills/github/github-repo-management/references/github-api-cheatsheet.md +0 -161
- package/skills/github-orchestrator/SKILL.md +0 -103
- package/skills/mcp/DESCRIPTION.md +0 -3
- package/skills/mcp/mcporter/SKILL.md +0 -122
- package/skills/mcp/native-mcp/SKILL.md +0 -356
- package/skills/software-development/architect/SKILL.md +0 -154
- package/skills/software-development/assure-guardrails/SKILL.md +0 -239
- package/skills/software-development/plan/SKILL.md +0 -64
- package/skills/software-development/requesting-code-review/SKILL.md +0 -291
- package/skills/software-development/subagent-driven-development/SKILL.md +0 -433
- package/skills/software-development/systematic-debugging/SKILL.md +0 -366
- package/skills/software-development/test-driven-development/SKILL.md +0 -342
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { DEFAULT_ALLOWLIST, mergeAllowlist } from "./egress-allowlist.js";
|
|
4
|
+
/**
|
|
5
|
+
* Egress firewall config generation for the docker sandbox backend.
|
|
6
|
+
*
|
|
7
|
+
* Architecture (replaces the earlier tinyproxy CONNECT-proxy):
|
|
8
|
+
*
|
|
9
|
+
* sandbox-egress (internal: true, subnet 172.30.0.0/24)
|
|
10
|
+
* ├─ sandbox containers (docker run --dns <coredns-ip>)
|
|
11
|
+
* ├─ coredns-strict 172.30.0.10 → returns nginx-strict IP for allowlist hosts, NXDOMAIN otherwise
|
|
12
|
+
* ├─ coredns-open 172.30.0.11 → returns nginx-open IP for ANY hostname (except hard-deny SSRF magnets)
|
|
13
|
+
* ├─ nginx-egress-strict 172.30.0.20 → ssl_preread, SNI must match allowlist; tunnel upstream via proxy-egress
|
|
14
|
+
* └─ nginx-egress-open 172.30.0.21 → tunnel any SNI; client DNS already gated through coredns-open
|
|
15
|
+
*
|
|
16
|
+
* Why this instead of a forward proxy (tinyproxy/smokescreen): clients
|
|
17
|
+
* inside the sandbox (especially the OpenAI/Anthropic SDKs that build
|
|
18
|
+
* their own undici dispatchers) don't honour HTTP_PROXY / HTTPS_PROXY.
|
|
19
|
+
* Forcing them to cooperate is a losing battle. By spoofing DNS and
|
|
20
|
+
* peeking SNI at the network layer, the sandbox sees no proxy at all —
|
|
21
|
+
* it dials `api.openai.com:443` directly. The firewall intercepts the
|
|
22
|
+
* connection by virtue of being the only thing the spoofed DNS pointed
|
|
23
|
+
* at.
|
|
24
|
+
*
|
|
25
|
+
* Inspired by Vercel Sandbox's egress firewall (SNI peeking, no env
|
|
26
|
+
* vars). Their implementation also handles TLS termination for
|
|
27
|
+
* credentials brokering and Postgres STARTTLS — we don't (yet).
|
|
28
|
+
*
|
|
29
|
+
* ## Wildcards
|
|
30
|
+
*
|
|
31
|
+
* Entries in egress-allowlist.ts with a leading dot (`".github.com"`)
|
|
32
|
+
* match the apex plus every subdomain. nginx's `map` directive supports
|
|
33
|
+
* this syntax natively. CoreDNS uses the `template` plugin with an
|
|
34
|
+
* anchored regex to do the same.
|
|
35
|
+
*/
|
|
36
|
+
/** Fixed IPs assigned in docker-compose.yml so `--dns` and nginx upstreams can reference them. */
|
|
37
|
+
export const COREDNS_STRICT_IP = "172.30.0.10";
|
|
38
|
+
export const COREDNS_OPEN_IP = "172.30.0.11";
|
|
39
|
+
export const NGINX_STRICT_IP = "172.30.0.20";
|
|
40
|
+
export const NGINX_OPEN_IP = "172.30.0.21";
|
|
41
|
+
/**
|
|
42
|
+
* Static IP of the in-network OTEL collector (docker backend). Sandboxes
|
|
43
|
+
* dial it directly by IP — it lives on `sandbox-egress` alongside the
|
|
44
|
+
* firewalls, so no DNS lookup (and therefore no coredns allowlist entry)
|
|
45
|
+
* is involved. The collector is the ONLY OTLP endpoint a sandbox ever
|
|
46
|
+
* sees: it terminates the sandbox's telemetry and re-exports to the real
|
|
47
|
+
* backend over `proxy-egress` using credentials that stay host-side. This
|
|
48
|
+
* is why the strict SNI firewall no longer needs to know about collector
|
|
49
|
+
* hosts or non-443 ports — that hop happens on the trusted outbound leg,
|
|
50
|
+
* not through `ssl_preread`.
|
|
51
|
+
*/
|
|
52
|
+
export const OTEL_COLLECTOR_IP = "172.30.0.30";
|
|
53
|
+
/** OTLP/HTTP + OTLP/gRPC receiver ports the in-network collector listens on. */
|
|
54
|
+
export const OTEL_COLLECTOR_OTLP_HTTP_PORT = 4318;
|
|
55
|
+
export const OTEL_COLLECTOR_OTLP_GRPC_PORT = 4317;
|
|
56
|
+
/** Endpoint a docker sandbox is told to export OTLP to (HTTP/protobuf). */
|
|
57
|
+
export const OTEL_COLLECTOR_SANDBOX_ENDPOINT = `http://${OTEL_COLLECTOR_IP}:${OTEL_COLLECTOR_OTLP_HTTP_PORT}`;
|
|
58
|
+
/**
|
|
59
|
+
* UID the `otel/opentelemetry-collector` image runs as (its Dockerfile
|
|
60
|
+
* `USER`). The collector reads the OTLP config (which can hold backend auth
|
|
61
|
+
* headers) as this non-root user. To let it read a mode-0600 file from the
|
|
62
|
+
* shared volume WITHOUT running as root or making the secret world-readable,
|
|
63
|
+
* the harness's own `lastlight` user is pinned to this same UID (see the
|
|
64
|
+
* Dockerfile `useradd -u`), so the collector reads files it wrote as their
|
|
65
|
+
* owner. The Dockerfile and the collector image tag in docker-compose.yml
|
|
66
|
+
* must move together if a future image bumps the UID — enforced by a test in
|
|
67
|
+
* docker-compose.test.ts.
|
|
68
|
+
*/
|
|
69
|
+
export const OTEL_COLLECTOR_UID = 10001;
|
|
70
|
+
/** Subnet for sandbox-egress. Compose declares this CIDR so the IPs above are valid. */
|
|
71
|
+
export const SANDBOX_EGRESS_SUBNET = "172.30.0.0/24";
|
|
72
|
+
/**
|
|
73
|
+
* Hostnames the open coredns explicitly NXDOMAINs even in unrestricted
|
|
74
|
+
* mode — known SSRF magnets. nginx's `ssl_preread` SNI inspection alone
|
|
75
|
+
* can't catch hostnames that resolve to private IPs (the DNS server
|
|
76
|
+
* we control gets the first say), so this is the right layer to enforce
|
|
77
|
+
* "no cloud metadata, no internal services" even with `unrestricted_egress`.
|
|
78
|
+
*/
|
|
79
|
+
const OPEN_MODE_HARD_DENY = [
|
|
80
|
+
"metadata.google.internal",
|
|
81
|
+
"169.254.169.254", // GCP / AWS / Azure metadata service IP literal
|
|
82
|
+
];
|
|
83
|
+
/**
|
|
84
|
+
* Convert one allowlist entry to its nginx `map` line.
|
|
85
|
+
*
|
|
86
|
+
* "openai.com" → '.openai.com $ssl_preread_server_name:443;'
|
|
87
|
+
*
|
|
88
|
+
* Every entry is treated as apex+subdomain (see egress-allowlist.ts).
|
|
89
|
+
* The generated line uses nginx's `.foo.com` leading-dot syntax,
|
|
90
|
+
* which is its built-in "match this domain and all subdomains" form.
|
|
91
|
+
* Upstream is whatever SNI the client sent — we don't know which
|
|
92
|
+
* subdomain they're targeting at config-gen time.
|
|
93
|
+
*/
|
|
94
|
+
function nginxMapLine(entry) {
|
|
95
|
+
return ` .${entry} $ssl_preread_server_name:443;`;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Build one CoreDNS `match` regex per allowlist entry. All entries
|
|
99
|
+
* share a SINGLE `template` block (see renderCorefileStrict) because
|
|
100
|
+
* CoreDNS only honours one `template` per zone — multiple blocks
|
|
101
|
+
* silently shadow each other.
|
|
102
|
+
*
|
|
103
|
+
* The `(^|\.)` prefix matches the apex (`openai.com.`) or any subdomain
|
|
104
|
+
* (`api.openai.com.`) — same semantics as nginx's leading-dot form.
|
|
105
|
+
*/
|
|
106
|
+
function corednsMatchLineForEntry(entry) {
|
|
107
|
+
const escapedHost = entry.replaceAll(".", "\\.");
|
|
108
|
+
return ` match (^|\\.)${escapedHost}\\.$`;
|
|
109
|
+
}
|
|
110
|
+
/** Build the strict nginx.conf — ssl_preread + map on SNI allowlist. */
|
|
111
|
+
export function renderNginxStrictConf() {
|
|
112
|
+
const mapLines = mergeAllowlist(DEFAULT_ALLOWLIST).map(nginxMapLine).join("\n");
|
|
113
|
+
return `# Generated by lastlight at harness boot. DO NOT EDIT BY HAND —
|
|
114
|
+
# source of truth is src/sandbox/egress-allowlist.ts.
|
|
115
|
+
|
|
116
|
+
error_log /dev/stderr warn;
|
|
117
|
+
pid /tmp/nginx.pid;
|
|
118
|
+
|
|
119
|
+
events { worker_connections 1024; }
|
|
120
|
+
|
|
121
|
+
# Streams (TCP/UDP). \`ssl_preread\` reads the TLS ClientHello without
|
|
122
|
+
# terminating the session, exposing the SNI hostname as
|
|
123
|
+
# \`$ssl_preread_server_name\`. We \`map\` that to an upstream — anything
|
|
124
|
+
# unmatched is sunk to 127.0.0.1:1 (no listener, instant reset).
|
|
125
|
+
stream {
|
|
126
|
+
log_format basic '$remote_addr -> $ssl_preread_server_name -> $upstream_addr status=$status';
|
|
127
|
+
access_log /dev/stdout basic;
|
|
128
|
+
|
|
129
|
+
# Runtime resolver for upstream hostnames (docker's embedded DNS).
|
|
130
|
+
resolver 127.0.0.11 valid=30s ipv6=off;
|
|
131
|
+
|
|
132
|
+
# Allowlist. The \`hostnames\` directive enables nginx's wildcard
|
|
133
|
+
# hostname match: a key like ".github.com" matches "github.com" AND
|
|
134
|
+
# any subdomain. WITHOUT \`hostnames\`, map does exact string match
|
|
135
|
+
# only and ".github.com" never matches anything real (we hit this
|
|
136
|
+
# exact bug in prod — every allowlisted host fell through to the
|
|
137
|
+
# black hole).
|
|
138
|
+
map $ssl_preread_server_name $upstream_target {
|
|
139
|
+
hostnames;
|
|
140
|
+
${mapLines}
|
|
141
|
+
# Anything else → black hole. The connection resets immediately;
|
|
142
|
+
# no data ever leaves the sandbox-egress network.
|
|
143
|
+
default 127.0.0.1:1;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
server {
|
|
147
|
+
listen 443;
|
|
148
|
+
ssl_preread on;
|
|
149
|
+
proxy_pass $upstream_target;
|
|
150
|
+
proxy_connect_timeout 5s;
|
|
151
|
+
proxy_timeout 60s;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
`;
|
|
155
|
+
}
|
|
156
|
+
/** Build the open nginx.conf — no SNI allowlist, just tunnel whatever DNS pointed at. */
|
|
157
|
+
export function renderNginxOpenConf() {
|
|
158
|
+
return `# Generated by lastlight at harness boot. DO NOT EDIT BY HAND.
|
|
159
|
+
# This nginx is used only by phases that declared \`unrestricted_egress: true\`.
|
|
160
|
+
# No SNI allowlist — every host is tunnelled. The corresponding coredns-open
|
|
161
|
+
# refuses to resolve the cloud-metadata hostnames so SSRF to them is blocked
|
|
162
|
+
# even in this mode.
|
|
163
|
+
|
|
164
|
+
error_log /dev/stderr warn;
|
|
165
|
+
pid /tmp/nginx.pid;
|
|
166
|
+
|
|
167
|
+
events { worker_connections 1024; }
|
|
168
|
+
|
|
169
|
+
stream {
|
|
170
|
+
log_format basic '$remote_addr -> $ssl_preread_server_name -> $upstream_addr status=$status';
|
|
171
|
+
access_log /dev/stdout basic;
|
|
172
|
+
resolver 127.0.0.11 valid=30s ipv6=off;
|
|
173
|
+
|
|
174
|
+
server {
|
|
175
|
+
listen 443;
|
|
176
|
+
ssl_preread on;
|
|
177
|
+
# $ssl_preread_server_name is whatever SNI the client sent. Tunnel
|
|
178
|
+
# to that hostname on :443. No allowlist check.
|
|
179
|
+
proxy_pass $ssl_preread_server_name:443;
|
|
180
|
+
proxy_connect_timeout 5s;
|
|
181
|
+
proxy_timeout 60s;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
`;
|
|
185
|
+
}
|
|
186
|
+
/** Build the Corefile that returns the strict nginx IP for allowlist hosts. */
|
|
187
|
+
export function renderCorefileStrict() {
|
|
188
|
+
// ALL allowlist entries share a SINGLE template block. CoreDNS only
|
|
189
|
+
// honours one `template` per (class, type, zone), so multiple blocks
|
|
190
|
+
// silently overwrite each other — we learned this in prod when only
|
|
191
|
+
// the first host in the list resolved.
|
|
192
|
+
const matchLines = mergeAllowlist(DEFAULT_ALLOWLIST).map(corednsMatchLineForEntry).join("\n");
|
|
193
|
+
return `# Generated by lastlight from src/sandbox/egress-allowlist.ts.
|
|
194
|
+
# All allowlist hosts live in a single \`template IN A\` block (multiple
|
|
195
|
+
# template blocks per zone are NOT additive — they shadow each other).
|
|
196
|
+
# Queries that don't match any \`match\` regex fall through to the
|
|
197
|
+
# catch-all NXDOMAIN at the end.
|
|
198
|
+
.:53 {
|
|
199
|
+
template IN A {
|
|
200
|
+
${matchLines}
|
|
201
|
+
answer "{{ .Name }} 60 IN A ${NGINX_STRICT_IP}"
|
|
202
|
+
}
|
|
203
|
+
# Catch-all: NXDOMAIN for everything else (including AAAA queries,
|
|
204
|
+
# since nginx-egress only binds 443 on IPv4).
|
|
205
|
+
template IN ANY {
|
|
206
|
+
rcode NXDOMAIN
|
|
207
|
+
}
|
|
208
|
+
errors
|
|
209
|
+
log
|
|
210
|
+
}
|
|
211
|
+
`;
|
|
212
|
+
}
|
|
213
|
+
/** Build the Corefile that returns the open nginx IP for ANY hostname (except hard-denies). */
|
|
214
|
+
export function renderCorefileOpen() {
|
|
215
|
+
// Hard-deny hosts each get their OWN zone block. CoreDNS routes queries
|
|
216
|
+
// to the longest-suffix-matching zone, so `metadata.google.internal:53`
|
|
217
|
+
// catches both the apex and every subdomain before the catch-all `.`
|
|
218
|
+
// zone ever sees them. We do NOT use a multi-`template` layout inside
|
|
219
|
+
// the catch-all zone because CoreDNS only honours one template per
|
|
220
|
+
// (class, type) and prior templates with non-matching `match` clauses
|
|
221
|
+
// silently drop the query — leaving sandboxes with no DNS answer at
|
|
222
|
+
// all (which is exactly what failed in prod the first time explore
|
|
223
|
+
// ran on the open path).
|
|
224
|
+
const hardDenyZones = OPEN_MODE_HARD_DENY.map((host) => `# Hard-deny zone for ${host} (SSRF floor — NXDOMAIN even in unrestricted mode).
|
|
225
|
+
${host}:53 {
|
|
226
|
+
template IN ANY {
|
|
227
|
+
rcode NXDOMAIN
|
|
228
|
+
}
|
|
229
|
+
errors
|
|
230
|
+
log
|
|
231
|
+
}
|
|
232
|
+
`).join("\n");
|
|
233
|
+
return `# Generated by lastlight. Used by sandbox phases with unrestricted_egress: true.
|
|
234
|
+
# Returns nginx-egress-open's IP for every hostname EXCEPT the SSRF hard-denies
|
|
235
|
+
# (cloud metadata literals etc).
|
|
236
|
+
.:53 {
|
|
237
|
+
# Everything → nginx-egress-open IP. Hard-deny names are intercepted
|
|
238
|
+
# by the per-host zones declared below (longest-suffix zone match).
|
|
239
|
+
template IN A {
|
|
240
|
+
answer "{{ .Name }} 60 IN A ${NGINX_OPEN_IP}"
|
|
241
|
+
}
|
|
242
|
+
# AAAA returns empty (we route only via IPv4).
|
|
243
|
+
template IN AAAA {
|
|
244
|
+
rcode NOERROR
|
|
245
|
+
}
|
|
246
|
+
errors
|
|
247
|
+
log
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
${hardDenyZones}`;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Write the four config files under $STATE_DIR/proxy/. Idempotent.
|
|
254
|
+
* Returns the directory path. Compose bind-mounts this dir into the
|
|
255
|
+
* coredns + nginx containers read-only.
|
|
256
|
+
*/
|
|
257
|
+
export function writeEgressFirewallConfigs(stateDir) {
|
|
258
|
+
const dir = join(stateDir, "proxy");
|
|
259
|
+
mkdirSync(dir, { recursive: true });
|
|
260
|
+
writeFileSync(join(dir, "nginx-strict.conf"), renderNginxStrictConf());
|
|
261
|
+
writeFileSync(join(dir, "nginx-open.conf"), renderNginxOpenConf());
|
|
262
|
+
writeFileSync(join(dir, "Corefile.strict"), renderCorefileStrict());
|
|
263
|
+
writeFileSync(join(dir, "Corefile.open"), renderCorefileOpen());
|
|
264
|
+
return dir;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Parse an OTLP headers string (`key1=val1,key2=val2`, the OTEL spec format
|
|
268
|
+
* used by `OTEL_EXPORTER_OTLP_HEADERS`) into an ordered list of pairs.
|
|
269
|
+
* Malformed segments (no `=`, empty key) are dropped. Values keep any inner
|
|
270
|
+
* `=` so bearer tokens survive intact.
|
|
271
|
+
*/
|
|
272
|
+
function parseOtlpHeaders(raw) {
|
|
273
|
+
if (!raw)
|
|
274
|
+
return [];
|
|
275
|
+
const out = [];
|
|
276
|
+
for (const segment of raw.split(",")) {
|
|
277
|
+
const eq = segment.indexOf("=");
|
|
278
|
+
if (eq <= 0)
|
|
279
|
+
continue;
|
|
280
|
+
const key = segment.slice(0, eq).trim();
|
|
281
|
+
const value = segment.slice(eq + 1).trim();
|
|
282
|
+
// Reject CR/LF in either part: a real newline in an HTTP header value is
|
|
283
|
+
// header injection on the collector→backend leg, and would also break the
|
|
284
|
+
// YAML scalar. A header carrying a newline is corruption/injection, not a
|
|
285
|
+
// valid OTLP config — drop the pair rather than emit something dangerous.
|
|
286
|
+
if (!key || /[\r\n]/.test(key) || /[\r\n]/.test(value))
|
|
287
|
+
continue;
|
|
288
|
+
out.push([key, value]);
|
|
289
|
+
}
|
|
290
|
+
return out;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* YAML double-quoted-scalar escape. Backslash and quote are the cases that
|
|
294
|
+
* appear in tokens/URLs; CR/LF/TAB are escaped too as a hard guarantee that
|
|
295
|
+
* we NEVER emit a syntactically invalid collector config regardless of input
|
|
296
|
+
* (header values are already CR/LF-filtered in parseOtlpHeaders, but a stray
|
|
297
|
+
* control char in any other field must not be able to break the file).
|
|
298
|
+
*/
|
|
299
|
+
function yamlQuote(s) {
|
|
300
|
+
return `"${s
|
|
301
|
+
.replace(/\\/g, "\\\\")
|
|
302
|
+
.replace(/"/g, '\\"')
|
|
303
|
+
.replace(/\n/g, "\\n")
|
|
304
|
+
.replace(/\r/g, "\\r")
|
|
305
|
+
.replace(/\t/g, "\\t")}"`;
|
|
306
|
+
}
|
|
307
|
+
/** OTLP signals the collector pipelines cover, in stable output order. */
|
|
308
|
+
const OTLP_SIGNALS = ["traces", "metrics", "logs"];
|
|
309
|
+
/**
|
|
310
|
+
* Resolve one signal's backend per the OTEL env precedence: a
|
|
311
|
+
* signal-specific endpoint/headers var wins, otherwise the generic one.
|
|
312
|
+
* Returns null when neither is set — that signal's pipeline then drops to
|
|
313
|
+
* the `debug` exporter instead of being misrouted to another signal's URL.
|
|
314
|
+
*/
|
|
315
|
+
function resolveSignalBackend(env, signal) {
|
|
316
|
+
const SIG = signal.toUpperCase();
|
|
317
|
+
const specificEndpoint = (env[`OTEL_EXPORTER_OTLP_${SIG}_ENDPOINT`] || "").trim();
|
|
318
|
+
const genericEndpoint = (env.OTEL_EXPORTER_OTLP_ENDPOINT || "").trim();
|
|
319
|
+
if (!specificEndpoint && !genericEndpoint)
|
|
320
|
+
return null;
|
|
321
|
+
const specificHeaders = (env[`OTEL_EXPORTER_OTLP_${SIG}_HEADERS`] || "").trim();
|
|
322
|
+
const genericHeaders = env.OTEL_EXPORTER_OTLP_HEADERS;
|
|
323
|
+
return specificEndpoint
|
|
324
|
+
? { endpointField: `${signal}_endpoint`, endpoint: specificEndpoint, headers: parseOtlpHeaders(specificHeaders || genericHeaders) }
|
|
325
|
+
: { endpointField: "endpoint", endpoint: genericEndpoint, headers: parseOtlpHeaders(specificHeaders || genericHeaders) };
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Render the in-network OTEL collector config (docker backend).
|
|
329
|
+
*
|
|
330
|
+
* `opts.active` gates whether the collector forwards anywhere. It MUST track
|
|
331
|
+
* the harness's effective `otel.enabled && otel.forwardToSandbox` — when
|
|
332
|
+
* false, every pipeline is wired to a `debug` exporter (drops data) and NO
|
|
333
|
+
* backend endpoint or credential is emitted, even if `OTEL_EXPORTER_OTLP_*`
|
|
334
|
+
* env vars are present. This is the security floor: the collector listens on
|
|
335
|
+
* a static IP that sandboxes can reach, so if forwarding weren't gated, a
|
|
336
|
+
* sandbox could POST directly to it and exfil to the real backend regardless
|
|
337
|
+
* of the documented `LASTLIGHT_OTEL_ENABLED` / `FORWARD_TO_SANDBOX` opt-outs.
|
|
338
|
+
*
|
|
339
|
+
* When active, the collector re-exports to the REAL backend — the standard
|
|
340
|
+
* `OTEL_EXPORTER_OTLP_ENDPOINT` / `OTEL_EXPORTER_OTLP_HEADERS` plus their
|
|
341
|
+
* per-signal `OTEL_EXPORTER_OTLP_<SIGNAL>_ENDPOINT` / `_HEADERS` overrides,
|
|
342
|
+
* resolved with the spec's precedence. Each signal gets its own exporter so
|
|
343
|
+
* a split traces/metrics/logs setup is routed correctly rather than all
|
|
344
|
+
* collapsing onto whichever endpoint won a fallback chain. Those credentials
|
|
345
|
+
* live here, in a file on the host mounted read-only into the collector, and
|
|
346
|
+
* are NEVER forwarded into an untrusted sandbox. The sandbox can only
|
|
347
|
+
* influence span *content* sent to the harness's own fixed backend — it
|
|
348
|
+
* cannot redirect where the collector exports, so this adds no SSRF/exfil
|
|
349
|
+
* surface.
|
|
350
|
+
*
|
|
351
|
+
* A signal with no configured endpoint (neither specific nor generic) is
|
|
352
|
+
* likewise wired to `debug` so the collector still boots cleanly and accepts
|
|
353
|
+
* connections — sandboxes simply never send what isn't routed.
|
|
354
|
+
*/
|
|
355
|
+
export function renderOtelCollectorConfig(opts) {
|
|
356
|
+
const env = opts.env ?? process.env;
|
|
357
|
+
const exporterBlocks = [];
|
|
358
|
+
const pipelineBlocks = [];
|
|
359
|
+
let needsDebug = false;
|
|
360
|
+
for (const signal of OTLP_SIGNALS) {
|
|
361
|
+
// When inactive, resolve NO backend — every signal drops to debug and
|
|
362
|
+
// not a single OTEL_* value reaches the generated file.
|
|
363
|
+
const backend = opts.active ? resolveSignalBackend(env, signal) : null;
|
|
364
|
+
let exporterName;
|
|
365
|
+
if (!backend) {
|
|
366
|
+
exporterName = "debug";
|
|
367
|
+
needsDebug = true;
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
exporterName = `otlphttp/${signal}`;
|
|
371
|
+
const headerLines = backend.headers.length
|
|
372
|
+
? `\n headers:\n${backend.headers.map(([k, v]) => ` ${yamlQuote(k)}: ${yamlQuote(v)}`).join("\n")}`
|
|
373
|
+
: "";
|
|
374
|
+
exporterBlocks.push(` ${exporterName}:\n ${backend.endpointField}: ${yamlQuote(backend.endpoint)}${headerLines}`);
|
|
375
|
+
}
|
|
376
|
+
pipelineBlocks.push(` ${signal}:\n receivers: [otlp]\n processors: [batch]\n exporters: [${exporterName}]`);
|
|
377
|
+
}
|
|
378
|
+
if (needsDebug)
|
|
379
|
+
exporterBlocks.push(` debug:\n verbosity: normal`);
|
|
380
|
+
const statusComment = opts.active
|
|
381
|
+
? `# Sandboxes export OTLP to this collector (reached by its internal IP on
|
|
382
|
+
# sandbox-egress); the collector re-exports to the configured backend over
|
|
383
|
+
# proxy-egress. Backend credentials below stay host-side — they are never
|
|
384
|
+
# forwarded into a sandbox.`
|
|
385
|
+
: `# Telemetry forwarding is DISABLED (LASTLIGHT_OTEL_ENABLED is off, or
|
|
386
|
+
# LASTLIGHT_OTEL_FORWARD_TO_SANDBOX is false). The collector still listens so
|
|
387
|
+
# sandboxes don't get connection errors, but every signal drops to a local
|
|
388
|
+
# debug exporter — nothing is forwarded to any backend, and no OTEL_* endpoint
|
|
389
|
+
# or credential is emitted here. This is the egress floor for the static
|
|
390
|
+
# collector IP regardless of any OTEL_* env vars present.`;
|
|
391
|
+
return `# Generated by lastlight at harness boot. DO NOT EDIT BY HAND —
|
|
392
|
+
# source of truth is src/sandbox/egress-firewall-config.ts.
|
|
393
|
+
#
|
|
394
|
+
${statusComment}
|
|
395
|
+
receivers:
|
|
396
|
+
otlp:
|
|
397
|
+
protocols:
|
|
398
|
+
http:
|
|
399
|
+
endpoint: 0.0.0.0:${OTEL_COLLECTOR_OTLP_HTTP_PORT}
|
|
400
|
+
grpc:
|
|
401
|
+
endpoint: 0.0.0.0:${OTEL_COLLECTOR_OTLP_GRPC_PORT}
|
|
402
|
+
|
|
403
|
+
processors:
|
|
404
|
+
batch: {}
|
|
405
|
+
|
|
406
|
+
exporters:
|
|
407
|
+
${exporterBlocks.join("\n")}
|
|
408
|
+
|
|
409
|
+
service:
|
|
410
|
+
pipelines:
|
|
411
|
+
${pipelineBlocks.join("\n")}
|
|
412
|
+
`;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Write the in-network collector config to $STATE_DIR/proxy/otel-collector.yaml.
|
|
416
|
+
* Mode 0600 — it can contain backend auth headers. Called at harness boot
|
|
417
|
+
* (and by the compose `egress-init` one-shot) so the file exists before the
|
|
418
|
+
* `otel-collector` service reads it. `opts.active` gates forwarding — see
|
|
419
|
+
* `renderOtelCollectorConfig`. The harness passes its parsed
|
|
420
|
+
* `otel.enabled && otel.forwardToSandbox`; the env-only callers compute it
|
|
421
|
+
* via `otelForwardActiveFromEnv`.
|
|
422
|
+
*/
|
|
423
|
+
export function writeOtelCollectorConfig(stateDir, opts) {
|
|
424
|
+
const dir = join(stateDir, "proxy");
|
|
425
|
+
mkdirSync(dir, { recursive: true });
|
|
426
|
+
const path = join(dir, "otel-collector.yaml");
|
|
427
|
+
// Mode 0600 — it can carry backend auth headers. In the docker deployment
|
|
428
|
+
// the writer (harness/egress-init, gosu'd to `lastlight`) and the collector
|
|
429
|
+
// share UID 10001 (see OTEL_COLLECTOR_UID + the Dockerfile pin), so the
|
|
430
|
+
// collector reads this file as its owner without it being world-readable.
|
|
431
|
+
writeFileSync(path, renderOtelCollectorConfig(opts), { mode: 0o600 });
|
|
432
|
+
return path;
|
|
433
|
+
}
|
|
434
|
+
//# sourceMappingURL=egress-firewall-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"egress-firewall-config.js","sourceRoot":"","sources":["../../src/sandbox/egress-firewall-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|