@planu/cli 4.14.1 → 5.0.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/CHANGELOG.md +35 -1
- package/LICENSE +55 -12
- package/README.md +10 -2
- package/dist/cli/commands/install.d.ts +1 -12
- package/dist/cli/commands/install.js +9 -65
- package/dist/cli/commands/job.js +4 -3
- package/dist/cli/commands/spec.js +2 -2
- package/dist/cli/commands/validate.js +2 -2
- package/dist/cli/router.js +1 -3
- package/dist/config/environment-schema.json +1 -3
- package/dist/config/official-sdd-tools.d.ts +4 -0
- package/dist/config/official-sdd-tools.js +35 -0
- package/dist/config/product-public.d.ts +139 -3
- package/dist/config/product-public.js +8 -1
- package/dist/config/product-public.json +7 -36
- package/dist/config/registries/hosts/codex.json +20 -363
- package/dist/config/release-policy.json +84 -2
- package/dist/config/runtime-policy.json +12 -1
- package/dist/config/skill-templates/planu-resume-work.md +2 -2
- package/dist/core/index.d.ts +1 -1
- package/dist/core/spec-validator.d.ts +4 -4
- package/dist/core/spec-validator.js +1 -1
- package/dist/engine/abortable-process-runner.d.ts +20 -2
- package/dist/engine/abortable-process-runner.js +243 -74
- package/dist/engine/ambiguity-scorer.js +4 -26
- package/dist/engine/auth/scope-mapper.js +1 -7
- package/dist/engine/autopilot/state-updater.js +4 -4
- package/dist/engine/cascade-hooks/hooks/review-enricher.hook.js +9 -6
- package/dist/engine/cascade-hooks/state-drift-detector.d.ts +3 -3
- package/dist/engine/cascade-hooks/state-drift-detector.js +1 -1
- package/dist/engine/ci-generator/planu-config.js +1 -7
- package/dist/engine/clarification-gate/gate.d.ts +3 -3
- package/dist/engine/code-scanner/layer-scanner.js +6 -22
- package/dist/engine/core-bridge.d.ts +3 -3
- package/dist/engine/cost-guardrails.d.ts +1 -1
- package/dist/engine/cost-guardrails.js +1 -1
- package/dist/engine/criterion-identity.d.ts +1 -0
- package/dist/engine/criterion-identity.js +7 -0
- package/dist/engine/dashboard/data-loader.js +2 -7
- package/dist/engine/dashboard/templates-layout.js +0 -3
- package/dist/engine/dashboard/templates-project.js +1 -13
- package/dist/engine/docs-site-generator/data-collector.d.ts +1 -1
- package/dist/engine/docs-site-generator/data-collector.js +7 -28
- package/dist/engine/docs-site-generator/tools-renderer.js +3 -10
- package/dist/engine/evidence-gates/artifact-reader.d.ts +10 -1
- package/dist/engine/evidence-gates/artifact-reader.js +115 -2
- package/dist/engine/evidence-gates/lifecycle-gate.js +4 -3
- package/dist/engine/evidence-gates/reconciliation-freshness.d.ts +11 -0
- package/dist/engine/evidence-gates/reconciliation-freshness.js +72 -0
- package/dist/engine/evidence-index/done-drift.d.ts +1 -1
- package/dist/engine/evidence-index/done-drift.js +40 -18
- package/dist/engine/execution/context.js +3 -0
- package/dist/engine/execution/durable-job-heartbeat-runtime.d.ts +4 -0
- package/dist/engine/execution/durable-job-heartbeat-runtime.js +400 -0
- package/dist/engine/execution/job-runtime.d.ts +30 -0
- package/dist/engine/execution/job-runtime.js +114 -6
- package/dist/engine/execution/public-job-sanitizer.d.ts +4 -0
- package/dist/engine/execution/public-job-sanitizer.js +31 -0
- package/dist/engine/execution/validate-job-executor.d.ts +12 -1
- package/dist/engine/execution/validate-job-executor.js +549 -86
- package/dist/engine/execution/validate-job-payload.js +107 -50
- package/dist/engine/execution/validate-job-recovery.js +1 -1
- package/dist/engine/execution/validate-worker-runtime.d.ts +5 -0
- package/dist/engine/execution/validate-worker-runtime.js +90 -2
- package/dist/engine/execution/validate-worker-thread.js +17 -24
- package/dist/engine/handoff-artifacts/schemas.d.ts +2 -1
- package/dist/engine/handoff-artifacts/schemas.js +33 -0
- package/dist/engine/handoff-packager.d.ts +19 -0
- package/dist/engine/handoff-packager.js +415 -13
- package/dist/engine/hooks/effort-configurator.js +1 -1
- package/dist/engine/impact-detector/test-break-predictor.js +3 -3
- package/dist/engine/impact-detector/tool-registration.js +1 -1
- package/dist/engine/implementation-contract/renderer.js +23 -8
- package/dist/engine/lifecycle-reconciliation-io.d.ts +11 -0
- package/dist/engine/lifecycle-reconciliation-io.js +180 -0
- package/dist/engine/lifecycle-reconciliation.d.ts +16 -0
- package/dist/engine/lifecycle-reconciliation.js +406 -0
- package/dist/engine/local-first/tool-classification.js +2 -0
- package/dist/engine/macos-keychain-native-port.d.ts +5 -0
- package/dist/engine/macos-keychain-native-port.js +19 -7
- package/dist/engine/migrations/remove-commercial-state.d.ts +3 -0
- package/dist/engine/migrations/remove-commercial-state.js +343 -0
- package/dist/engine/network-policy.js +1 -0
- package/dist/engine/planu-core.darwin-arm64.node.manifest.json +7 -7
- package/dist/engine/planu-core.darwin-arm64.node.sbom.json +10 -10
- package/dist/engine/planu-core.darwin-x64.node.manifest.json +7 -7
- package/dist/engine/planu-core.darwin-x64.node.sbom.json +10 -10
- package/dist/engine/planu-core.linux-arm64-gnu.node.manifest.json +7 -7
- package/dist/engine/planu-core.linux-arm64-gnu.node.sbom.json +10 -10
- package/dist/engine/planu-core.linux-arm64-musl.node.manifest.json +7 -7
- package/dist/engine/planu-core.linux-arm64-musl.node.sbom.json +10 -10
- package/dist/engine/planu-core.linux-x64-gnu.node.manifest.json +7 -7
- package/dist/engine/planu-core.linux-x64-gnu.node.sbom.json +10 -10
- package/dist/engine/planu-core.linux-x64-musl.node.manifest.json +7 -7
- package/dist/engine/planu-core.linux-x64-musl.node.sbom.json +10 -10
- package/dist/engine/planu-core.win32-arm64-msvc.node.manifest.json +7 -7
- package/dist/engine/planu-core.win32-arm64-msvc.node.sbom.json +10 -10
- package/dist/engine/planu-core.win32-x64-msvc.node.manifest.json +7 -7
- package/dist/engine/planu-core.win32-x64-msvc.node.sbom.json +10 -10
- package/dist/engine/readiness-checker.d.ts +1 -16
- package/dist/engine/readiness-checker.js +27 -88
- package/dist/engine/runtime-policy.d.ts +2 -0
- package/dist/engine/runtime-policy.js +42 -5
- package/dist/engine/runtime-security/rate-limiter.js +0 -1
- package/dist/engine/safety/contained-project-file.d.ts +3 -0
- package/dist/engine/safety/contained-project-file.js +23 -0
- package/dist/engine/scope-boundaries/contradiction-checker.js +63 -1
- package/dist/engine/scope-boundaries/scope-validator.js +45 -7
- package/dist/engine/session/checkpoint-writer.js +0 -9
- package/dist/engine/spec-format/acceptance-criteria.d.ts +6 -2
- package/dist/engine/spec-format/acceptance-criteria.js +77 -18
- package/dist/engine/spec-format/markdown-sections.d.ts +14 -0
- package/dist/engine/spec-format/markdown-sections.js +105 -0
- package/dist/engine/spec-format/read-technical-section.js +33 -5
- package/dist/engine/spec-format/technical-md-populator.js +3 -2
- package/dist/engine/spec-format/unified-spec-builder.d.ts +7 -0
- package/dist/engine/spec-format/unified-spec-builder.js +202 -0
- package/dist/engine/spec-migrator/canonical-deduplicator.d.ts +18 -0
- package/dist/engine/spec-migrator/canonical-deduplicator.js +479 -0
- package/dist/engine/spec-migrator/filesystem-import.d.ts +4 -8
- package/dist/engine/spec-migrator/filesystem-import.js +121 -14
- package/dist/engine/spec-migrator/frontmatter-parser.js +32 -15
- package/dist/engine/spec-migrator/index.d.ts +1 -0
- package/dist/engine/spec-migrator/index.js +1 -0
- package/dist/engine/spec-state-machine/transition-spec.d.ts +6 -1
- package/dist/engine/spec-state-machine/transition-spec.js +10 -1
- package/dist/engine/technical-enricher/index.js +16 -8
- package/dist/engine/technical-enricher/render-enriched.d.ts +1 -0
- package/dist/engine/technical-enricher/render-enriched.js +42 -14
- package/dist/engine/telemetry/health-checker.js +53 -17
- package/dist/engine/telemetry/telemetry-client.js +26 -8
- package/dist/engine/text-signal-boundaries.d.ts +2 -0
- package/dist/engine/text-signal-boundaries.js +25 -2
- package/dist/engine/time/relative-date.d.ts +3 -0
- package/dist/engine/time/relative-date.js +16 -0
- package/dist/engine/timing/budget.d.ts +11 -0
- package/dist/engine/timing/budget.js +27 -9
- package/dist/engine/token-optimizer/analytics.d.ts +1 -1
- package/dist/engine/token-optimizer/analytics.js +5 -6
- package/dist/engine/usage-tracker/core.d.ts +2 -2
- package/dist/engine/usage-tracker/core.js +1 -2
- package/dist/engine/usage-tracker/index.d.ts +0 -3
- package/dist/engine/usage-tracker/index.js +0 -3
- package/dist/engine/usage-tracker/stats.d.ts +2 -2
- package/dist/engine/usage-tracker/stats.js +1 -28
- package/dist/engine/validation/durable-validation.d.ts +1 -1
- package/dist/engine/validation/durable-validation.js +3 -82
- package/dist/engine/validation/validation-freshness.d.ts +11 -1
- package/dist/engine/validation/validation-freshness.js +231 -91
- package/dist/engine/validation/validation-receipt.d.ts +9 -6
- package/dist/engine/validation/validation-receipt.js +84 -24
- package/dist/engine/validation/validation-source-digest.d.ts +2 -0
- package/dist/engine/validation/validation-source-digest.js +86 -0
- package/dist/engine/validation/validation-submission.d.ts +5 -4
- package/dist/engine/validation/validation-submission.js +25 -11
- package/dist/engine/validation/validation-worktree.d.ts +21 -0
- package/dist/engine/validation/validation-worktree.js +260 -0
- package/dist/engine/validation-impact-planner.d.ts +14 -2
- package/dist/engine/validation-impact-planner.js +0 -3
- package/dist/engine/validator/dor-dod.d.ts +1 -0
- package/dist/engine/validator/dor-dod.js +5 -3
- package/dist/engine/validator/extractors.d.ts +1 -5
- package/dist/engine/validator/extractors.js +24 -69
- package/dist/engine/validator/spec-compliance-runner.d.ts +12 -24
- package/dist/engine/validator/spec-compliance-runner.js +429 -211
- package/dist/engine/validator.js +35 -22
- package/dist/index.js +4 -16
- package/dist/resources/usage-stats-resource.js +2 -18
- package/dist/security/adapters/linux-secret-service.js +7 -3
- package/dist/security/adapters/windows-credential-manager.js +6 -4
- package/dist/server/routes/specs.js +10 -0
- package/dist/storage/base-store.d.ts +1 -1
- package/dist/storage/base-store.js +1 -1
- package/dist/storage/feedback-remote.d.ts +2 -3
- package/dist/storage/feedback-remote.js +45 -31
- package/dist/storage/global-store.d.ts +1 -17
- package/dist/storage/global-store.js +1 -42
- package/dist/storage/index.d.ts +0 -2
- package/dist/storage/index.js +0 -2
- package/dist/storage/migrations/canonical-storage.js +124 -11
- package/dist/storage/runtime-db.d.ts +9 -0
- package/dist/storage/runtime-db.js +64 -8
- package/dist/storage/spec-store.d.ts +14 -2
- package/dist/storage/spec-store.js +104 -14
- package/dist/storage/status-store/atomic-status-write.d.ts +5 -0
- package/dist/storage/status-store/atomic-status-write.js +57 -0
- package/dist/storage/status-store/file-lock.js +30 -11
- package/dist/storage/status-store/self-healing.js +3 -3
- package/dist/storage/status-store/version-sync.d.ts +2 -1
- package/dist/storage/status-store/version-sync.js +35 -40
- package/dist/storage/usage-store.d.ts +1 -5
- package/dist/storage/usage-store.js +0 -35
- package/dist/tools/auto-update-handler.d.ts +3 -3
- package/dist/tools/auto-update-handler.js +3 -3
- package/dist/tools/challenge-spec/resilience-challenge-scenarios.d.ts +2 -1
- package/dist/tools/challenge-spec/resilience-challenge-scenarios.js +10 -7
- package/dist/tools/challenge-spec/scenario-collector.js +1 -1
- package/dist/tools/challenge-spec/scenarios-data.js +1 -1
- package/dist/tools/challenge-spec/scenarios-utils.d.ts +1 -1
- package/dist/tools/challenge-spec/scenarios-utils.js +35 -11
- package/dist/tools/challenge-spec.js +5 -5
- package/dist/tools/check-readiness.js +1 -1
- package/dist/tools/configure-telemetry.js +30 -2
- package/dist/tools/create-spec/spec-builder.js +0 -11
- package/dist/tools/create-spec.d.ts +1 -0
- package/dist/tools/create-spec.js +444 -239
- package/dist/tools/git/branch-ops.d.ts +1 -1
- package/dist/tools/git/branch-ops.js +3 -2
- package/dist/tools/init-project/handler.js +10 -12
- package/dist/tools/init-project/host-assets-writer.js +26 -1
- package/dist/tools/init-project/lint-scaffolder.d.ts +3 -3
- package/dist/tools/init-project/migration-runner.d.ts +2 -4
- package/dist/tools/init-project/migration-runner.js +3 -2
- package/dist/tools/init-project/portable-index-reconciler.d.ts +4 -0
- package/dist/tools/init-project/portable-index-reconciler.js +14 -0
- package/dist/tools/init-project/result-builder.js +1 -3
- package/dist/tools/jobs/handlers.js +38 -6
- package/dist/tools/list-specs.js +5 -26
- package/dist/tools/merge-risk-handler.js +2 -2
- package/dist/tools/package-handoff.js +2 -0
- package/dist/tools/reconcile-spec.js +0 -27
- package/dist/tools/register-agent-squad-tools.js +2 -2
- package/dist/tools/register-platform-tools/design-stack-tools.js +11 -11
- package/dist/tools/register-platform-tools/lifecycle-infra-tools.js +7 -7
- package/dist/tools/register-sdd-tools.d.ts +1 -1
- package/dist/tools/register-sdd-tools.js +2 -29
- package/dist/tools/register-spec-tools/analysis-tools.js +8 -8
- package/dist/tools/register-spec-tools/core-spec-tools.js +17 -8
- package/dist/tools/safe-handler.d.ts +5 -13
- package/dist/tools/safe-handler.js +7 -23
- package/dist/tools/schemas/index.d.ts +1 -1
- package/dist/tools/schemas/index.js +1 -1
- package/dist/tools/schemas/output-schemas.d.ts +0 -23
- package/dist/tools/schemas/output-schemas.js +0 -28
- package/dist/tools/schemas/spec.d.ts +4 -0
- package/dist/tools/schemas/spec.js +3 -0
- package/dist/tools/schemas/validate-output-schema.d.ts +11 -0
- package/dist/tools/schemas/validate-output-schema.js +7 -0
- package/dist/tools/spec-prompt-handler.js +3 -3
- package/dist/tools/sync-spec-state-handler.d.ts +1 -1
- package/dist/tools/sync-spec-state-handler.js +1 -1
- package/dist/tools/tool-entry.d.ts +4 -4
- package/dist/tools/tool-entry.js +3 -3
- package/dist/tools/tool-registry/core-tools.js +31 -15
- package/dist/tools/tool-registry/group-infra.js +27 -110
- package/dist/tools/tool-registry/group-quality-compliance.js +26 -26
- package/dist/tools/tool-registry/inventory.d.ts +2 -0
- package/dist/tools/tool-registry/inventory.js +2 -0
- package/dist/tools/tool-registry-helpers.d.ts +3 -3
- package/dist/tools/tool-registry-helpers.js +3 -3
- package/dist/tools/tool-usage-report/registered-tools.d.ts +1 -1
- package/dist/tools/tool-usage-report/registered-tools.js +3 -15
- package/dist/tools/update-status/approval-gates.js +3 -0
- package/dist/tools/update-status/done-receipt-verifier.js +40 -27
- package/dist/tools/update-status/evidence-gate.js +36 -1
- package/dist/tools/update-status/file-sync.d.ts +2 -1
- package/dist/tools/update-status/file-sync.js +57 -6
- package/dist/tools/update-status/index.d.ts +2 -0
- package/dist/tools/update-status/index.js +95 -58
- package/dist/tools/update-status/response-builder.js +3 -3
- package/dist/tools/update-status/side-effects.d.ts +2 -0
- package/dist/tools/update-status/side-effects.js +2 -2
- package/dist/tools/update-status/transition-guard.js +32 -15
- package/dist/tools/update-status-actions.d.ts +4 -2
- package/dist/tools/update-status-actions.js +16 -14
- package/dist/tools/usage-report.js +1 -16
- package/dist/tools/usage-stats.js +1 -18
- package/dist/tools/usage-tracking.d.ts +4 -0
- package/dist/tools/usage-tracking.js +20 -0
- package/dist/tools/validate-lint.d.ts +1 -0
- package/dist/tools/validate-lint.js +1 -1
- package/dist/tools/validate.d.ts +2 -1
- package/dist/tools/validate.js +114 -34
- package/dist/transports/http-transport.js +6 -8
- package/dist/transports/mcp-types.d.ts +0 -2
- package/dist/transports/session-context.js +1 -1
- package/dist/types/cascade-hooks.d.ts +1 -1
- package/dist/types/ci.d.ts +0 -4
- package/dist/types/clarification-token.d.ts +1 -1
- package/dist/types/claude-code-runtime.d.ts +0 -27
- package/dist/types/commercial-migration.d.ts +17 -0
- package/dist/types/commercial-migration.js +2 -0
- package/dist/types/common/primitives.d.ts +0 -2
- package/dist/types/core-bridge.d.ts +4 -10
- package/dist/types/dashboard.d.ts +3 -7
- package/dist/types/docs.d.ts +0 -1
- package/dist/types/durable-job.d.ts +4 -4
- package/dist/types/durable-validation.d.ts +100 -2
- package/dist/types/durable-validation.js +165 -1
- package/dist/types/errors/structured-error.d.ts +3 -3
- package/dist/types/estimation.d.ts +2 -0
- package/dist/types/evidence-gates.d.ts +9 -1
- package/dist/types/execution.d.ts +2 -0
- package/dist/types/gemini-integration.d.ts +0 -11
- package/dist/types/handoff-artifacts.d.ts +41 -0
- package/dist/types/hooks-advanced.d.ts +0 -13
- package/dist/types/impact-detection.d.ts +2 -2
- package/dist/types/index.d.ts +2 -6
- package/dist/types/index.js +1 -5
- package/dist/types/kiro-integration.d.ts +0 -6
- package/dist/types/network-policy.d.ts +1 -1
- package/dist/types/project/lint-scaffolder.d.ts +1 -1
- package/dist/types/runtime-policy.d.ts +11 -0
- package/dist/types/session-safeguard.d.ts +0 -31
- package/dist/types/spec/canonical-repair.d.ts +81 -0
- package/dist/types/spec/canonical-repair.js +2 -0
- package/dist/types/spec/core.d.ts +14 -0
- package/dist/types/spec/index.d.ts +1 -0
- package/dist/types/spec/index.js +1 -0
- package/dist/types/spec/inputs.d.ts +29 -0
- package/dist/types/spec-format.d.ts +24 -0
- package/dist/types/telemetry.d.ts +4 -1
- package/dist/types/transport.d.ts +0 -2
- package/dist/types/usage.d.ts +1 -36
- package/dist/types/usage.js +1 -1
- package/dist/types/validate-worker.d.ts +6 -0
- package/dist/types/validation-receipt.d.ts +1 -1
- package/dist/types/validation.d.ts +2 -2
- package/package.json +16 -12
- package/planu-native.json +1 -1
- package/planu-plugin.json +23 -40
- package/scripts/lib/pending-release-file.mjs +93 -0
- package/src/i18n/messages/en.json +0 -41
- package/src/i18n/messages/es.json +0 -41
- package/src/i18n/messages/pt.json +0 -41
- package/dist/cli/commands/activate.d.ts +0 -14
- package/dist/cli/commands/activate.js +0 -174
- package/dist/config/license-plans.json +0 -251
- package/dist/config/product-public.ts +0 -148
- package/dist/config/server-instructions.ts +0 -121
- package/dist/config/version-defaults.ts +0 -76
- package/dist/config/version.ts +0 -21
- package/dist/engine/ai-integration/gemini/settings-generator.d.ts +0 -4
- package/dist/engine/ai-integration/gemini/settings-generator.js +0 -195
- package/dist/engine/ai-integration/kiro/hooks-generator.d.ts +0 -6
- package/dist/engine/ai-integration/kiro/hooks-generator.js +0 -75
- package/dist/engine/hooks/full-spectrum-generator.d.ts +0 -32
- package/dist/engine/hooks/full-spectrum-generator.js +0 -117
- package/dist/engine/license-validator/config-loader.d.ts +0 -5
- package/dist/engine/license-validator/config-loader.js +0 -16
- package/dist/engine/license-validator/core.d.ts +0 -15
- package/dist/engine/license-validator/core.js +0 -151
- package/dist/engine/license-validator/fingerprint.d.ts +0 -3
- package/dist/engine/license-validator/fingerprint.js +0 -17
- package/dist/engine/license-validator/lemon-squeezy.d.ts +0 -5
- package/dist/engine/license-validator/lemon-squeezy.js +0 -55
- package/dist/engine/license-validator.d.ts +0 -5
- package/dist/engine/license-validator.js +0 -6
- package/dist/engine/product-intelligence/index.d.ts +0 -7
- package/dist/engine/product-intelligence/index.js +0 -211
- package/dist/engine/session/auto-save.d.ts +0 -18
- package/dist/engine/session/auto-save.js +0 -98
- package/dist/engine/session/context-snapshot.d.ts +0 -8
- package/dist/engine/session/context-snapshot.js +0 -49
- package/dist/engine/session/memory-sync.d.ts +0 -12
- package/dist/engine/session/memory-sync.js +0 -34
- package/dist/engine/session/session-diff.d.ts +0 -11
- package/dist/engine/session/session-diff.js +0 -42
- package/dist/engine/session/session-merge.d.ts +0 -12
- package/dist/engine/session/session-merge.js +0 -55
- package/dist/engine/session-safeguard/checkpoint-runner.d.ts +0 -4
- package/dist/engine/session-safeguard/checkpoint-runner.js +0 -70
- package/dist/engine/session-safeguard/learnings-buffer.d.ts +0 -15
- package/dist/engine/session-safeguard/learnings-buffer.js +0 -91
- package/dist/engine/session-safeguard/precompaction-drain.d.ts +0 -10
- package/dist/engine/session-safeguard/precompaction-drain.js +0 -82
- package/dist/engine/session-safeguard/unpushed-detector.d.ts +0 -7
- package/dist/engine/session-safeguard/unpushed-detector.js +0 -59
- package/dist/engine/spec-changelog/core.d.ts +0 -16
- package/dist/engine/spec-changelog/core.js +0 -176
- package/dist/engine/spec-changelog/diff.d.ts +0 -18
- package/dist/engine/spec-changelog/diff.js +0 -121
- package/dist/engine/spec-changelog/index.d.ts +0 -3
- package/dist/engine/spec-changelog/index.js +0 -4
- package/dist/engine/trial-engine.d.ts +0 -5
- package/dist/engine/trial-engine.js +0 -14
- package/dist/engine/usage-tracker/rate-check.d.ts +0 -7
- package/dist/engine/usage-tracker/rate-check.js +0 -18
- package/dist/engine/usage-tracker/rate-limiter.d.ts +0 -14
- package/dist/engine/usage-tracker/rate-limiter.js +0 -73
- package/dist/engine/usage-tracker/trial.d.ts +0 -27
- package/dist/engine/usage-tracker/trial.js +0 -49
- package/dist/engine/webhook/event-handlers.d.ts +0 -25
- package/dist/engine/webhook/event-handlers.js +0 -89
- package/dist/engine/webhook/index.d.ts +0 -4
- package/dist/engine/webhook/index.js +0 -5
- package/dist/engine/webhook/server.d.ts +0 -12
- package/dist/engine/webhook/server.js +0 -249
- package/dist/engine/webhook/signature.d.ts +0 -16
- package/dist/engine/webhook/signature.js +0 -49
- package/dist/storage/license-store.d.ts +0 -6
- package/dist/storage/license-store.js +0 -63
- package/dist/storage/session-store.d.ts +0 -20
- package/dist/storage/session-store.js +0 -136
- package/dist/tools/activate-license.d.ts +0 -6
- package/dist/tools/activate-license.js +0 -132
- package/dist/tools/checkpoint-handler.d.ts +0 -3
- package/dist/tools/checkpoint-handler.js +0 -130
- package/dist/tools/export-session.d.ts +0 -3
- package/dist/tools/export-session.js +0 -90
- package/dist/tools/license-gate.d.ts +0 -18
- package/dist/tools/license-gate.js +0 -222
- package/dist/tools/license-status.d.ts +0 -3
- package/dist/tools/license-status.js +0 -93
- package/dist/tools/list-sessions.d.ts +0 -3
- package/dist/tools/list-sessions.js +0 -46
- package/dist/tools/planu-session-checkpoint.d.ts +0 -6
- package/dist/tools/planu-session-checkpoint.js +0 -42
- package/dist/tools/restore-session.d.ts +0 -3
- package/dist/tools/restore-session.js +0 -71
- package/dist/tools/spec-history.d.ts +0 -3
- package/dist/tools/spec-history.js +0 -59
- package/dist/tools/webhook.d.ts +0 -6
- package/dist/tools/webhook.js +0 -150
- package/dist/types/changelog.d.ts +0 -49
- package/dist/types/changelog.js +0 -3
- package/dist/types/licensing.d.ts +0 -109
- package/dist/types/licensing.js +0 -4
- package/dist/types/product-intelligence.d.ts +0 -60
- package/dist/types/product-intelligence.js +0 -3
- package/dist/types/session.d.ts +0 -84
- package/dist/types/session.js +0 -3
- package/dist/types/webhook.d.ts +0 -62
- package/dist/types/webhook.js +0 -4
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
// engine/webhook/event-handlers.ts — Handles individual Lemon Squeezy webhook events.
|
|
2
|
-
// Each handler receives parsed payload and applies the corresponding business logic.
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
// Individual event handlers
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
/**
|
|
7
|
-
* Handles subscription_cancelled: the customer cancelled their subscription.
|
|
8
|
-
* The tier should be reverted to free when the subscription period ends.
|
|
9
|
-
*/
|
|
10
|
-
export function handleSubscriptionCancelled(payload) {
|
|
11
|
-
const attrs = payload.data.attributes;
|
|
12
|
-
const endsAt = attrs.ends_at ?? 'unknown date';
|
|
13
|
-
const productName = attrs.product_name ?? 'Planu subscription';
|
|
14
|
-
return {
|
|
15
|
-
success: true,
|
|
16
|
-
eventType: 'subscription_cancelled',
|
|
17
|
-
statusCode: 200,
|
|
18
|
-
message: [
|
|
19
|
-
`Subscription cancellation recorded: ${productName}`,
|
|
20
|
-
`Access continues until: ${endsAt}`,
|
|
21
|
-
'Tier will revert to free upon expiry.',
|
|
22
|
-
].join(' | '),
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Handles subscription_updated: plan tier may have changed (upgrade/downgrade).
|
|
27
|
-
*/
|
|
28
|
-
export function handleSubscriptionUpdated(payload) {
|
|
29
|
-
const attrs = payload.data.attributes;
|
|
30
|
-
const variantName = attrs.variant_name ?? 'unknown variant';
|
|
31
|
-
const status = attrs.status ?? 'unknown';
|
|
32
|
-
return {
|
|
33
|
-
success: true,
|
|
34
|
-
eventType: 'subscription_updated',
|
|
35
|
-
statusCode: 200,
|
|
36
|
-
message: [`Subscription updated: variant=${variantName}`, `status=${status}`].join(' | '),
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Handles license_key_created: a new license key was issued (after purchase).
|
|
41
|
-
* This is informational — the user will activate the key via activate_license tool.
|
|
42
|
-
*/
|
|
43
|
-
export function handleLicenseKeyCreated(payload) {
|
|
44
|
-
const attrs = payload.data.attributes;
|
|
45
|
-
const productName = attrs.product_name ?? 'Planu';
|
|
46
|
-
const userEmail = attrs.user_email ?? 'unknown';
|
|
47
|
-
return {
|
|
48
|
-
success: true,
|
|
49
|
-
eventType: 'license_key_created',
|
|
50
|
-
statusCode: 200,
|
|
51
|
-
message: [
|
|
52
|
-
`License key created for: ${productName}`,
|
|
53
|
-
`customer=${userEmail}`,
|
|
54
|
-
'Key can be activated using activate_license tool.',
|
|
55
|
-
].join(' | '),
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Returns a generic "acknowledged" result for unhandled but recognised events.
|
|
60
|
-
*/
|
|
61
|
-
export function handleUnknownEvent(eventType) {
|
|
62
|
-
return {
|
|
63
|
-
success: true,
|
|
64
|
-
eventType,
|
|
65
|
-
statusCode: 200,
|
|
66
|
-
message: `Event acknowledged (no handler): ${eventType}`,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
// ---------------------------------------------------------------------------
|
|
70
|
-
// Dispatcher
|
|
71
|
-
// ---------------------------------------------------------------------------
|
|
72
|
-
const HANDLERS = {
|
|
73
|
-
subscription_cancelled: handleSubscriptionCancelled,
|
|
74
|
-
subscription_updated: handleSubscriptionUpdated,
|
|
75
|
-
license_key_created: handleLicenseKeyCreated,
|
|
76
|
-
};
|
|
77
|
-
/**
|
|
78
|
-
* Dispatches a parsed webhook payload to the appropriate event handler.
|
|
79
|
-
* Unhandled but valid events are acknowledged with a 200 response.
|
|
80
|
-
*/
|
|
81
|
-
export function dispatchWebhookEvent(payload) {
|
|
82
|
-
const eventType = payload.meta.event_name;
|
|
83
|
-
const handler = HANDLERS[eventType];
|
|
84
|
-
if (handler) {
|
|
85
|
-
return handler(payload);
|
|
86
|
-
}
|
|
87
|
-
return handleUnknownEvent(eventType);
|
|
88
|
-
}
|
|
89
|
-
//# sourceMappingURL=event-handlers.js.map
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export { startWebhookServer, stopWebhookServer, getWebhookState, setWebhookSecret, } from './server.js';
|
|
2
|
-
export { validateSignature, extractSignatureHeader } from './signature.js';
|
|
3
|
-
export { dispatchWebhookEvent, handleSubscriptionCancelled, handleSubscriptionUpdated, handleLicenseKeyCreated, handleUnknownEvent, } from './event-handlers.js';
|
|
4
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
// engine/webhook/index.ts — Barrel re-export for the webhook engine module.
|
|
2
|
-
export { startWebhookServer, stopWebhookServer, getWebhookState, setWebhookSecret, } from './server.js';
|
|
3
|
-
export { validateSignature, extractSignatureHeader } from './signature.js';
|
|
4
|
-
export { dispatchWebhookEvent, handleSubscriptionCancelled, handleSubscriptionUpdated, handleLicenseKeyCreated, handleUnknownEvent, } from './event-handlers.js';
|
|
5
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { WebhookServerState } from '../../types/index.js';
|
|
2
|
-
/** Returns the current webhook server state (immutable snapshot). */
|
|
3
|
-
export declare function getWebhookState(): WebhookServerState;
|
|
4
|
-
/** Replaces the webhook secret used for HMAC-SHA256 validation. */
|
|
5
|
-
export declare function setWebhookSecret(secret: string): void;
|
|
6
|
-
/** Starts the HTTP webhook server on the given port. Idempotent. */
|
|
7
|
-
export declare function startWebhookServer(port?: number): Promise<WebhookServerState>;
|
|
8
|
-
/** Stops the running webhook server gracefully. Idempotent. */
|
|
9
|
-
export declare function stopWebhookServer(): Promise<WebhookServerState>;
|
|
10
|
-
/** Reset replay protection state (for testing only). */
|
|
11
|
-
export declare function _resetReplayState(): void;
|
|
12
|
-
//# sourceMappingURL=server.d.ts.map
|
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
// engine/webhook/server.ts — Lightweight HTTP webhook receiver using node:http.
|
|
2
|
-
// No framework dependencies — only built-in Node.js modules.
|
|
3
|
-
import { createServer } from 'node:http';
|
|
4
|
-
import { randomUUID, createHash } from 'node:crypto';
|
|
5
|
-
import { validateSignature, extractSignatureHeader } from './signature.js';
|
|
6
|
-
import { dispatchWebhookEvent } from './event-handlers.js';
|
|
7
|
-
// Constants
|
|
8
|
-
const DEFAULT_PORT = 2358;
|
|
9
|
-
const MAX_RECENT_EVENTS = 20;
|
|
10
|
-
const MAX_BODY_SIZE = 1024 * 1024; // 1 MB
|
|
11
|
-
const NONCE_CACHE_SIZE = 1000;
|
|
12
|
-
const MAX_TIMESTAMP_AGE_MS = 5 * 60 * 1000; // 5 minutes
|
|
13
|
-
const RATE_LIMIT_WINDOW_MS = 60 * 1000;
|
|
14
|
-
const RATE_LIMIT_MAX = 10;
|
|
15
|
-
// Module state
|
|
16
|
-
let activeServer = null;
|
|
17
|
-
let serverState = makeIdleState();
|
|
18
|
-
let webhookSecret = '';
|
|
19
|
-
const recentNonces = new Set();
|
|
20
|
-
const nonceQueue = [];
|
|
21
|
-
const rateLimitMap = new Map();
|
|
22
|
-
// Public API
|
|
23
|
-
/** Returns the current webhook server state (immutable snapshot). */
|
|
24
|
-
export function getWebhookState() {
|
|
25
|
-
return serverState;
|
|
26
|
-
}
|
|
27
|
-
/** Replaces the webhook secret used for HMAC-SHA256 validation. */
|
|
28
|
-
export function setWebhookSecret(secret) {
|
|
29
|
-
webhookSecret = secret;
|
|
30
|
-
}
|
|
31
|
-
/** Starts the HTTP webhook server on the given port. Idempotent. */
|
|
32
|
-
export async function startWebhookServer(port = DEFAULT_PORT) {
|
|
33
|
-
if (activeServer !== null) {
|
|
34
|
-
return serverState;
|
|
35
|
-
}
|
|
36
|
-
return new Promise((resolve, reject) => {
|
|
37
|
-
const server = createServer(handleRequest);
|
|
38
|
-
server.on('error', (err) => {
|
|
39
|
-
reject(err);
|
|
40
|
-
});
|
|
41
|
-
server.listen(port, () => {
|
|
42
|
-
activeServer = server;
|
|
43
|
-
serverState = {
|
|
44
|
-
running: true,
|
|
45
|
-
port,
|
|
46
|
-
startedAt: new Date().toISOString(),
|
|
47
|
-
eventsProcessed: 0,
|
|
48
|
-
recentEvents: [],
|
|
49
|
-
};
|
|
50
|
-
if (!webhookSecret) {
|
|
51
|
-
console.warn('[Planu] Warning: Webhook server started without a secret. All incoming requests will be accepted without signature verification. Set a secret via configure_webhook.');
|
|
52
|
-
}
|
|
53
|
-
resolve(serverState);
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
/** Stops the running webhook server gracefully. Idempotent. */
|
|
58
|
-
export async function stopWebhookServer() {
|
|
59
|
-
if (activeServer === null) {
|
|
60
|
-
serverState = makeIdleState();
|
|
61
|
-
return serverState;
|
|
62
|
-
}
|
|
63
|
-
const server = activeServer;
|
|
64
|
-
return new Promise((resolve, reject) => {
|
|
65
|
-
server.close((err) => {
|
|
66
|
-
/* v8 ignore start */
|
|
67
|
-
if (err) {
|
|
68
|
-
reject(err);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
/* v8 ignore stop */
|
|
72
|
-
activeServer = null;
|
|
73
|
-
serverState = makeIdleState();
|
|
74
|
-
resolve(serverState);
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
// HTTP request handler
|
|
79
|
-
function handleRequest(req, res) {
|
|
80
|
-
if (req.method !== 'POST') {
|
|
81
|
-
res.writeHead(405, { 'Content-Type': 'text/plain' });
|
|
82
|
-
res.end('Method Not Allowed');
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
collectBody(req)
|
|
86
|
-
.then((rawBody) => {
|
|
87
|
-
processWebhookRequest(rawBody, req.headers, res);
|
|
88
|
-
})
|
|
89
|
-
.catch(() => {
|
|
90
|
-
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
91
|
-
res.end('Bad Request');
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
function collectBody(req) {
|
|
95
|
-
return new Promise((resolve, reject) => {
|
|
96
|
-
const chunks = [];
|
|
97
|
-
let totalSize = 0;
|
|
98
|
-
req.on('data', (chunk) => {
|
|
99
|
-
totalSize += chunk.length;
|
|
100
|
-
if (totalSize > MAX_BODY_SIZE) {
|
|
101
|
-
reject(new Error('Payload too large'));
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
chunks.push(chunk);
|
|
105
|
-
});
|
|
106
|
-
req.on('end', () => {
|
|
107
|
-
resolve(Buffer.concat(chunks));
|
|
108
|
-
});
|
|
109
|
-
req.on('error', reject);
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
// Replay & rate-limit protection (SPEC-094 AC-7)
|
|
113
|
-
function isReplayAttack(headers, bodyStr) {
|
|
114
|
-
const tsHeader = headers['x-webhook-timestamp'] ?? headers['X-Webhook-Timestamp'];
|
|
115
|
-
const timestamp = typeof tsHeader === 'string' ? tsHeader : undefined;
|
|
116
|
-
if (timestamp) {
|
|
117
|
-
const age = Date.now() - new Date(timestamp).getTime();
|
|
118
|
-
if (age > MAX_TIMESTAMP_AGE_MS || age < -MAX_TIMESTAMP_AGE_MS) {
|
|
119
|
-
return 'Webhook timestamp too old or in the future';
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
const nonce = extractNonce(headers, bodyStr);
|
|
123
|
-
if (nonce && recentNonces.has(nonce)) {
|
|
124
|
-
return 'Duplicate webhook (replay detected)';
|
|
125
|
-
}
|
|
126
|
-
if (nonce) {
|
|
127
|
-
trackNonce(nonce);
|
|
128
|
-
}
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
function extractNonce(headers, bodyStr) {
|
|
132
|
-
const idHeader = headers['x-webhook-id'] ?? headers['X-Webhook-Id'];
|
|
133
|
-
if (typeof idHeader === 'string') {
|
|
134
|
-
return idHeader;
|
|
135
|
-
}
|
|
136
|
-
return createHash('sha256').update(bodyStr).digest('hex').slice(0, 32);
|
|
137
|
-
}
|
|
138
|
-
function trackNonce(nonce) {
|
|
139
|
-
recentNonces.add(nonce);
|
|
140
|
-
nonceQueue.push(nonce);
|
|
141
|
-
while (nonceQueue.length > NONCE_CACHE_SIZE) {
|
|
142
|
-
const old = nonceQueue.shift();
|
|
143
|
-
if (old) {
|
|
144
|
-
recentNonces.delete(old);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
function isRateLimited(source) {
|
|
149
|
-
const now = Date.now();
|
|
150
|
-
const entry = rateLimitMap.get(source);
|
|
151
|
-
if (!entry || now - entry.windowStart > RATE_LIMIT_WINDOW_MS) {
|
|
152
|
-
rateLimitMap.set(source, { count: 1, windowStart: now });
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
entry.count++;
|
|
156
|
-
return entry.count > RATE_LIMIT_MAX;
|
|
157
|
-
}
|
|
158
|
-
/** Reset replay protection state (for testing only). */
|
|
159
|
-
export function _resetReplayState() {
|
|
160
|
-
recentNonces.clear();
|
|
161
|
-
nonceQueue.length = 0;
|
|
162
|
-
rateLimitMap.clear();
|
|
163
|
-
}
|
|
164
|
-
function processWebhookRequest(rawBody, headers, res) {
|
|
165
|
-
// Rate limiting
|
|
166
|
-
const source = String(headers['x-forwarded-for'] ?? headers['x-real-ip'] ?? 'unknown');
|
|
167
|
-
if (isRateLimited(source)) {
|
|
168
|
-
res.writeHead(429, { 'Content-Type': 'text/plain' });
|
|
169
|
-
res.end('Too Many Requests');
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
// Replay protection
|
|
173
|
-
const replayReason = isReplayAttack(headers, rawBody.toString('utf8'));
|
|
174
|
-
if (replayReason) {
|
|
175
|
-
recordEvent('replay_rejected', false, replayReason, '');
|
|
176
|
-
res.writeHead(409, { 'Content-Type': 'text/plain' });
|
|
177
|
-
res.end(replayReason);
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
// Signature validation (only if a secret is configured)
|
|
181
|
-
if (webhookSecret) {
|
|
182
|
-
const sig = extractSignatureHeader(headers);
|
|
183
|
-
if (!validateSignature(rawBody, sig, webhookSecret)) {
|
|
184
|
-
recordEvent('signature_invalid', false, 'Signature validation failed', '');
|
|
185
|
-
res.writeHead(401, { 'Content-Type': 'text/plain' });
|
|
186
|
-
res.end('Unauthorized');
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
// Parse JSON body
|
|
191
|
-
let payload;
|
|
192
|
-
try {
|
|
193
|
-
payload = JSON.parse(rawBody.toString('utf8'));
|
|
194
|
-
}
|
|
195
|
-
catch {
|
|
196
|
-
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
197
|
-
res.end('Invalid JSON');
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
if (!payload.meta?.event_name) {
|
|
201
|
-
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
202
|
-
res.end('Invalid payload: missing meta.event_name');
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
// Dispatch event
|
|
206
|
-
const eventType = payload.meta.event_name;
|
|
207
|
-
try {
|
|
208
|
-
const result = dispatchWebhookEvent(payload);
|
|
209
|
-
recordEvent(eventType, result.success, result.message, result.eventType);
|
|
210
|
-
res.writeHead(result.statusCode, { 'Content-Type': 'application/json' });
|
|
211
|
-
res.end(JSON.stringify({ ok: true, message: result.message }));
|
|
212
|
-
/* v8 ignore start */
|
|
213
|
-
}
|
|
214
|
-
catch (err) {
|
|
215
|
-
const errMsg = err instanceof Error ? err.message : 'Unknown error';
|
|
216
|
-
recordEvent(eventType, false, errMsg, eventType);
|
|
217
|
-
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
|
218
|
-
res.end('Internal Server Error');
|
|
219
|
-
}
|
|
220
|
-
/* v8 ignore stop */
|
|
221
|
-
}
|
|
222
|
-
// Event recording
|
|
223
|
-
function recordEvent(_eventType, processed, summary, resolvedType) {
|
|
224
|
-
const record = {
|
|
225
|
-
id: randomUUID(),
|
|
226
|
-
eventType: resolvedType,
|
|
227
|
-
receivedAt: new Date().toISOString(),
|
|
228
|
-
processed,
|
|
229
|
-
summary,
|
|
230
|
-
...(processed ? {} : { error: summary }),
|
|
231
|
-
};
|
|
232
|
-
const recentEvents = [record, ...serverState.recentEvents].slice(0, MAX_RECENT_EVENTS);
|
|
233
|
-
serverState = {
|
|
234
|
-
...serverState,
|
|
235
|
-
eventsProcessed: serverState.eventsProcessed + 1,
|
|
236
|
-
recentEvents,
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
// Helpers
|
|
240
|
-
function makeIdleState() {
|
|
241
|
-
return {
|
|
242
|
-
running: false,
|
|
243
|
-
port: DEFAULT_PORT,
|
|
244
|
-
startedAt: null,
|
|
245
|
-
eventsProcessed: 0,
|
|
246
|
-
recentEvents: [],
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
//# sourceMappingURL=server.js.map
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Validates an HMAC-SHA256 signature from a Lemon Squeezy webhook request.
|
|
3
|
-
* Uses timing-safe comparison to prevent timing attacks.
|
|
4
|
-
*
|
|
5
|
-
* @param rawBody - The raw request body bytes (NOT parsed JSON)
|
|
6
|
-
* @param signatureHeader - The value of the X-Signature header
|
|
7
|
-
* @param secret - The webhook secret configured in Lemon Squeezy dashboard
|
|
8
|
-
* @returns true if signature is valid, false otherwise
|
|
9
|
-
*/
|
|
10
|
-
export declare function validateSignature(rawBody: Buffer, signatureHeader: string, secret: string): boolean;
|
|
11
|
-
/**
|
|
12
|
-
* Extracts the signature header value from a map of HTTP headers.
|
|
13
|
-
* Header names are case-insensitive in HTTP/1.1.
|
|
14
|
-
*/
|
|
15
|
-
export declare function extractSignatureHeader(headers: Record<string, string | string[] | undefined>): string;
|
|
16
|
-
//# sourceMappingURL=signature.d.ts.map
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
// engine/webhook/signature.ts — HMAC-SHA256 signature validation for Lemon Squeezy webhooks.
|
|
2
|
-
// Validates the X-Signature header against the raw request body.
|
|
3
|
-
import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
4
|
-
// ---------------------------------------------------------------------------
|
|
5
|
-
// Constants
|
|
6
|
-
// ---------------------------------------------------------------------------
|
|
7
|
-
const SIGNATURE_HEADER = 'x-signature';
|
|
8
|
-
const HMAC_ALGO = 'sha256';
|
|
9
|
-
// ---------------------------------------------------------------------------
|
|
10
|
-
// Public API
|
|
11
|
-
// ---------------------------------------------------------------------------
|
|
12
|
-
/**
|
|
13
|
-
* Validates an HMAC-SHA256 signature from a Lemon Squeezy webhook request.
|
|
14
|
-
* Uses timing-safe comparison to prevent timing attacks.
|
|
15
|
-
*
|
|
16
|
-
* @param rawBody - The raw request body bytes (NOT parsed JSON)
|
|
17
|
-
* @param signatureHeader - The value of the X-Signature header
|
|
18
|
-
* @param secret - The webhook secret configured in Lemon Squeezy dashboard
|
|
19
|
-
* @returns true if signature is valid, false otherwise
|
|
20
|
-
*/
|
|
21
|
-
export function validateSignature(rawBody, signatureHeader, secret) {
|
|
22
|
-
if (!secret || !signatureHeader) {
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
const expectedHex = createHmac(HMAC_ALGO, secret).update(rawBody).digest('hex');
|
|
26
|
-
try {
|
|
27
|
-
const expectedBuf = Buffer.from(expectedHex, 'hex');
|
|
28
|
-
const receivedBuf = Buffer.from(signatureHeader, 'hex');
|
|
29
|
-
if (expectedBuf.length !== receivedBuf.length) {
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
return timingSafeEqual(expectedBuf, receivedBuf);
|
|
33
|
-
}
|
|
34
|
-
catch /* v8 ignore start */ {
|
|
35
|
-
return false;
|
|
36
|
-
} /* v8 ignore stop */
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Extracts the signature header value from a map of HTTP headers.
|
|
40
|
-
* Header names are case-insensitive in HTTP/1.1.
|
|
41
|
-
*/
|
|
42
|
-
export function extractSignatureHeader(headers) {
|
|
43
|
-
const raw = headers[SIGNATURE_HEADER];
|
|
44
|
-
if (Array.isArray(raw)) {
|
|
45
|
-
return raw[0] ?? '';
|
|
46
|
-
}
|
|
47
|
-
return raw ?? '';
|
|
48
|
-
}
|
|
49
|
-
//# sourceMappingURL=signature.js.map
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { LicenseState } from '../types/index.js';
|
|
2
|
-
export declare function getLicenseState(): Promise<LicenseState | null>;
|
|
3
|
-
export declare function saveLicenseState(state: LicenseState): Promise<void>;
|
|
4
|
-
export declare function deleteLicenseState(): Promise<void>;
|
|
5
|
-
export declare function countActiveProjects(): Promise<number>;
|
|
6
|
-
//# sourceMappingURL=license-store.d.ts.map
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
// storage/license-store.ts — Persistence layer for license state (~/.planu/data/global/license.json).
|
|
2
|
-
import { readdir } from 'node:fs/promises';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
import { readJson, globalDataDir } from './base-store.js';
|
|
5
|
-
import { withFileLock } from './file-mutex.js';
|
|
6
|
-
import { resolveStorageLayout } from './storage-layout.js';
|
|
7
|
-
import { deleteSecretField, resolveSecretField, storeSecretField, } from '../security/secret-field.js';
|
|
8
|
-
import { writeSecureJson } from '../security/secure-json.js';
|
|
9
|
-
// ---------------------------------------------------------------------------
|
|
10
|
-
// Paths
|
|
11
|
-
// ---------------------------------------------------------------------------
|
|
12
|
-
function licenseFile() {
|
|
13
|
-
return join(globalDataDir(), 'license.json');
|
|
14
|
-
}
|
|
15
|
-
// ---------------------------------------------------------------------------
|
|
16
|
-
// Public API
|
|
17
|
-
// ---------------------------------------------------------------------------
|
|
18
|
-
export async function getLicenseState() {
|
|
19
|
-
const state = await readJson(licenseFile(), null);
|
|
20
|
-
if (!state) {
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
return { ...state, licenseKey: await resolveSecretField(state.licenseKey) };
|
|
24
|
-
}
|
|
25
|
-
export async function saveLicenseState(state) {
|
|
26
|
-
await withFileLock(licenseFile(), async () => {
|
|
27
|
-
const licenseKey = await storeSecretField('global/license', 'license', state.licenseKey);
|
|
28
|
-
await writeSecureJson(licenseFile(), { ...state, licenseKey });
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
export async function deleteLicenseState() {
|
|
32
|
-
const { unlink } = await import('node:fs/promises');
|
|
33
|
-
try {
|
|
34
|
-
const state = await readJson(licenseFile(), null);
|
|
35
|
-
await deleteSecretField(state?.licenseKey);
|
|
36
|
-
await unlink(licenseFile());
|
|
37
|
-
}
|
|
38
|
-
catch (err) {
|
|
39
|
-
if (isNodeError(err) && err.code === 'ENOENT') {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
throw err;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
export async function countActiveProjects() {
|
|
46
|
-
try {
|
|
47
|
-
const entries = await readdir(resolveStorageLayout().projects, { withFileTypes: true });
|
|
48
|
-
return entries.filter((e) => e.isDirectory()).length;
|
|
49
|
-
}
|
|
50
|
-
catch (err) {
|
|
51
|
-
if (isNodeError(err) && err.code === 'ENOENT') {
|
|
52
|
-
return 0;
|
|
53
|
-
}
|
|
54
|
-
throw err;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
// ---------------------------------------------------------------------------
|
|
58
|
-
// Internal helpers
|
|
59
|
-
// ---------------------------------------------------------------------------
|
|
60
|
-
function isNodeError(err) {
|
|
61
|
-
return err instanceof Error && 'code' in err;
|
|
62
|
-
}
|
|
63
|
-
//# sourceMappingURL=license-store.js.map
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { Session, SessionMemory, SessionListItem } from '../types/index.js';
|
|
2
|
-
/** Save a session to disk. */
|
|
3
|
-
export declare function save(session: Session): Promise<void>;
|
|
4
|
-
/** Load a session by ID. Returns null if not found. */
|
|
5
|
-
export declare function load(projectId: string, sessionId: string): Promise<Session | null>;
|
|
6
|
-
/** List sessions for a project, ordered by lastSavedAt desc. */
|
|
7
|
-
export declare function list(projectId: string): Promise<Session[]>;
|
|
8
|
-
/** Get the most recent session for a project. */
|
|
9
|
-
export declare function getLatest(projectId: string): Promise<Session | null>;
|
|
10
|
-
/** Delete a session by ID. Returns true if removed. */
|
|
11
|
-
export declare function deleteSession(projectId: string, sessionId: string): Promise<boolean>;
|
|
12
|
-
/** List sessions as lightweight items for display. */
|
|
13
|
-
export declare function listItems(projectId: string, limit?: number): Promise<SessionListItem[]>;
|
|
14
|
-
/** Clean up old sessions: keep max 50, remove sessions older than 30 days. */
|
|
15
|
-
export declare function cleanup(projectId: string): Promise<number>;
|
|
16
|
-
/** Load cross-conversation memory for a project. */
|
|
17
|
-
export declare function loadMemory(projectId: string): Promise<SessionMemory>;
|
|
18
|
-
/** Save cross-conversation memory. */
|
|
19
|
-
export declare function saveMemory(memory: SessionMemory): Promise<void>;
|
|
20
|
-
//# sourceMappingURL=session-store.d.ts.map
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { readJson, writeJson, projectDataDir } from './base-store.js';
|
|
2
|
-
import { withFileLock } from './file-mutex.js';
|
|
3
|
-
import { readdir, unlink } from 'node:fs/promises';
|
|
4
|
-
// --- internal helpers ---
|
|
5
|
-
function sanitizeId(id) {
|
|
6
|
-
const sanitized = id.replace(/[/\\]/g, '_').replace(/\.\./g, '_');
|
|
7
|
-
if (!sanitized || sanitized === '.' || sanitized === '..') {
|
|
8
|
-
throw new Error(`Invalid ID: ${id}`);
|
|
9
|
-
}
|
|
10
|
-
return sanitized;
|
|
11
|
-
}
|
|
12
|
-
function sessionsDir(projectId) {
|
|
13
|
-
return `${projectDataDir(projectId)}/sessions`;
|
|
14
|
-
}
|
|
15
|
-
function sessionFile(projectId, sessionId) {
|
|
16
|
-
return `${sessionsDir(projectId)}/session-${sanitizeId(sessionId)}.json`;
|
|
17
|
-
}
|
|
18
|
-
function memoryFile(projectId) {
|
|
19
|
-
return `${projectDataDir(projectId)}/session-memory.json`;
|
|
20
|
-
}
|
|
21
|
-
async function listSessionFiles(projectId) {
|
|
22
|
-
try {
|
|
23
|
-
const dir = sessionsDir(projectId);
|
|
24
|
-
const entries = await readdir(dir);
|
|
25
|
-
return entries.filter((f) => f.startsWith('session-') && f.endsWith('.json'));
|
|
26
|
-
}
|
|
27
|
-
catch (error) {
|
|
28
|
-
if (error.code === 'ENOENT' ||
|
|
29
|
-
(error instanceof Error && error.message === 'ENOENT')) {
|
|
30
|
-
return [];
|
|
31
|
-
}
|
|
32
|
-
throw error;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
// --- public API ---
|
|
36
|
-
/** Save a session to disk. */
|
|
37
|
-
export async function save(session) {
|
|
38
|
-
const file = sessionFile(session.projectId, session.id);
|
|
39
|
-
await withFileLock(file, () => writeJson(file, session));
|
|
40
|
-
}
|
|
41
|
-
/** Load a session by ID. Returns null if not found. */
|
|
42
|
-
export async function load(projectId, sessionId) {
|
|
43
|
-
return readJson(sessionFile(projectId, sessionId), null);
|
|
44
|
-
}
|
|
45
|
-
/** List sessions for a project, ordered by lastSavedAt desc. */
|
|
46
|
-
export async function list(projectId) {
|
|
47
|
-
const files = await listSessionFiles(projectId);
|
|
48
|
-
const sessions = [];
|
|
49
|
-
for (const f of files) {
|
|
50
|
-
const id = f.replace('session-', '').replace('.json', '');
|
|
51
|
-
const s = await load(projectId, id);
|
|
52
|
-
if (s) {
|
|
53
|
-
sessions.push(s);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
sessions.sort((a, b) => b.lastSavedAt.localeCompare(a.lastSavedAt));
|
|
57
|
-
return sessions;
|
|
58
|
-
}
|
|
59
|
-
/** Get the most recent session for a project. */
|
|
60
|
-
export async function getLatest(projectId) {
|
|
61
|
-
const all = await list(projectId);
|
|
62
|
-
return all[0] ?? null;
|
|
63
|
-
}
|
|
64
|
-
/** Delete a session by ID. Returns true if removed. */
|
|
65
|
-
export async function deleteSession(projectId, sessionId) {
|
|
66
|
-
try {
|
|
67
|
-
await unlink(sessionFile(projectId, sessionId));
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
catch (error) {
|
|
71
|
-
if (error.code === 'ENOENT' ||
|
|
72
|
-
(error instanceof Error && error.message === 'ENOENT')) {
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
throw error;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
/** List sessions as lightweight items for display. */
|
|
79
|
-
export async function listItems(projectId, limit = 10) {
|
|
80
|
-
const all = await list(projectId);
|
|
81
|
-
return all.slice(0, limit).map((s) => ({
|
|
82
|
-
id: s.id,
|
|
83
|
-
startedAt: s.startedAt,
|
|
84
|
-
lastSavedAt: s.lastSavedAt,
|
|
85
|
-
contextSummary: s.contextSummary.slice(0, 200),
|
|
86
|
-
toolCallCount: s.toolCallCount,
|
|
87
|
-
}));
|
|
88
|
-
}
|
|
89
|
-
// --- cleanup (AC-10) ---
|
|
90
|
-
const MAX_SESSIONS = 50;
|
|
91
|
-
const MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000; // 30 days
|
|
92
|
-
/** Clean up old sessions: keep max 50, remove sessions older than 30 days. */
|
|
93
|
-
export async function cleanup(projectId) {
|
|
94
|
-
const all = await list(projectId);
|
|
95
|
-
let removed = 0;
|
|
96
|
-
const now = Date.now();
|
|
97
|
-
// Remove sessions older than 30 days
|
|
98
|
-
for (const s of all) {
|
|
99
|
-
const age = now - new Date(s.lastSavedAt).getTime();
|
|
100
|
-
if (age > MAX_AGE_MS) {
|
|
101
|
-
await deleteSession(projectId, s.id);
|
|
102
|
-
removed++;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
// Remove excess beyond MAX_SESSIONS (oldest first, list is already desc)
|
|
106
|
-
const remaining = all.length - removed;
|
|
107
|
-
if (remaining > MAX_SESSIONS) {
|
|
108
|
-
const toRemove = all.slice(MAX_SESSIONS).filter((s) => {
|
|
109
|
-
const age = now - new Date(s.lastSavedAt).getTime();
|
|
110
|
-
return age <= MAX_AGE_MS; // not already removed above
|
|
111
|
-
});
|
|
112
|
-
for (const s of toRemove) {
|
|
113
|
-
await deleteSession(projectId, s.id);
|
|
114
|
-
removed++;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
if (removed > 0) {
|
|
118
|
-
console.warn(`[Planu] Cleaned up ${String(removed)} old sessions for project ${projectId}`);
|
|
119
|
-
}
|
|
120
|
-
return removed;
|
|
121
|
-
}
|
|
122
|
-
// --- session memory (AC-11) ---
|
|
123
|
-
/** Load cross-conversation memory for a project. */
|
|
124
|
-
export async function loadMemory(projectId) {
|
|
125
|
-
return readJson(memoryFile(projectId), {
|
|
126
|
-
projectId,
|
|
127
|
-
decisions: [],
|
|
128
|
-
updatedAt: new Date().toISOString(),
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
/** Save cross-conversation memory. */
|
|
132
|
-
export async function saveMemory(memory) {
|
|
133
|
-
const file = memoryFile(memory.projectId);
|
|
134
|
-
await withFileLock(file, () => writeJson(file, memory));
|
|
135
|
-
}
|
|
136
|
-
//# sourceMappingURL=session-store.js.map
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { ActivateLicenseInput, ToolResult } from '../types/index.js';
|
|
2
|
-
export declare function handleActivateLicense(args: ActivateLicenseInput): Promise<ToolResult>;
|
|
3
|
-
export declare function handleDeactivateLicense(args: {
|
|
4
|
-
confirm: boolean;
|
|
5
|
-
}): Promise<ToolResult>;
|
|
6
|
-
//# sourceMappingURL=activate-license.d.ts.map
|