@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,132 +0,0 @@
|
|
|
1
|
-
// tools/activate-license.ts — Handler for activate_license MCP tool.
|
|
2
|
-
// Activates a license key via Lemon Squeezy API and persists the state locally.
|
|
3
|
-
import { hostname, platform, userInfo } from 'node:os';
|
|
4
|
-
import { basename } from 'node:path';
|
|
5
|
-
import { licenseStore } from '../storage/index.js';
|
|
6
|
-
import { activateLicense, computeMachineFingerprint, deactivateLicense, loadLicensePlans, variantToTier, } from '../engine/license-validator.js';
|
|
7
|
-
import { invalidateTierCache } from './license-gate.js';
|
|
8
|
-
import { t, ti } from '../i18n/index.js';
|
|
9
|
-
function buildInstanceName() {
|
|
10
|
-
const host = hostname();
|
|
11
|
-
const user = userInfo().username;
|
|
12
|
-
const os = platform();
|
|
13
|
-
const project = basename(process.cwd());
|
|
14
|
-
return `planu-${host} · ${user}@${os} · ${project}`;
|
|
15
|
-
}
|
|
16
|
-
export async function handleActivateLicense(args) {
|
|
17
|
-
const { licenseKey, instanceName } = args;
|
|
18
|
-
if (!licenseKey || licenseKey.trim().length === 0) {
|
|
19
|
-
return {
|
|
20
|
-
content: [{ type: 'text', text: t('tools.activate_license.keyRequired') }],
|
|
21
|
-
isError: true,
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
const name = instanceName ?? buildInstanceName();
|
|
25
|
-
try {
|
|
26
|
-
const response = await activateLicense(licenseKey.trim(), name);
|
|
27
|
-
if (!response.activated) {
|
|
28
|
-
return {
|
|
29
|
-
content: [
|
|
30
|
-
{
|
|
31
|
-
type: 'text',
|
|
32
|
-
text: ti('tools.activate_license.activationFailed', {
|
|
33
|
-
error: response.error ?? 'Unknown error',
|
|
34
|
-
}),
|
|
35
|
-
},
|
|
36
|
-
],
|
|
37
|
-
isError: true,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
const tier = variantToTier(response.meta.variant_name);
|
|
41
|
-
const now = new Date().toISOString();
|
|
42
|
-
const state = {
|
|
43
|
-
licenseKey: response.license_key.key,
|
|
44
|
-
instanceId: response.instance.id,
|
|
45
|
-
tier,
|
|
46
|
-
activatedAt: now,
|
|
47
|
-
lastValidatedAt: now,
|
|
48
|
-
variantName: response.meta.variant_name,
|
|
49
|
-
customerName: response.meta.customer_name,
|
|
50
|
-
customerEmail: response.meta.customer_email,
|
|
51
|
-
machineFingerprint: computeMachineFingerprint(),
|
|
52
|
-
activationLimit: response.license_key.activation_limit,
|
|
53
|
-
activationUsage: response.license_key.activation_usage,
|
|
54
|
-
};
|
|
55
|
-
await licenseStore.saveLicenseState(state);
|
|
56
|
-
invalidateTierCache();
|
|
57
|
-
const tierLabel = tier.charAt(0).toUpperCase() + tier.slice(1);
|
|
58
|
-
return {
|
|
59
|
-
content: [
|
|
60
|
-
{
|
|
61
|
-
type: 'text',
|
|
62
|
-
text: [
|
|
63
|
-
`✅ ${t('tools.activate_license.activatedSuccess')}`,
|
|
64
|
-
'',
|
|
65
|
-
ti('tools.activate_license.plan', { tier: tierLabel }),
|
|
66
|
-
ti('tools.activate_license.customer', { name: response.meta.customer_name }),
|
|
67
|
-
ti('tools.activate_license.instance', { name }),
|
|
68
|
-
'',
|
|
69
|
-
tier === 'pro'
|
|
70
|
-
? t('tools.activate_license.proAccess')
|
|
71
|
-
: ti('tools.activate_license.tierAccess', { tier }),
|
|
72
|
-
].join('\n'),
|
|
73
|
-
},
|
|
74
|
-
],
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
catch (error) {
|
|
78
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
79
|
-
return {
|
|
80
|
-
content: [
|
|
81
|
-
{
|
|
82
|
-
type: 'text',
|
|
83
|
-
text: [
|
|
84
|
-
ti('tools.activate_license.activationFailed', { error: message }),
|
|
85
|
-
'',
|
|
86
|
-
t('tools.activate_license.commonCauses'),
|
|
87
|
-
`• ${t('tools.activate_license.causeInvalidKey')}`,
|
|
88
|
-
`• ${t('tools.activate_license.causeAlreadyActivated')}`,
|
|
89
|
-
`• ${t('tools.activate_license.causeNetwork')}`,
|
|
90
|
-
].join('\n'),
|
|
91
|
-
},
|
|
92
|
-
],
|
|
93
|
-
isError: true,
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
export async function handleDeactivateLicense(args) {
|
|
98
|
-
if (!args.confirm) {
|
|
99
|
-
return {
|
|
100
|
-
content: [{ type: 'text', text: t('tools.activate_license.deactivationCancelled') }],
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
const state = await licenseStore.getLicenseState();
|
|
104
|
-
if (!state) {
|
|
105
|
-
return {
|
|
106
|
-
content: [{ type: 'text', text: t('tools.activate_license.noActiveLicense') }],
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
try {
|
|
110
|
-
await deactivateLicense(state.licenseKey, state.instanceId);
|
|
111
|
-
}
|
|
112
|
-
catch {
|
|
113
|
-
// Even if remote deactivation fails, we clear local state
|
|
114
|
-
}
|
|
115
|
-
await licenseStore.deleteLicenseState();
|
|
116
|
-
invalidateTierCache();
|
|
117
|
-
return {
|
|
118
|
-
content: [
|
|
119
|
-
{
|
|
120
|
-
type: 'text',
|
|
121
|
-
text: [
|
|
122
|
-
`✅ ${t('tools.activate_license.deactivatedSuccess')}`,
|
|
123
|
-
ti('tools.activate_license.deactivatedFreePlan', {
|
|
124
|
-
count: String(loadLicensePlans().freeTools.length),
|
|
125
|
-
}),
|
|
126
|
-
t('tools.activate_license.keyReusable'),
|
|
127
|
-
].join('\n'),
|
|
128
|
-
},
|
|
129
|
-
],
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
//# sourceMappingURL=activate-license.js.map
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
// tools/checkpoint-handler.ts — Handler for session_checkpoint tool (SPEC-220).
|
|
2
|
-
import { execFileSync } from 'node:child_process';
|
|
3
|
-
import { saveSessionState, getStagedFiles, getActiveSpecIds, } from '../storage/session-state-store.js';
|
|
4
|
-
import { hashProjectPath } from '../storage/base-store.js';
|
|
5
|
-
import { listSpecs } from '../storage/spec-store.js';
|
|
6
|
-
// ---------------------------------------------------------------------------
|
|
7
|
-
// Internal helpers
|
|
8
|
-
// ---------------------------------------------------------------------------
|
|
9
|
-
const SEPARATOR = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━';
|
|
10
|
-
function formatList(items) {
|
|
11
|
-
if (items.length === 0) {
|
|
12
|
-
return '(none)';
|
|
13
|
-
}
|
|
14
|
-
return items.map((i) => `- ${i}`).join('\n');
|
|
15
|
-
}
|
|
16
|
-
function buildHandoffBlock(savedAt, activeSpecs, decisions, nextSteps, stagedFiles) {
|
|
17
|
-
const lines = [
|
|
18
|
-
`Saved: ${savedAt}`,
|
|
19
|
-
'',
|
|
20
|
-
`Active specs: ${activeSpecs.length > 0 ? activeSpecs.join(', ') : '(none)'}`,
|
|
21
|
-
'',
|
|
22
|
-
'Decisions:',
|
|
23
|
-
formatList(decisions),
|
|
24
|
-
'',
|
|
25
|
-
'Next steps:',
|
|
26
|
-
formatList(nextSteps),
|
|
27
|
-
'',
|
|
28
|
-
`Staged files: ${stagedFiles.length > 0 ? stagedFiles.join(', ') : '(none)'}`,
|
|
29
|
-
];
|
|
30
|
-
return lines.join('\n');
|
|
31
|
-
}
|
|
32
|
-
async function resolveActiveSpecs(projectPath, projectId) {
|
|
33
|
-
// Try spec store first (in-progress specs)
|
|
34
|
-
try {
|
|
35
|
-
const resolvedId = projectId !== '' ? projectId : hashProjectPath(projectPath);
|
|
36
|
-
const specs = await listSpecs(resolvedId);
|
|
37
|
-
const inProgress = specs
|
|
38
|
-
.filter((s) => s.status === 'implementing')
|
|
39
|
-
.map((s) => `${s.id} (${s.status})`);
|
|
40
|
-
if (inProgress.length > 0) {
|
|
41
|
-
return inProgress;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
// Fallback to filesystem scan
|
|
46
|
-
}
|
|
47
|
-
return getActiveSpecIds(projectPath);
|
|
48
|
-
}
|
|
49
|
-
// ---------------------------------------------------------------------------
|
|
50
|
-
// Pending HTML changes notification (SPEC-326)
|
|
51
|
-
// ---------------------------------------------------------------------------
|
|
52
|
-
function checkPlanuUncommittedChanges(projectPath) {
|
|
53
|
-
try {
|
|
54
|
-
const output = execFileSync('git', ['status', '--porcelain', 'planu/'], {
|
|
55
|
-
cwd: projectPath,
|
|
56
|
-
encoding: 'utf-8',
|
|
57
|
-
timeout: 5_000,
|
|
58
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
59
|
-
});
|
|
60
|
-
return output.trim().length > 0;
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
// ---------------------------------------------------------------------------
|
|
67
|
-
// Worktree teardown recommendation (SPEC-279)
|
|
68
|
-
// ---------------------------------------------------------------------------
|
|
69
|
-
function buildWorktreeTeardownRecommendation(activeSpecs) {
|
|
70
|
-
const specNames = activeSpecs.map((s) => s.replace(/ \(implementing\)$/, '')).join(', ');
|
|
71
|
-
return {
|
|
72
|
-
type: 'worktree-teardown',
|
|
73
|
-
message: `Parallel sprint detected — ${String(activeSpecs.length)} specs in progress (${specNames}). Run these steps to close the sprint safely:`,
|
|
74
|
-
steps: [
|
|
75
|
-
'For each worktree: bash scripts/auto-merge.sh (full pipeline + merge to develop)',
|
|
76
|
-
'bash scripts/prune-worktrees.sh (clean up merged/orphan worktrees)',
|
|
77
|
-
'pnpm test:coverage (verify thresholds before promoting to main)',
|
|
78
|
-
'git checkout main && git merge --ff-only develop && git push origin main',
|
|
79
|
-
],
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
// ---------------------------------------------------------------------------
|
|
83
|
-
// Handler
|
|
84
|
-
// ---------------------------------------------------------------------------
|
|
85
|
-
export async function handleSessionCheckpoint(args) {
|
|
86
|
-
const { projectPath, projectId, decisions = [], nextSteps = [] } = args;
|
|
87
|
-
const savedAt = new Date().toISOString();
|
|
88
|
-
const stagedFiles = getStagedFiles(projectPath);
|
|
89
|
-
const activeSpecs = await resolveActiveSpecs(projectPath, projectId);
|
|
90
|
-
await saveSessionState(projectPath, {
|
|
91
|
-
savedAt,
|
|
92
|
-
activeSpecs,
|
|
93
|
-
recentDecisions: decisions.slice(0, 5),
|
|
94
|
-
stagedFiles,
|
|
95
|
-
nextSteps,
|
|
96
|
-
});
|
|
97
|
-
const handoff = buildHandoffBlock(savedAt, activeSpecs, decisions, nextSteps, stagedFiles);
|
|
98
|
-
// SPEC-256: always include /btw tip in checkpoint recommendations
|
|
99
|
-
const recommendations = [
|
|
100
|
-
{
|
|
101
|
-
type: 'context-tip',
|
|
102
|
-
message: 'Use /btw for side questions during implementation — keeps context clean for the task. Example: /btw what does useEffect cleanup do?',
|
|
103
|
-
command: '/btw your question here',
|
|
104
|
-
},
|
|
105
|
-
];
|
|
106
|
-
// SPEC-279: emit worktree teardown instructions when a parallel sprint is detected
|
|
107
|
-
if (activeSpecs.length >= 2) {
|
|
108
|
-
recommendations.push(buildWorktreeTeardownRecommendation(activeSpecs));
|
|
109
|
-
}
|
|
110
|
-
// SPEC-326: Pending HTML changes notification
|
|
111
|
-
const planuChanges = checkPlanuUncommittedChanges(projectPath);
|
|
112
|
-
const planuReminder = planuChanges
|
|
113
|
-
? '\n\n📁 Spec changes pending — commit to save: `git add planu/specs/ planu/conventions.json && git commit -m "chore(planu): save spec updates"`'
|
|
114
|
-
: '';
|
|
115
|
-
const text = [
|
|
116
|
-
`Session checkpoint saved — ${savedAt}`,
|
|
117
|
-
'',
|
|
118
|
-
'Session Handoff Block (paste in new session):',
|
|
119
|
-
SEPARATOR,
|
|
120
|
-
handoff,
|
|
121
|
-
SEPARATOR,
|
|
122
|
-
].join('\n') + planuReminder;
|
|
123
|
-
return {
|
|
124
|
-
content: [{ type: 'text', text }],
|
|
125
|
-
structuredContent: {
|
|
126
|
-
recommendations,
|
|
127
|
-
},
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
//# sourceMappingURL=checkpoint-handler.js.map
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { hashProjectPath } from '../storage/base-store.js';
|
|
2
|
-
import * as sessionStore from '../storage/session-store.js';
|
|
3
|
-
function formatAsMarkdown(session) {
|
|
4
|
-
const lines = [];
|
|
5
|
-
lines.push(`# Session Export: ${session.id}`);
|
|
6
|
-
lines.push('');
|
|
7
|
-
lines.push(`## Summary`);
|
|
8
|
-
lines.push(`- **Project**: ${session.projectId}`);
|
|
9
|
-
lines.push(`- **Started**: ${session.startedAt}`);
|
|
10
|
-
lines.push(`- **Last Saved**: ${session.lastSavedAt}`);
|
|
11
|
-
lines.push(`- **Tool Calls**: ${String(session.toolCallCount)}`);
|
|
12
|
-
lines.push('');
|
|
13
|
-
if (session.activeSpecs.length > 0) {
|
|
14
|
-
lines.push('## Active Specs');
|
|
15
|
-
for (const spec of session.activeSpecs) {
|
|
16
|
-
lines.push(`- **${spec.specId}** (${spec.status}): ${spec.summary}`);
|
|
17
|
-
}
|
|
18
|
-
lines.push('');
|
|
19
|
-
}
|
|
20
|
-
if (session.decisions.length > 0) {
|
|
21
|
-
lines.push('## Decisions');
|
|
22
|
-
for (const d of session.decisions) {
|
|
23
|
-
const specRef = d.specId ? ` [${d.specId}]` : '';
|
|
24
|
-
lines.push(`- **${d.decision}**${specRef}`);
|
|
25
|
-
if (d.rationale) {
|
|
26
|
-
lines.push(` - _${d.rationale}_`);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
lines.push('');
|
|
30
|
-
}
|
|
31
|
-
if (session.modifiedFiles.length > 0) {
|
|
32
|
-
lines.push('## Modified Files');
|
|
33
|
-
for (const f of session.modifiedFiles) {
|
|
34
|
-
lines.push(`- \`${f.path}\` (${f.action})`);
|
|
35
|
-
}
|
|
36
|
-
lines.push('');
|
|
37
|
-
}
|
|
38
|
-
if (Object.keys(session.metadata).length > 0) {
|
|
39
|
-
lines.push('## Metadata');
|
|
40
|
-
lines.push('```json');
|
|
41
|
-
lines.push(JSON.stringify(session.metadata, null, 2));
|
|
42
|
-
lines.push('```');
|
|
43
|
-
}
|
|
44
|
-
return lines.join('\n');
|
|
45
|
-
}
|
|
46
|
-
export async function handleExportSession(params) {
|
|
47
|
-
const projectId = hashProjectPath(params.projectPath);
|
|
48
|
-
const format = params.format ?? 'markdown';
|
|
49
|
-
try {
|
|
50
|
-
let session;
|
|
51
|
-
if (params.sessionId) {
|
|
52
|
-
session = await sessionStore.load(projectId, params.sessionId);
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
session = await sessionStore.getLatest(projectId);
|
|
56
|
-
}
|
|
57
|
-
if (!session) {
|
|
58
|
-
return {
|
|
59
|
-
content: [{ type: 'text', text: 'No sessions found for this project.' }],
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
if (format === 'json') {
|
|
63
|
-
const summary = [
|
|
64
|
-
`✅ **Session exported:** ${session.id}`,
|
|
65
|
-
'',
|
|
66
|
-
`**Project:** ${session.projectId}`,
|
|
67
|
-
`**Started:** ${session.startedAt}`,
|
|
68
|
-
`**Tool Calls:** ${String(session.toolCallCount)}`,
|
|
69
|
-
`**Active Specs:** ${String(session.activeSpecs.length)}`,
|
|
70
|
-
`**Decisions:** ${String(session.decisions.length)}`,
|
|
71
|
-
`**Modified Files:** ${String(session.modifiedFiles.length)}`,
|
|
72
|
-
].join('\n');
|
|
73
|
-
return {
|
|
74
|
-
content: [{ type: 'text', text: summary }],
|
|
75
|
-
structuredContent: session,
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
return {
|
|
79
|
-
content: [{ type: 'text', text: formatAsMarkdown(session) }],
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
catch (error) {
|
|
83
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
84
|
-
return {
|
|
85
|
-
content: [{ type: 'text', text: `Error exporting session: ${message}` }],
|
|
86
|
-
isError: true,
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
//# sourceMappingURL=export-session.js.map
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { PricingTier, ToolResult } from '../types/index.js';
|
|
2
|
-
export declare function getCurrentTier(): Promise<PricingTier>;
|
|
3
|
-
export declare function invalidateTierCache(): void;
|
|
4
|
-
/**
|
|
5
|
-
* Synchronous tier estimate based on environment variables only — no disk I/O.
|
|
6
|
-
* Used at server startup to set the display name before async validation runs.
|
|
7
|
-
* Returns 'enterprise' for owner token, 'pro' if a license key env var is set, 'free' otherwise.
|
|
8
|
-
*/
|
|
9
|
-
export declare function getTierSync(): PricingTier;
|
|
10
|
-
export declare function shouldBypassDailyRateLimit(toolName: string): boolean;
|
|
11
|
-
export declare function withLicenseGate<T>(toolName: string, handler: (args: T, extra?: unknown) => Promise<ToolResult>): (args: T, extra?: unknown) => Promise<ToolResult>;
|
|
12
|
-
/**
|
|
13
|
-
* Wraps a handler with rate limiting (free tier only) and usage event recording.
|
|
14
|
-
* For free tier: checks daily limit + cooldown before executing.
|
|
15
|
-
* For all tiers: records a usage event after execution (fire-and-forget).
|
|
16
|
-
*/
|
|
17
|
-
export declare function withUsageTracking<T>(toolName: string, handler: (args: T, extra?: unknown) => Promise<ToolResult>): (args: T, extra?: unknown) => Promise<ToolResult>;
|
|
18
|
-
//# sourceMappingURL=license-gate.d.ts.map
|
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
// tools/license-gate.ts — Runtime license enforcement for MCP tool handlers.
|
|
2
|
-
// Provides getCurrentTier() with in-memory cache and withLicenseGate() wrapper.
|
|
3
|
-
import { createHash } from 'node:crypto';
|
|
4
|
-
import { licenseStore } from '../storage/index.js';
|
|
5
|
-
import * as usageStore from '../storage/usage-store.js';
|
|
6
|
-
import { resolveCurrentTier, checkToolAccess, isValidationFresh, isInGracePeriod, validateLicense, computeMachineFingerprint, isOpenAccessActive, } from '../engine/license-validator.js';
|
|
7
|
-
import { evaluateRateLimit, isTrialActive, trialDaysRemaining, recordUsageEvent, } from '../engine/usage-tracker/index.js';
|
|
8
|
-
import { getSessionContext } from '../transports/session-context.js';
|
|
9
|
-
// ---------------------------------------------------------------------------
|
|
10
|
-
// Owner token override (SHA-256 verified, not reversible)
|
|
11
|
-
// ---------------------------------------------------------------------------
|
|
12
|
-
// Salt prefix for owner token hashing (v1). New token hashes must be generated
|
|
13
|
-
// as: sha256(HASH_SALT + token). To regenerate OWNER_TOKEN_HASH run:
|
|
14
|
-
// node -e "const{createHash}=require('crypto');console.log(createHash('sha256').update('planu-owner-v1'+process.argv[1]).digest('hex'))" <token>
|
|
15
|
-
const HASH_SALT = 'planu-owner-v1';
|
|
16
|
-
// Hash generated without salt (legacy). Kept for migration: if you rotate the
|
|
17
|
-
// owner token, generate a new salted hash and replace OWNER_TOKEN_HASH_V1.
|
|
18
|
-
const OWNER_TOKEN_HASH = '9f9e8410fd0a718540fe1e29f7beaa2506b539c8732915d8eddcb3bcff9b9465';
|
|
19
|
-
function checkOwnerOverride() {
|
|
20
|
-
const token = process.env.SDD_OWNER_TOKEN;
|
|
21
|
-
if (!token) {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
const trimmed = token.trim();
|
|
25
|
-
// Try salted hash first (current scheme).
|
|
26
|
-
const saltedHash = createHash('sha256')
|
|
27
|
-
.update(HASH_SALT + trimmed)
|
|
28
|
-
.digest('hex');
|
|
29
|
-
if (saltedHash === OWNER_TOKEN_HASH) {
|
|
30
|
-
return 'enterprise';
|
|
31
|
-
}
|
|
32
|
-
// Fall back to legacy unsalted hash so existing tokens keep working.
|
|
33
|
-
// DEPRECATED: regenerate OWNER_TOKEN_HASH with salt when rotating the token.
|
|
34
|
-
const legacyHash = createHash('sha256').update(trimmed).digest('hex');
|
|
35
|
-
return legacyHash === OWNER_TOKEN_HASH ? 'enterprise' : null;
|
|
36
|
-
}
|
|
37
|
-
// ---------------------------------------------------------------------------
|
|
38
|
-
// In-memory tier cache (avoids disk read on every tool call)
|
|
39
|
-
// ---------------------------------------------------------------------------
|
|
40
|
-
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
41
|
-
let cachedTier = null;
|
|
42
|
-
let cacheTimestamp = 0;
|
|
43
|
-
export async function getCurrentTier() {
|
|
44
|
-
// Per-session license key (hosted multi-tenant mode via HTTP transport)
|
|
45
|
-
const sessionCtx = getSessionContext();
|
|
46
|
-
if (sessionCtx?.licenseKey) {
|
|
47
|
-
return resolveSessionTier(sessionCtx.licenseKey);
|
|
48
|
-
}
|
|
49
|
-
const now = Date.now();
|
|
50
|
-
if (cachedTier !== null && now - cacheTimestamp < CACHE_TTL_MS) {
|
|
51
|
-
return cachedTier;
|
|
52
|
-
}
|
|
53
|
-
const ownerTier = checkOwnerOverride();
|
|
54
|
-
if (ownerTier) {
|
|
55
|
-
cachedTier = ownerTier;
|
|
56
|
-
cacheTimestamp = now;
|
|
57
|
-
return cachedTier;
|
|
58
|
-
}
|
|
59
|
-
let state = await licenseStore.getLicenseState();
|
|
60
|
-
// Synchronous online validation when license is stale (>7 days)
|
|
61
|
-
if (state && !isValidationFresh(state)) {
|
|
62
|
-
state = await performOnlineValidation(state);
|
|
63
|
-
}
|
|
64
|
-
cachedTier = resolveCurrentTier(state);
|
|
65
|
-
cacheTimestamp = now;
|
|
66
|
-
return cachedTier;
|
|
67
|
-
}
|
|
68
|
-
export function invalidateTierCache() {
|
|
69
|
-
cachedTier = null;
|
|
70
|
-
cacheTimestamp = 0;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Synchronous tier estimate based on environment variables only — no disk I/O.
|
|
74
|
-
* Used at server startup to set the display name before async validation runs.
|
|
75
|
-
* Returns 'enterprise' for owner token, 'pro' if a license key env var is set, 'free' otherwise.
|
|
76
|
-
*/
|
|
77
|
-
export function getTierSync() {
|
|
78
|
-
if (checkOwnerOverride()) {
|
|
79
|
-
return 'enterprise';
|
|
80
|
-
}
|
|
81
|
-
if (process.env.SDD_LICENSE_KEY?.trim()) {
|
|
82
|
-
return 'pro';
|
|
83
|
-
}
|
|
84
|
-
return 'free';
|
|
85
|
-
}
|
|
86
|
-
// ---------------------------------------------------------------------------
|
|
87
|
-
// Per-session license resolution (hosted multi-tenant mode)
|
|
88
|
-
// ---------------------------------------------------------------------------
|
|
89
|
-
const sessionTierCache = new Map();
|
|
90
|
-
const SESSION_TIER_TTL_MS = 10 * 60 * 1000; // 10 min — longer since online validation is expensive
|
|
91
|
-
const RATE_LIMIT_EXEMPT_TOOLS = new Set([
|
|
92
|
-
'planu_status',
|
|
93
|
-
'update_status_batch',
|
|
94
|
-
'configure_code_graph',
|
|
95
|
-
'code_graph_status',
|
|
96
|
-
]);
|
|
97
|
-
export function shouldBypassDailyRateLimit(toolName) {
|
|
98
|
-
return RATE_LIMIT_EXEMPT_TOOLS.has(toolName);
|
|
99
|
-
}
|
|
100
|
-
async function resolveSessionTier(licenseKey) {
|
|
101
|
-
const now = Date.now();
|
|
102
|
-
const cached = sessionTierCache.get(licenseKey);
|
|
103
|
-
if (cached && now < cached.expiresAt) {
|
|
104
|
-
return cached.tier;
|
|
105
|
-
}
|
|
106
|
-
try {
|
|
107
|
-
const result = await validateLicense(licenseKey, 'hosted');
|
|
108
|
-
const tier = result.valid ? 'pro' : 'free';
|
|
109
|
-
sessionTierCache.set(licenseKey, { tier, expiresAt: now + SESSION_TIER_TTL_MS });
|
|
110
|
-
return tier;
|
|
111
|
-
}
|
|
112
|
-
catch {
|
|
113
|
-
// API unreachable — degrade to free (no grace period for session keys)
|
|
114
|
-
sessionTierCache.set(licenseKey, { tier: 'free', expiresAt: now + 60_000 }); // retry in 1 min
|
|
115
|
-
return 'free';
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
// ---------------------------------------------------------------------------
|
|
119
|
-
// License gate wrapper
|
|
120
|
-
// ---------------------------------------------------------------------------
|
|
121
|
-
export function withLicenseGate(toolName, handler) {
|
|
122
|
-
return async (args, extra) => {
|
|
123
|
-
const tier = await getCurrentTier();
|
|
124
|
-
const trialContext = await resolveTrialContext(tier);
|
|
125
|
-
const check = checkToolAccess(toolName, tier, trialContext);
|
|
126
|
-
if (!check.allowed) {
|
|
127
|
-
return { content: [{ type: 'text', text: check.message }], isError: true };
|
|
128
|
-
}
|
|
129
|
-
return handler(args, extra);
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Resolves trial activity for free-tier users. Returns empty context for paid tiers
|
|
134
|
-
* (no need to query trial state — they already have full access).
|
|
135
|
-
*/
|
|
136
|
-
async function resolveTrialContext(tier) {
|
|
137
|
-
if (tier !== 'free') {
|
|
138
|
-
return { trialActive: false, trialDaysRemaining: 0 };
|
|
139
|
-
}
|
|
140
|
-
try {
|
|
141
|
-
const trialState = await usageStore.ensureTrialState();
|
|
142
|
-
return {
|
|
143
|
-
trialActive: isTrialActive(trialState.firstRunAt),
|
|
144
|
-
trialDaysRemaining: trialDaysRemaining(trialState.firstRunAt),
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
catch {
|
|
148
|
-
return { trialActive: false, trialDaysRemaining: 0 };
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
// ---------------------------------------------------------------------------
|
|
152
|
-
// Usage tracking wrapper
|
|
153
|
-
// ---------------------------------------------------------------------------
|
|
154
|
-
/**
|
|
155
|
-
* Wraps a handler with rate limiting (free tier only) and usage event recording.
|
|
156
|
-
* For free tier: checks daily limit + cooldown before executing.
|
|
157
|
-
* For all tiers: records a usage event after execution (fire-and-forget).
|
|
158
|
-
*/
|
|
159
|
-
export function withUsageTracking(toolName, handler) {
|
|
160
|
-
return async (args, extra) => {
|
|
161
|
-
const tier = await getCurrentTier();
|
|
162
|
-
// During open-access window, skip rate limiting for free tier
|
|
163
|
-
if (tier === 'free' && !isOpenAccessActive() && !shouldBypassDailyRateLimit(toolName)) {
|
|
164
|
-
const today = new Date().toISOString().slice(0, 10);
|
|
165
|
-
const [trialState, callCount, lastCallAt] = await Promise.all([
|
|
166
|
-
usageStore.ensureTrialState(),
|
|
167
|
-
usageStore.getDailyCallCount(today),
|
|
168
|
-
usageStore.getLastCallTimestamp(),
|
|
169
|
-
]);
|
|
170
|
-
const maxCalls = isTrialActive(trialState.firstRunAt) ? 50 : 30;
|
|
171
|
-
const check = evaluateRateLimit(callCount, maxCalls, lastCallAt, new Date());
|
|
172
|
-
if (!check.allowed) {
|
|
173
|
-
return { content: [{ type: 'text', text: check.message }], isError: true };
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
// Cast to ToolResult | undefined: TypeScript says ToolResult, but JS runtime may
|
|
177
|
-
// return undefined if a handler has a missing return path not caught at compile time.
|
|
178
|
-
// noImplicitReturns in tsconfig prevents new occurrences; this guard handles old ones.
|
|
179
|
-
const rawResult = (await handler(args, extra));
|
|
180
|
-
const result = rawResult ?? {
|
|
181
|
-
content: [{ type: 'text', text: 'Internal error: tool handler returned no result' }],
|
|
182
|
-
isError: true,
|
|
183
|
-
};
|
|
184
|
-
const success = !result.isError;
|
|
185
|
-
void usageStore
|
|
186
|
-
.appendUsageEvent(recordUsageEvent(toolName, tier, success))
|
|
187
|
-
.catch((err) => {
|
|
188
|
-
console.error('[planu] appendUsageEvent failed:', err);
|
|
189
|
-
});
|
|
190
|
-
return result;
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
// ---------------------------------------------------------------------------
|
|
194
|
-
// Synchronous online validation (replaces background fire-and-forget)
|
|
195
|
-
// ---------------------------------------------------------------------------
|
|
196
|
-
async function performOnlineValidation(state) {
|
|
197
|
-
try {
|
|
198
|
-
const result = await validateLicense(state.licenseKey, state.instanceId);
|
|
199
|
-
if (!result.valid) {
|
|
200
|
-
// License revoked remotely — clear local state
|
|
201
|
-
await licenseStore.deleteLicenseState();
|
|
202
|
-
return null;
|
|
203
|
-
}
|
|
204
|
-
const refreshed = {
|
|
205
|
-
...state,
|
|
206
|
-
lastValidatedAt: new Date().toISOString(),
|
|
207
|
-
activationUsage: result.license_key.activation_usage,
|
|
208
|
-
machineFingerprint: computeMachineFingerprint(),
|
|
209
|
-
};
|
|
210
|
-
await licenseStore.saveLicenseState(refreshed);
|
|
211
|
-
return refreshed;
|
|
212
|
-
}
|
|
213
|
-
catch {
|
|
214
|
-
// API unreachable — allow if within grace period (TTL + 3 days)
|
|
215
|
-
if (isInGracePeriod(state)) {
|
|
216
|
-
return state;
|
|
217
|
-
}
|
|
218
|
-
// Grace period expired — degrade to free
|
|
219
|
-
return null;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
//# sourceMappingURL=license-gate.js.map
|