productfoo 0.0.0-stage → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +338 -2
- package/bin/foo +3 -0
- package/bin/foo.mjs +42 -0
- package/commands/bug.md +31 -0
- package/commands/discovery.md +28 -0
- package/commands/ideate.md +27 -0
- package/commands/init.md +28 -0
- package/commands/pitch.md +27 -0
- package/commands/preview.md +25 -0
- package/commands/research.md +31 -0
- package/commands/roast.md +27 -0
- package/commands/spec.md +30 -0
- package/config/pricing.yaml +198 -0
- package/config/routing-policy.yaml +171 -0
- package/dist-public/src/activeModel.d.ts +83 -0
- package/dist-public/src/activeModel.js +331 -0
- package/dist-public/src/agentAdapters.d.ts +122 -0
- package/dist-public/src/agentAdapters.js +178 -0
- package/dist-public/src/agentState.d.ts +43 -0
- package/dist-public/src/agentState.js +177 -0
- package/dist-public/src/bugCapsule.d.ts +94 -0
- package/dist-public/src/bugCapsule.js +227 -0
- package/dist-public/src/bugCapture.d.ts +26 -0
- package/dist-public/src/bugCapture.js +251 -0
- package/dist-public/src/bugOwnerLog.d.ts +93 -0
- package/dist-public/src/bugOwnerLog.js +304 -0
- package/dist-public/src/bugReportStore.d.ts +48 -0
- package/dist-public/src/bugReportStore.js +860 -0
- package/dist-public/src/bugSpecContext.d.ts +7 -0
- package/dist-public/src/bugSpecContext.js +129 -0
- package/dist-public/src/channelFreshness.d.ts +25 -0
- package/dist-public/src/channelFreshness.js +194 -0
- package/dist-public/src/cli.d.ts +30 -0
- package/dist-public/src/cli.js +1667 -0
- package/dist-public/src/cliArgs.d.ts +7 -0
- package/dist-public/src/cliArgs.js +44 -0
- package/dist-public/src/codeCatalog.d.ts +41 -0
- package/dist-public/src/codeCatalog.js +197 -0
- package/dist-public/src/commandContract.d.ts +84 -0
- package/dist-public/src/commandContract.js +390 -0
- package/dist-public/src/commandUsage.d.ts +3 -0
- package/dist-public/src/commandUsage.js +30 -0
- package/dist-public/src/commands/agent.d.ts +60 -0
- package/dist-public/src/commands/agent.js +1353 -0
- package/dist-public/src/commands/asp.d.ts +8 -0
- package/dist-public/src/commands/asp.js +669 -0
- package/dist-public/src/commands/evalCmd.d.ts +4 -0
- package/dist-public/src/commands/evalCmd.js +405 -0
- package/dist-public/src/commands/login.d.ts +25 -0
- package/dist-public/src/commands/login.js +94 -0
- package/dist-public/src/commands/loop.d.ts +16 -0
- package/dist-public/src/commands/loop.js +619 -0
- package/dist-public/src/commands/reconcile.d.ts +41 -0
- package/dist-public/src/commands/reconcile.js +292 -0
- package/dist-public/src/commands/runtime.d.ts +1 -0
- package/dist-public/src/commands/runtime.js +36 -0
- package/dist-public/src/commands/scanRedact.d.ts +8 -0
- package/dist-public/src/commands/scanRedact.js +266 -0
- package/dist-public/src/commands/setup.d.ts +28 -0
- package/dist-public/src/commands/setup.js +398 -0
- package/dist-public/src/commands/shared.d.ts +17 -0
- package/dist-public/src/commands/shared.js +147 -0
- package/dist-public/src/commands/source.d.ts +1 -0
- package/dist-public/src/commands/source.js +134 -0
- package/dist-public/src/commands/specCmd.d.ts +24 -0
- package/dist-public/src/commands/specCmd.js +794 -0
- package/dist-public/src/commands/update.d.ts +61 -0
- package/dist-public/src/commands/update.js +244 -0
- package/dist-public/src/comprehensionSignal.d.ts +20 -0
- package/dist-public/src/comprehensionSignal.js +166 -0
- package/dist-public/src/conductor/diversity.d.ts +48 -0
- package/dist-public/src/conductor/diversity.js +423 -0
- package/dist-public/src/conductor/engine.d.ts +7 -0
- package/dist-public/src/conductor/engine.js +295 -0
- package/dist-public/src/conductor/families/ideate.d.ts +2 -0
- package/dist-public/src/conductor/families/ideate.js +255 -0
- package/dist-public/src/conductor/families/interview.d.ts +2 -0
- package/dist-public/src/conductor/families/interview.js +39 -0
- package/dist-public/src/conductor/families/specSwarm.d.ts +2 -0
- package/dist-public/src/conductor/families/specSwarm.js +146 -0
- package/dist-public/src/conductor/familyDelivery.d.ts +6 -0
- package/dist-public/src/conductor/familyDelivery.js +37 -0
- package/dist-public/src/conductor/familyPackArtifact.d.ts +4 -0
- package/dist-public/src/conductor/familyPackArtifact.js +260 -0
- package/dist-public/src/conductor/ideateDecision.d.ts +6 -0
- package/dist-public/src/conductor/ideateDecision.js +237 -0
- package/dist-public/src/conductor/pack.d.ts +26 -0
- package/dist-public/src/conductor/pack.js +75 -0
- package/dist-public/src/conductor/types.d.ts +111 -0
- package/dist-public/src/conductor/types.js +7 -0
- package/dist-public/src/configPath.d.ts +2 -0
- package/dist-public/src/configPath.js +7 -0
- package/dist-public/src/discoveryArtifacts.d.ts +54 -0
- package/dist-public/src/discoveryArtifacts.js +467 -0
- package/dist-public/src/discoveryCommand.d.ts +127 -0
- package/dist-public/src/discoveryCommand.js +2148 -0
- package/dist-public/src/discoveryConnections.d.ts +339 -0
- package/dist-public/src/discoveryConnections.js +613 -0
- package/dist-public/src/discoveryDocs.d.ts +129 -0
- package/dist-public/src/discoveryDocs.js +577 -0
- package/dist-public/src/discoveryEvidence.d.ts +59 -0
- package/dist-public/src/discoveryEvidence.js +198 -0
- package/dist-public/src/discoveryFireflies.d.ts +25 -0
- package/dist-public/src/discoveryFireflies.js +551 -0
- package/dist-public/src/discoveryGmail.d.ts +12 -0
- package/dist-public/src/discoveryGmail.js +466 -0
- package/dist-public/src/discoveryGong.d.ts +12 -0
- package/dist-public/src/discoveryGong.js +491 -0
- package/dist-public/src/discoveryLearningRouting.d.ts +90 -0
- package/dist-public/src/discoveryLearningRouting.js +405 -0
- package/dist-public/src/discoveryMatching.d.ts +26 -0
- package/dist-public/src/discoveryMatching.js +73 -0
- package/dist-public/src/discoveryOutlook.d.ts +12 -0
- package/dist-public/src/discoveryOutlook.js +492 -0
- package/dist-public/src/discoveryProcess.d.ts +221 -0
- package/dist-public/src/discoveryProcess.js +703 -0
- package/dist-public/src/discoveryProof.d.ts +143 -0
- package/dist-public/src/discoveryProof.js +529 -0
- package/dist-public/src/discoveryProviders.d.ts +152 -0
- package/dist-public/src/discoveryProviders.js +756 -0
- package/dist-public/src/discoveryReadiness.d.ts +219 -0
- package/dist-public/src/discoveryReadiness.js +672 -0
- package/dist-public/src/discoveryReconcile.d.ts +57 -0
- package/dist-public/src/discoveryReconcile.js +424 -0
- package/dist-public/src/discoverySlack.d.ts +12 -0
- package/dist-public/src/discoverySlack.js +452 -0
- package/dist-public/src/discoverySources.d.ts +56 -0
- package/dist-public/src/discoverySources.js +322 -0
- package/dist-public/src/discoverySync.d.ts +52 -0
- package/dist-public/src/discoverySync.js +151 -0
- package/dist-public/src/discoverySynthesis.d.ts +57 -0
- package/dist-public/src/discoverySynthesis.js +215 -0
- package/dist-public/src/discoveryTeams.d.ts +12 -0
- package/dist-public/src/discoveryTeams.js +516 -0
- package/dist-public/src/discoveryZoom.d.ts +12 -0
- package/dist-public/src/discoveryZoom.js +523 -0
- package/dist-public/src/elicitationSurface.d.ts +13 -0
- package/dist-public/src/elicitationSurface.js +77 -0
- package/dist-public/src/errorReport.d.ts +99 -0
- package/dist-public/src/errorReport.js +443 -0
- package/dist-public/src/errorTaxonomy.d.ts +84 -0
- package/dist-public/src/errorTaxonomy.js +189 -0
- package/dist-public/src/evalCalibration.d.ts +82 -0
- package/dist-public/src/evalCalibration.js +169 -0
- package/dist-public/src/evalChecks.d.ts +21 -0
- package/dist-public/src/evalChecks.js +295 -0
- package/dist-public/src/evalCompile.d.ts +56 -0
- package/dist-public/src/evalCompile.js +58 -0
- package/dist-public/src/evalImprove.d.ts +60 -0
- package/dist-public/src/evalImprove.js +175 -0
- package/dist-public/src/evalJudge.d.ts +47 -0
- package/dist-public/src/evalJudge.js +143 -0
- package/dist-public/src/evalLabels.d.ts +100 -0
- package/dist-public/src/evalLabels.js +287 -0
- package/dist-public/src/evalRecord.d.ts +68 -0
- package/dist-public/src/evalRecord.js +165 -0
- package/dist-public/src/evalRoast.d.ts +88 -0
- package/dist-public/src/evalRoast.js +249 -0
- package/dist-public/src/evidenceIntake.d.ts +66 -0
- package/dist-public/src/evidenceIntake.js +356 -0
- package/dist-public/src/feedback.d.ts +78 -0
- package/dist-public/src/feedback.js +294 -0
- package/dist-public/src/firefliesClient.d.ts +106 -0
- package/dist-public/src/firefliesClient.js +258 -0
- package/dist-public/src/firstMoveTensions.d.ts +10 -0
- package/dist-public/src/firstMoveTensions.js +93 -0
- package/dist-public/src/gitRun.d.ts +21 -0
- package/dist-public/src/gitRun.js +50 -0
- package/dist-public/src/githubReconcile.d.ts +94 -0
- package/dist-public/src/githubReconcile.js +205 -0
- package/dist-public/src/gmailNango.d.ts +131 -0
- package/dist-public/src/gmailNango.js +329 -0
- package/dist-public/src/gongNango.d.ts +118 -0
- package/dist-public/src/gongNango.js +308 -0
- package/dist-public/src/grammarGate.d.ts +13 -0
- package/dist-public/src/grammarGate.js +72 -0
- package/dist-public/src/harnessGates.d.ts +65 -0
- package/dist-public/src/harnessGates.js +168 -0
- package/dist-public/src/humanSurfaceEval.d.ts +19 -0
- package/dist-public/src/humanSurfaceEval.js +156 -0
- package/dist-public/src/ideate.d.ts +43 -0
- package/dist-public/src/ideate.js +255 -0
- package/dist-public/src/ideateContext.d.ts +11 -0
- package/dist-public/src/ideateContext.js +83 -0
- package/dist-public/src/index.d.ts +2 -0
- package/dist-public/src/index.js +4 -0
- package/dist-public/src/initCommand.d.ts +25 -0
- package/dist-public/src/initCommand.js +558 -0
- package/dist-public/src/interviewGate.d.ts +25 -0
- package/dist-public/src/interviewGate.js +126 -0
- package/dist-public/src/jsonFile.d.ts +2 -0
- package/dist-public/src/jsonFile.js +22 -0
- package/dist-public/src/loopBudget.d.ts +182 -0
- package/dist-public/src/loopBudget.js +494 -0
- package/dist-public/src/loopContracts.d.ts +35 -0
- package/dist-public/src/loopContracts.js +364 -0
- package/dist-public/src/machineState.d.ts +1 -0
- package/dist-public/src/machineState.js +9 -0
- package/dist-public/src/notionNango.d.ts +93 -0
- package/dist-public/src/notionNango.js +279 -0
- package/dist-public/src/orchestrationResult.d.ts +11 -0
- package/dist-public/src/orchestrationResult.js +73 -0
- package/dist-public/src/outlookNango.d.ts +124 -0
- package/dist-public/src/outlookNango.js +327 -0
- package/dist-public/src/packetCorrections.d.ts +40 -0
- package/dist-public/src/packetCorrections.js +172 -0
- package/dist-public/src/packetGlossary.d.ts +6 -0
- package/dist-public/src/packetGlossary.js +42 -0
- package/dist-public/src/packetIndex.d.ts +54 -0
- package/dist-public/src/packetIndex.js +744 -0
- package/dist-public/src/packetIndexEntry.d.ts +3 -0
- package/dist-public/src/packetIndexEntry.js +128 -0
- package/dist-public/src/packetIndexServe.d.ts +13 -0
- package/dist-public/src/packetIndexServe.js +113 -0
- package/dist-public/src/packetIndexWorker.d.ts +7 -0
- package/dist-public/src/packetIndexWorker.js +45 -0
- package/dist-public/src/packetLifecycle.d.ts +30 -0
- package/dist-public/src/packetLifecycle.js +127 -0
- package/dist-public/src/packetReconcile.d.ts +73 -0
- package/dist-public/src/packetReconcile.js +386 -0
- package/dist-public/src/packetStatus.d.ts +125 -0
- package/dist-public/src/packetStatus.js +770 -0
- package/dist-public/src/packetValidation.d.ts +17 -0
- package/dist-public/src/packetValidation.js +333 -0
- package/dist-public/src/packetVerify.d.ts +73 -0
- package/dist-public/src/packetVerify.js +303 -0
- package/dist-public/src/packetWrite.d.ts +52 -0
- package/dist-public/src/packetWrite.js +448 -0
- package/dist-public/src/pitch.d.ts +38 -0
- package/dist-public/src/pitch.js +154 -0
- package/dist-public/src/processMemo.d.ts +8 -0
- package/dist-public/src/processMemo.js +23 -0
- package/dist-public/src/productDetect.d.ts +30 -0
- package/dist-public/src/productDetect.js +376 -0
- package/dist-public/src/profile.d.ts +62 -0
- package/dist-public/src/profile.js +334 -0
- package/dist-public/src/questionModel.d.ts +46 -0
- package/dist-public/src/questionModel.js +74 -0
- package/dist-public/src/questionText.d.ts +7 -0
- package/dist-public/src/questionText.js +38 -0
- package/dist-public/src/redactConfig.d.ts +2 -0
- package/dist-public/src/redactConfig.js +11 -0
- package/dist-public/src/redactEngine.d.ts +59 -0
- package/dist-public/src/redactEngine.js +509 -0
- package/dist-public/src/redactGate.d.ts +63 -0
- package/dist-public/src/redactGate.js +209 -0
- package/dist-public/src/repoRead.d.ts +29 -0
- package/dist-public/src/repoRead.js +160 -0
- package/dist-public/src/research.d.ts +242 -0
- package/dist-public/src/research.js +1314 -0
- package/dist-public/src/researchCompletion.d.ts +34 -0
- package/dist-public/src/researchCompletion.js +123 -0
- package/dist-public/src/researchExecution.d.ts +47 -0
- package/dist-public/src/researchExecution.js +309 -0
- package/dist-public/src/researchFormat.d.ts +21 -0
- package/dist-public/src/researchFormat.js +77 -0
- package/dist-public/src/researchGate.d.ts +86 -0
- package/dist-public/src/researchGate.js +1036 -0
- package/dist-public/src/researchReceipt.d.ts +76 -0
- package/dist-public/src/researchReceipt.js +148 -0
- package/dist-public/src/researchRender.d.ts +2 -0
- package/dist-public/src/researchRender.js +267 -0
- package/dist-public/src/researchSigningKey.d.ts +19 -0
- package/dist-public/src/researchSigningKey.js +95 -0
- package/dist-public/src/riskClassify.d.ts +42 -0
- package/dist-public/src/riskClassify.js +149 -0
- package/dist-public/src/routines.d.ts +42 -0
- package/dist-public/src/routines.js +269 -0
- package/dist-public/src/routingPolicy.d.ts +172 -0
- package/dist-public/src/routingPolicy.js +646 -0
- package/dist-public/src/secretInput.d.ts +8 -0
- package/dist-public/src/secretInput.js +89 -0
- package/dist-public/src/selfCheck.d.ts +7 -0
- package/dist-public/src/selfCheck.js +473 -0
- package/dist-public/src/selfCheckCache.d.ts +29 -0
- package/dist-public/src/selfCheckCache.js +147 -0
- package/dist-public/src/severityClass.d.ts +15 -0
- package/dist-public/src/severityClass.js +22 -0
- package/dist-public/src/skillClientPolicy.d.ts +8 -0
- package/dist-public/src/skillClientPolicy.js +16 -0
- package/dist-public/src/skillLocal.d.ts +98 -0
- package/dist-public/src/skillLocal.js +463 -0
- package/dist-public/src/skillTreeDigest.d.ts +9 -0
- package/dist-public/src/skillTreeDigest.js +64 -0
- package/dist-public/src/slackNango.d.ts +62 -0
- package/dist-public/src/slackNango.js +208 -0
- package/dist-public/src/social/arxiv.d.ts +19 -0
- package/dist-public/src/social/arxiv.js +96 -0
- package/dist-public/src/social/github.d.ts +2 -0
- package/dist-public/src/social/github.js +78 -0
- package/dist-public/src/social/hackernews.d.ts +2 -0
- package/dist-public/src/social/hackernews.js +67 -0
- package/dist-public/src/social/index.d.ts +10 -0
- package/dist-public/src/social/index.js +79 -0
- package/dist-public/src/social/reddit.d.ts +2 -0
- package/dist-public/src/social/reddit.js +125 -0
- package/dist-public/src/social/types.d.ts +73 -0
- package/dist-public/src/social/types.js +61 -0
- package/dist-public/src/social/xml.d.ts +10 -0
- package/dist-public/src/social/xml.js +49 -0
- package/dist-public/src/social/youtube.d.ts +7 -0
- package/dist-public/src/social/youtube.js +217 -0
- package/dist-public/src/sourceIngress.d.ts +488 -0
- package/dist-public/src/sourceIngress.js +1651 -0
- package/dist-public/src/specAfter.d.ts +187 -0
- package/dist-public/src/specAfter.js +814 -0
- package/dist-public/src/specBudget.d.ts +124 -0
- package/dist-public/src/specBudget.js +424 -0
- package/dist-public/src/specDecisionSignals.d.ts +38 -0
- package/dist-public/src/specDecisionSignals.js +121 -0
- package/dist-public/src/specLinkedCommits.d.ts +7 -0
- package/dist-public/src/specLinkedCommits.js +107 -0
- package/dist-public/src/specSections.d.ts +105 -0
- package/dist-public/src/specSections.js +718 -0
- package/dist-public/src/statusGlyphs.d.ts +18 -0
- package/dist-public/src/statusGlyphs.js +75 -0
- package/dist-public/src/swarmCommand.d.ts +10 -0
- package/dist-public/src/swarmCommand.js +96 -0
- package/dist-public/src/teamsNango.d.ts +114 -0
- package/dist-public/src/teamsNango.js +301 -0
- package/dist-public/src/tokenStore.d.ts +10 -0
- package/dist-public/src/tokenStore.js +77 -0
- package/dist-public/src/trustEnvelope.d.ts +78 -0
- package/dist-public/src/trustEnvelope.js +392 -0
- package/dist-public/src/untrustedText.d.ts +6 -0
- package/dist-public/src/untrustedText.js +20 -0
- package/dist-public/src/updateNotice.d.ts +41 -0
- package/dist-public/src/updateNotice.js +290 -0
- package/dist-public/src/vendorYaml.d.ts +1 -0
- package/dist-public/src/vendorYaml.js +7374 -0
- package/dist-public/src/workflowRunActuals.d.ts +67 -0
- package/dist-public/src/workflowRunActuals.js +184 -0
- package/dist-public/src/zendeskDiscovery.d.ts +12 -0
- package/dist-public/src/zendeskDiscovery.js +513 -0
- package/dist-public/src/zendeskNango.d.ts +131 -0
- package/dist-public/src/zendeskNango.js +344 -0
- package/dist-public/src/zoomNango.d.ts +106 -0
- package/dist-public/src/zoomNango.js +351 -0
- package/docs/CHANGELOG.md +32 -0
- package/docs/THIRD_PARTY_NOTICES.md +36 -0
- package/package.json +71 -4
- package/schemas/bug-capsule-receipt.schema.json +54 -0
- package/schemas/bug-capsule.schema.json +189 -0
- package/schemas/discovery-evidence-envelope.schema.json +79 -0
- package/schemas/discovery-processing-receipt.schema.json +71 -0
- package/schemas/discovery-service-contract.schema.json +189 -0
- package/skills/AGENTS.md +32 -0
- package/skills/README.md +287 -0
- package/skills/bug/SKILL.md +49 -0
- package/skills/build-preview-spec/SKILL.md +103 -0
- package/skills/capture-external-artifact/SKILL.md +23 -0
- package/skills/capture-external-artifact/agents/openai.yaml +4 -0
- package/skills/capture-external-artifact/references/extractor-libraries.md +27 -0
- package/skills/check-spec-health/SKILL.md +45 -0
- package/skills/check-spec-health/agents/openai.yaml +4 -0
- package/skills/competitive-positioning/SKILL.md +86 -0
- package/skills/competitive-positioning/agents/openai.yaml +4 -0
- package/skills/cpo-lens/SKILL.md +398 -0
- package/skills/cpo-lens/agents/openai.yaml +4 -0
- package/skills/create-prd/SKILL.md +26 -0
- package/skills/create-prd/agents/openai.yaml +4 -0
- package/skills/customer-discovery/SKILL.md +192 -0
- package/skills/design-system/SKILL.md +49 -0
- package/skills/design-system/agents/openai.yaml +4 -0
- package/skills/discussion-guide/SKILL.md +75 -0
- package/skills/discussion-guide/agents/openai.yaml +4 -0
- package/skills/firecrawl-research-papers/SKILL.md +96 -0
- package/skills/firecrawl-research-papers/agents/openai.yaml +4 -0
- package/skills/foo-roast/SKILL.md +325 -0
- package/skills/generate-gtm-brief/SKILL.md +41 -0
- package/skills/generate-gtm-brief/agents/openai.yaml +4 -0
- package/skills/generate-gtm-brief/references/gtm-brief-template.md +56 -0
- package/skills/gong-quotes/SKILL.md +43 -0
- package/skills/gong-quotes/agents/openai.yaml +4 -0
- package/skills/grill-me/SKILL.md +27 -0
- package/skills/grill-me/agents/openai.yaml +4 -0
- package/skills/ideate/SKILL.md +180 -0
- package/skills/ideate/references/family-packs.md +5 -0
- package/skills/initiative-tracker/SKILL.md +23 -0
- package/skills/initiative-tracker/agents/openai.yaml +4 -0
- package/skills/interop-review/SKILL.md +25 -0
- package/skills/interop-review/agents/openai.yaml +4 -0
- package/skills/interview-me/SKILL.md +404 -0
- package/skills/interview-me/agents/openai.yaml +4 -0
- package/skills/loop-library-audit/SKILL.md +123 -0
- package/skills/pitch/SKILL.md +110 -0
- package/skills/pitch/references/opportunity-assessment.md +136 -0
- package/skills/pitch/references/prfaq-template.md +54 -0
- package/skills/pmm-release-briefing/SKILL.md +27 -0
- package/skills/pmm-release-briefing/agents/openai.yaml +4 -0
- package/skills/pmm-release-briefing/references/variant-generalized.md +16 -0
- package/skills/positioning-messaging/SKILL.md +70 -0
- package/skills/positioning-messaging/agents/openai.yaml +4 -0
- package/skills/positioning-messaging/references/guest-insights.md +1400 -0
- package/skills/preview-swarm/SKILL.md +418 -0
- package/skills/preview-swarm/agents/openai.yaml +4 -0
- package/skills/preview-swarm/references/html-effectiveness-build-previews.md +168 -0
- package/skills/product-copywriting/SKILL.md +127 -0
- package/skills/product-copywriting/agents/openai.yaml +4 -0
- package/skills/product-copywriting/references/foo-voice.md +26 -0
- package/skills/product-shape-interview/SKILL.md +219 -0
- package/skills/product-shape-interview/agents/openai.yaml +4 -0
- package/skills/ready-for-release/SKILL.md +39 -0
- package/skills/ready-for-release/agents/openai.yaml +4 -0
- package/skills/redact-transcripts/SKILL.md +23 -0
- package/skills/redact-transcripts/agents/openai.yaml +4 -0
- package/skills/research-brief/SKILL.md +48 -0
- package/skills/research-brief/agents/openai.yaml +4 -0
- package/skills/research-refresh/SKILL.md +63 -0
- package/skills/research-refresh/references/worked-cases.md +44 -0
- package/skills/review-loop/SKILL.md +200 -0
- package/skills/review-spec/SKILL.md +32 -0
- package/skills/review-spec/agents/openai.yaml +4 -0
- package/skills/rfc-from-research/SKILL.md +42 -0
- package/skills/rfc-from-research/agents/openai.yaml +4 -0
- package/skills/rfc-from-research/references/rfc-pattern.md +69 -0
- package/skills/skill-registry.yaml +810 -0
- package/skills/spec-cleanup/SKILL.md +103 -0
- package/skills/spec-cleanup/agents/openai.yaml +4 -0
- package/skills/spec-swarm/SKILL.md +87 -0
- package/skills/spec-swarm/agents/openai.yaml +4 -0
- package/skills/spec-swarm/template/SKILL.template.md +94 -0
- package/skills/spec-swarm/template/personas/accessibility-inclusion.md +40 -0
- package/skills/spec-swarm/template/personas/ai-technical-pm.md +69 -0
- package/skills/spec-swarm/template/personas/analytics-engineering.md +39 -0
- package/skills/spec-swarm/template/personas/applied-ai-eval.md +64 -0
- package/skills/spec-swarm/template/personas/business-gm-pm.md +67 -0
- package/skills/spec-swarm/template/personas/consumer-craft-pm.md +68 -0
- package/skills/spec-swarm/template/personas/content-design.md +40 -0
- package/skills/spec-swarm/template/personas/cpo.md +42 -0
- package/skills/spec-swarm/template/personas/data-science.md +42 -0
- package/skills/spec-swarm/template/personas/developer-experience.md +65 -0
- package/skills/spec-swarm/template/personas/dx-product.md +70 -0
- package/skills/spec-swarm/template/personas/field.md +43 -0
- package/skills/spec-swarm/template/personas/finance-cfo.md +40 -0
- package/skills/spec-swarm/template/personas/growth-pm.md +67 -0
- package/skills/spec-swarm/template/personas/platform-pm.md +67 -0
- package/skills/spec-swarm/template/personas/pmm.md +43 -0
- package/skills/spec-swarm/template/personas/principal-product-designer.md +42 -0
- package/skills/spec-swarm/template/personas/product-ops-program.md +41 -0
- package/skills/spec-swarm/template/personas/qa-release.md +41 -0
- package/skills/spec-swarm/template/personas/responsible-ai-safety.md +64 -0
- package/skills/spec-swarm/template/personas/security-privacy-legal.md +40 -0
- package/skills/spec-swarm/template/personas/strategy-competitive.md +41 -0
- package/skills/spec-swarm/template/personas/technical-architect.md +42 -0
- package/skills/spec-swarm/template/personas/technical-lead.md +43 -0
- package/skills/spec-swarm/template/personas/user-research.md +41 -0
- package/skills/spec-swarm-accessibility-inclusion/SKILL.md +95 -0
- package/skills/spec-swarm-accessibility-inclusion/agents/openai.yaml +4 -0
- package/skills/spec-swarm-ai-technical-pm/SKILL.md +124 -0
- package/skills/spec-swarm-analytics-engineering/SKILL.md +94 -0
- package/skills/spec-swarm-analytics-engineering/agents/openai.yaml +4 -0
- package/skills/spec-swarm-applied-ai-eval/SKILL.md +120 -0
- package/skills/spec-swarm-business-gm-pm/SKILL.md +122 -0
- package/skills/spec-swarm-consumer-craft-pm/SKILL.md +122 -0
- package/skills/spec-swarm-content-design/SKILL.md +95 -0
- package/skills/spec-swarm-content-design/agents/openai.yaml +4 -0
- package/skills/spec-swarm-cpo/SKILL.md +94 -0
- package/skills/spec-swarm-cpo/agents/openai.yaml +4 -0
- package/skills/spec-swarm-data-science/SKILL.md +95 -0
- package/skills/spec-swarm-data-science/agents/openai.yaml +4 -0
- package/skills/spec-swarm-developer-experience/SKILL.md +121 -0
- package/skills/spec-swarm-dx-product/SKILL.md +126 -0
- package/skills/spec-swarm-field/SKILL.md +96 -0
- package/skills/spec-swarm-field/agents/openai.yaml +4 -0
- package/skills/spec-swarm-finance-cfo/SKILL.md +95 -0
- package/skills/spec-swarm-finance-cfo/agents/openai.yaml +4 -0
- package/skills/spec-swarm-growth-pm/SKILL.md +122 -0
- package/skills/spec-swarm-platform-pm/SKILL.md +122 -0
- package/skills/spec-swarm-pmm/SKILL.md +96 -0
- package/skills/spec-swarm-pmm/agents/openai.yaml +4 -0
- package/skills/spec-swarm-principal-product-designer/SKILL.md +95 -0
- package/skills/spec-swarm-principal-product-designer/agents/openai.yaml +4 -0
- package/skills/spec-swarm-product-ops-program/SKILL.md +96 -0
- package/skills/spec-swarm-product-ops-program/agents/openai.yaml +4 -0
- package/skills/spec-swarm-qa-release/SKILL.md +96 -0
- package/skills/spec-swarm-qa-release/agents/openai.yaml +4 -0
- package/skills/spec-swarm-responsible-ai-safety/SKILL.md +120 -0
- package/skills/spec-swarm-security-privacy-legal/SKILL.md +95 -0
- package/skills/spec-swarm-security-privacy-legal/agents/openai.yaml +4 -0
- package/skills/spec-swarm-strategy-competitive/SKILL.md +96 -0
- package/skills/spec-swarm-strategy-competitive/agents/openai.yaml +4 -0
- package/skills/spec-swarm-technical-architect/SKILL.md +95 -0
- package/skills/spec-swarm-technical-architect/agents/openai.yaml +4 -0
- package/skills/spec-swarm-technical-lead/SKILL.md +96 -0
- package/skills/spec-swarm-technical-lead/agents/openai.yaml +4 -0
- package/skills/spec-swarm-user-research/SKILL.md +95 -0
- package/skills/spec-swarm-user-research/agents/openai.yaml +4 -0
- package/skills/strategy-check/SKILL.md +170 -0
- package/skills/strategy-check/agents/openai.yaml +4 -0
- package/skills/study-digest/SKILL.md +41 -0
- package/skills/synthetic-customer/SKILL.md +68 -0
- package/skills/update-cli-spec/SKILL.md +69 -0
- package/skills/update-cli-spec/agents/openai.yaml +4 -0
- package/skills/voc-researcher/SKILL.md +45 -0
- package/skills/voc-researcher/agents/openai.yaml +4 -0
- package/skills-lock.json +35 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Local storage for direct provider credentials.
|
|
2
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
// The same machine-wide home `profile.ts` and `updateNotice.ts` already resolve
|
|
6
|
+
// — one home per machine, so keying once keys every project on it.
|
|
7
|
+
export function credentialsHome(env = process.env) {
|
|
8
|
+
return env.FOO_STATE_DIR?.trim() || env.FOO_HOME?.trim() || path.join(homedir(), ".foo");
|
|
9
|
+
}
|
|
10
|
+
export function credentialsPath(env = process.env) {
|
|
11
|
+
return path.join(credentialsHome(env), "credentials.json");
|
|
12
|
+
}
|
|
13
|
+
function readCredentials(env) {
|
|
14
|
+
try {
|
|
15
|
+
const parsed = JSON.parse(readFileSync(credentialsPath(env), "utf8"));
|
|
16
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// No file, unreadable file, corrupt JSON — all mean the same thing to a
|
|
20
|
+
// caller (nothing stored here) and none of them may crash a command.
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// Merge-write: saving one credential must never discard another. A field set
|
|
25
|
+
// to undefined in the patch is removed.
|
|
26
|
+
function writeCredentials(patch, env) {
|
|
27
|
+
const dir = credentialsHome(env);
|
|
28
|
+
// The credentials home exists to hold secrets (audit F15): create it 0700
|
|
29
|
+
// and re-tighten explicitly — the same re-chmod rule the file below follows,
|
|
30
|
+
// so a directory made loose by an older productfoo does not survive a save.
|
|
31
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
32
|
+
chmodSync(dir, 0o700);
|
|
33
|
+
const file = credentialsPath(env);
|
|
34
|
+
const merged = { ...readCredentials(env), ...patch };
|
|
35
|
+
for (const key of Object.keys(merged)) {
|
|
36
|
+
if (merged[key] === undefined)
|
|
37
|
+
delete merged[key];
|
|
38
|
+
}
|
|
39
|
+
writeFileSync(file, `${JSON.stringify(merged, null, 2)}\n`, { mode: 0o600 });
|
|
40
|
+
// writeFileSync's mode is a no-op on a file that already exists, so set it
|
|
41
|
+
// explicitly: a re-save must not silently inherit a loose mode from before.
|
|
42
|
+
chmodSync(file, 0o600);
|
|
43
|
+
return file;
|
|
44
|
+
}
|
|
45
|
+
export function readStoredFirecrawlKey(env = process.env) {
|
|
46
|
+
const key = readCredentials(env).firecrawl_api_key;
|
|
47
|
+
return typeof key === "string" ? key.trim() || null : null;
|
|
48
|
+
}
|
|
49
|
+
export function writeStoredFirecrawlKey(key, env = process.env) {
|
|
50
|
+
return writeCredentials({ firecrawl_api_key: key.trim() }, env);
|
|
51
|
+
}
|
|
52
|
+
export function clearStoredFirecrawlKey(env = process.env) {
|
|
53
|
+
const file = credentialsPath(env);
|
|
54
|
+
if (!existsSync(file))
|
|
55
|
+
return false;
|
|
56
|
+
const creds = readCredentials(env);
|
|
57
|
+
if (!creds.firecrawl_api_key)
|
|
58
|
+
return false;
|
|
59
|
+
// Preserve unrelated settings, including inert credentials left by older versions.
|
|
60
|
+
if (Object.keys(creds).some((key) => key !== "firecrawl_api_key")) {
|
|
61
|
+
writeCredentials({ firecrawl_api_key: undefined }, env);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
rmSync(file);
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
// An explicit provider key in the shell wins over the saved one.
|
|
69
|
+
export function resolveFirecrawlKey(env = process.env) {
|
|
70
|
+
const fromEnv = (env.FIRECRAWL_API_KEY ?? "").trim();
|
|
71
|
+
if (fromEnv)
|
|
72
|
+
return { key: fromEnv, source: "env" };
|
|
73
|
+
const stored = readStoredFirecrawlKey(env);
|
|
74
|
+
if (stored)
|
|
75
|
+
return { key: stored, source: "stored" };
|
|
76
|
+
return { key: null, source: "none" };
|
|
77
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export type JsonPrimitive = null | boolean | number | string;
|
|
2
|
+
export type JsonValue = JsonPrimitive | JsonValue[] | {
|
|
3
|
+
[key: string]: JsonValue;
|
|
4
|
+
};
|
|
5
|
+
export type TrustNamespace = "customer" | "foo";
|
|
6
|
+
export type SigningKeyStatus = "active" | "retired" | "revoked";
|
|
7
|
+
export type EnvelopeVerificationMode = "accept" | "revalidate";
|
|
8
|
+
export type DeepReadonly<T> = T extends JsonPrimitive ? T : T extends readonly (infer Item)[] ? readonly DeepReadonly<Item>[] : T extends object ? {
|
|
9
|
+
readonly [Key in keyof T]: DeepReadonly<T[Key]>;
|
|
10
|
+
} : T;
|
|
11
|
+
export interface TrustedSigningKey {
|
|
12
|
+
key_id: string;
|
|
13
|
+
public_key_pem: string;
|
|
14
|
+
status: SigningKeyStatus;
|
|
15
|
+
valid_from: string;
|
|
16
|
+
valid_until: string | null;
|
|
17
|
+
}
|
|
18
|
+
export interface TrustStore<N extends TrustNamespace = TrustNamespace> {
|
|
19
|
+
namespace: N;
|
|
20
|
+
keys: readonly TrustedSigningKey[];
|
|
21
|
+
}
|
|
22
|
+
export interface SplitTrustStores {
|
|
23
|
+
customer: TrustStore<"customer">;
|
|
24
|
+
foo: TrustStore<"foo">;
|
|
25
|
+
}
|
|
26
|
+
export interface EnvelopeInput<T extends JsonValue = JsonValue> {
|
|
27
|
+
namespace: TrustNamespace;
|
|
28
|
+
artifact_kind: string;
|
|
29
|
+
artifact_id: string;
|
|
30
|
+
payload: T;
|
|
31
|
+
key_id: string;
|
|
32
|
+
issued_at: string;
|
|
33
|
+
not_before: string;
|
|
34
|
+
expires_at: string;
|
|
35
|
+
sequence: number;
|
|
36
|
+
previous_hash: string | null;
|
|
37
|
+
}
|
|
38
|
+
export interface UnsignedEnvelope<T extends JsonValue = JsonValue> extends EnvelopeInput<T> {
|
|
39
|
+
schema_version: "1";
|
|
40
|
+
signature_algorithm: "Ed25519";
|
|
41
|
+
}
|
|
42
|
+
export interface SignedEnvelope<T extends JsonValue = JsonValue> extends UnsignedEnvelope<T> {
|
|
43
|
+
signature: string;
|
|
44
|
+
}
|
|
45
|
+
export interface AcceptedEnvelopeState {
|
|
46
|
+
readonly namespace: TrustNamespace;
|
|
47
|
+
readonly artifact_kind: string;
|
|
48
|
+
readonly artifact_id: string;
|
|
49
|
+
readonly sequence: number;
|
|
50
|
+
readonly envelope_hash: string;
|
|
51
|
+
}
|
|
52
|
+
export type EnvelopeFailureReason = "malformed_envelope" | "wrong_namespace" | "wrong_artifact_kind" | "wrong_artifact_id" | "invalid_trust_store" | "unknown_key" | "revoked_key" | "retired_key_for_new_acceptance" | "key_not_yet_valid" | "key_expired" | "envelope_not_yet_valid" | "envelope_expired" | "signature_invalid" | "invalid_sequence_state" | "replay" | "rollback" | "sequence_gap" | "previous_hash_mismatch";
|
|
53
|
+
export interface VerifyEnvelopeOptions<N extends TrustNamespace = TrustNamespace> {
|
|
54
|
+
expected_namespace: N;
|
|
55
|
+
expected_artifact_kind: string;
|
|
56
|
+
expected_artifact_id: string;
|
|
57
|
+
trust_stores: SplitTrustStores;
|
|
58
|
+
accepted_state: AcceptedEnvelopeState | null;
|
|
59
|
+
verification_mode: EnvelopeVerificationMode;
|
|
60
|
+
now: string;
|
|
61
|
+
}
|
|
62
|
+
export type VerifyEnvelopeResult = {
|
|
63
|
+
ok: true;
|
|
64
|
+
key_status: "active" | "retired";
|
|
65
|
+
envelope_hash: string;
|
|
66
|
+
next_state: Readonly<AcceptedEnvelopeState>;
|
|
67
|
+
verified_envelope: DeepReadonly<SignedEnvelope>;
|
|
68
|
+
} | {
|
|
69
|
+
ok: false;
|
|
70
|
+
reason: EnvelopeFailureReason;
|
|
71
|
+
};
|
|
72
|
+
/** RFC 8785 JCS serialization for JSON-domain values. */
|
|
73
|
+
export declare function canonicalJson(value: JsonValue): string;
|
|
74
|
+
export declare function canonicalJsonBytes(value: JsonValue): Buffer;
|
|
75
|
+
export declare function envelopeSigningBytes(envelope: SignedEnvelope | UnsignedEnvelope): Buffer;
|
|
76
|
+
export declare function envelopeHash(envelope: SignedEnvelope): string;
|
|
77
|
+
export declare function signEnvelope<T extends JsonValue>(input: EnvelopeInput<T>, privateKeyPem: string): SignedEnvelope<T>;
|
|
78
|
+
export declare function verifyEnvelope(envelopeValue: unknown, options: VerifyEnvelopeOptions): VerifyEnvelopeResult;
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import { createHash, createPrivateKey, createPublicKey, sign as ed25519Sign, verify as ed25519Verify, } from "node:crypto";
|
|
2
|
+
const ENVELOPE_SCHEMA_VERSION = "1";
|
|
3
|
+
const SIGNATURE_ALGORITHM = "Ed25519";
|
|
4
|
+
const SIGNING_DOMAIN = "foo.signed-envelope.v1";
|
|
5
|
+
const HASH_DOMAIN = "foo.signed-envelope-hash.v1";
|
|
6
|
+
const HEX_SHA256 = /^[0-9a-f]{64}$/;
|
|
7
|
+
const KEY_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
|
|
8
|
+
const ARTIFACT_KIND = /^[a-z0-9][a-z0-9.-]{0,127}$/;
|
|
9
|
+
function assertUnicodeScalars(value) {
|
|
10
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
11
|
+
const code = value.charCodeAt(index);
|
|
12
|
+
if (code >= 0xd800 && code <= 0xdbff) {
|
|
13
|
+
const next = value.charCodeAt(index + 1);
|
|
14
|
+
if (!(next >= 0xdc00 && next <= 0xdfff)) {
|
|
15
|
+
throw new TypeError("JSON strings and property names must contain Unicode scalar values");
|
|
16
|
+
}
|
|
17
|
+
index += 1;
|
|
18
|
+
}
|
|
19
|
+
else if (code >= 0xdc00 && code <= 0xdfff) {
|
|
20
|
+
throw new TypeError("JSON strings and property names must contain Unicode scalar values");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function canonicalize(value) {
|
|
25
|
+
if (value === null)
|
|
26
|
+
return "null";
|
|
27
|
+
if (typeof value === "boolean")
|
|
28
|
+
return value ? "true" : "false";
|
|
29
|
+
if (typeof value === "string") {
|
|
30
|
+
assertUnicodeScalars(value);
|
|
31
|
+
return JSON.stringify(value);
|
|
32
|
+
}
|
|
33
|
+
if (typeof value === "number") {
|
|
34
|
+
if (!Number.isFinite(value))
|
|
35
|
+
throw new TypeError("canonical JSON requires a finite JSON number");
|
|
36
|
+
return JSON.stringify(value);
|
|
37
|
+
}
|
|
38
|
+
if (Array.isArray(value)) {
|
|
39
|
+
const parts = [];
|
|
40
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
41
|
+
if (!(index in value))
|
|
42
|
+
throw new TypeError("canonical JSON refuses a sparse array");
|
|
43
|
+
parts.push(canonicalize(value[index]));
|
|
44
|
+
}
|
|
45
|
+
return `[${parts.join(",")}]`;
|
|
46
|
+
}
|
|
47
|
+
if (typeof value !== "object")
|
|
48
|
+
throw new TypeError("canonical JSON requires a JSON value");
|
|
49
|
+
const proto = Object.getPrototypeOf(value);
|
|
50
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
51
|
+
throw new TypeError("canonical JSON requires a plain JSON object");
|
|
52
|
+
}
|
|
53
|
+
const ownKeys = Reflect.ownKeys(value);
|
|
54
|
+
if (ownKeys.some((key) => typeof key !== "string")) {
|
|
55
|
+
throw new TypeError("canonical JSON requires string property names");
|
|
56
|
+
}
|
|
57
|
+
const keys = ownKeys;
|
|
58
|
+
for (const key of keys) {
|
|
59
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
60
|
+
if (!descriptor?.enumerable || !("value" in descriptor)) {
|
|
61
|
+
throw new TypeError("canonical JSON requires enumerable data properties");
|
|
62
|
+
}
|
|
63
|
+
assertUnicodeScalars(key);
|
|
64
|
+
}
|
|
65
|
+
keys.sort();
|
|
66
|
+
return `{${keys.map((key) => `${JSON.stringify(key)}:${canonicalize(value[key])}`).join(",")}}`;
|
|
67
|
+
}
|
|
68
|
+
/** RFC 8785 JCS serialization for JSON-domain values. */
|
|
69
|
+
export function canonicalJson(value) {
|
|
70
|
+
return canonicalize(value);
|
|
71
|
+
}
|
|
72
|
+
export function canonicalJsonBytes(value) {
|
|
73
|
+
return Buffer.from(canonicalJson(value), "utf8");
|
|
74
|
+
}
|
|
75
|
+
function timestamp(value, field) {
|
|
76
|
+
if (typeof value !== "string")
|
|
77
|
+
throw new TypeError(`${field} must be an RFC 3339 timestamp`);
|
|
78
|
+
const parsed = Date.parse(value);
|
|
79
|
+
if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
|
|
80
|
+
throw new TypeError(`${field} must be a canonical RFC 3339 timestamp`);
|
|
81
|
+
}
|
|
82
|
+
return parsed;
|
|
83
|
+
}
|
|
84
|
+
function boundedString(value, field, max) {
|
|
85
|
+
if (typeof value !== "string" || value.length === 0 || value.length > max || /[\u0000-\u001f\u007f]/.test(value)) {
|
|
86
|
+
throw new TypeError(`${field} must be a non-empty bounded string`);
|
|
87
|
+
}
|
|
88
|
+
assertUnicodeScalars(value);
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
function exactObjectKeys(value, expected) {
|
|
92
|
+
const actual = Object.keys(value).sort();
|
|
93
|
+
const wanted = [...expected].sort();
|
|
94
|
+
if (actual.length !== wanted.length || actual.some((key, index) => key !== wanted[index])) {
|
|
95
|
+
throw new TypeError("signed envelope has missing or unknown fields");
|
|
96
|
+
}
|
|
97
|
+
for (const key of actual) {
|
|
98
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
99
|
+
if (!descriptor?.enumerable || !("value" in descriptor)) {
|
|
100
|
+
throw new TypeError("signed envelope fields must be enumerable data properties");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function assertUnsignedEnvelope(value) {
|
|
105
|
+
if (value.schema_version !== ENVELOPE_SCHEMA_VERSION)
|
|
106
|
+
throw new TypeError("unsupported envelope schema_version");
|
|
107
|
+
if (value.signature_algorithm !== SIGNATURE_ALGORITHM)
|
|
108
|
+
throw new TypeError("unsupported signature algorithm");
|
|
109
|
+
if (value.namespace !== "customer" && value.namespace !== "foo")
|
|
110
|
+
throw new TypeError("invalid trust namespace");
|
|
111
|
+
if (!ARTIFACT_KIND.test(value.artifact_kind))
|
|
112
|
+
throw new TypeError("invalid artifact_kind");
|
|
113
|
+
boundedString(value.artifact_id, "artifact_id", 256);
|
|
114
|
+
if (!KEY_ID.test(value.key_id))
|
|
115
|
+
throw new TypeError("invalid key_id");
|
|
116
|
+
canonicalJson(value.payload);
|
|
117
|
+
const issuedAt = timestamp(value.issued_at, "issued_at");
|
|
118
|
+
const notBefore = timestamp(value.not_before, "not_before");
|
|
119
|
+
const expiresAt = timestamp(value.expires_at, "expires_at");
|
|
120
|
+
if (notBefore > issuedAt || expiresAt <= issuedAt)
|
|
121
|
+
throw new TypeError("invalid envelope validity interval");
|
|
122
|
+
if (!Number.isSafeInteger(value.sequence) || value.sequence < 1)
|
|
123
|
+
throw new TypeError("sequence must be a positive safe integer");
|
|
124
|
+
if (value.sequence === 1 && value.previous_hash !== null)
|
|
125
|
+
throw new TypeError("first envelope must have no previous_hash");
|
|
126
|
+
if (value.sequence > 1 && (typeof value.previous_hash !== "string" || !HEX_SHA256.test(value.previous_hash))) {
|
|
127
|
+
throw new TypeError("subsequent envelope needs a lowercase SHA-256 previous_hash");
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const UNSIGNED_FIELDS = [
|
|
131
|
+
"schema_version",
|
|
132
|
+
"namespace",
|
|
133
|
+
"artifact_kind",
|
|
134
|
+
"artifact_id",
|
|
135
|
+
"payload",
|
|
136
|
+
"key_id",
|
|
137
|
+
"issued_at",
|
|
138
|
+
"not_before",
|
|
139
|
+
"expires_at",
|
|
140
|
+
"sequence",
|
|
141
|
+
"previous_hash",
|
|
142
|
+
"signature_algorithm",
|
|
143
|
+
];
|
|
144
|
+
function parseSignedEnvelope(value) {
|
|
145
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
146
|
+
throw new TypeError("signed envelope must be an object");
|
|
147
|
+
const record = value;
|
|
148
|
+
exactObjectKeys(record, [...UNSIGNED_FIELDS, "signature"]);
|
|
149
|
+
if (typeof record.signature !== "string")
|
|
150
|
+
throw new TypeError("signature must be base64");
|
|
151
|
+
const envelope = record;
|
|
152
|
+
assertUnsignedEnvelope(envelope);
|
|
153
|
+
decodeSignature(envelope.signature);
|
|
154
|
+
return envelope;
|
|
155
|
+
}
|
|
156
|
+
function deepFreezeOwned(value) {
|
|
157
|
+
if (value !== null && typeof value === "object") {
|
|
158
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
159
|
+
deepFreezeOwned(value[key]);
|
|
160
|
+
}
|
|
161
|
+
Object.freeze(value);
|
|
162
|
+
}
|
|
163
|
+
return value;
|
|
164
|
+
}
|
|
165
|
+
function ownedSignedEnvelope(value) {
|
|
166
|
+
const parsed = parseSignedEnvelope(value);
|
|
167
|
+
const owned = JSON.parse(canonicalJson(parsed));
|
|
168
|
+
return parseSignedEnvelope(owned);
|
|
169
|
+
}
|
|
170
|
+
function unsignedEnvelope(envelope) {
|
|
171
|
+
const { signature: _signature, ...unsigned } = envelope;
|
|
172
|
+
exactObjectKeys(unsigned, UNSIGNED_FIELDS);
|
|
173
|
+
assertUnsignedEnvelope(unsigned);
|
|
174
|
+
return unsigned;
|
|
175
|
+
}
|
|
176
|
+
export function envelopeSigningBytes(envelope) {
|
|
177
|
+
const unsigned = unsignedEnvelope(envelope);
|
|
178
|
+
const prefix = `${SIGNING_DOMAIN}\0${unsigned.namespace}\0${unsigned.artifact_kind}\0`;
|
|
179
|
+
return Buffer.concat([Buffer.from(prefix, "utf8"), canonicalJsonBytes(unsigned)]);
|
|
180
|
+
}
|
|
181
|
+
export function envelopeHash(envelope) {
|
|
182
|
+
const parsed = parseSignedEnvelope(envelope);
|
|
183
|
+
const bytes = Buffer.concat([
|
|
184
|
+
Buffer.from(`${HASH_DOMAIN}\0`, "utf8"),
|
|
185
|
+
canonicalJsonBytes(parsed),
|
|
186
|
+
]);
|
|
187
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
188
|
+
}
|
|
189
|
+
function decodeSignature(signature) {
|
|
190
|
+
if (!/^[A-Za-z0-9+/]+={0,2}$/.test(signature) || signature.length % 4 !== 0) {
|
|
191
|
+
throw new TypeError("signature must be canonical base64");
|
|
192
|
+
}
|
|
193
|
+
const bytes = Buffer.from(signature, "base64");
|
|
194
|
+
if (bytes.length !== 64 || bytes.toString("base64") !== signature) {
|
|
195
|
+
throw new TypeError("signature must be a canonical Ed25519 signature");
|
|
196
|
+
}
|
|
197
|
+
return bytes;
|
|
198
|
+
}
|
|
199
|
+
export function signEnvelope(input, privateKeyPem) {
|
|
200
|
+
const unsigned = {
|
|
201
|
+
schema_version: ENVELOPE_SCHEMA_VERSION,
|
|
202
|
+
namespace: input.namespace,
|
|
203
|
+
artifact_kind: input.artifact_kind,
|
|
204
|
+
artifact_id: input.artifact_id,
|
|
205
|
+
payload: input.payload,
|
|
206
|
+
key_id: input.key_id,
|
|
207
|
+
issued_at: input.issued_at,
|
|
208
|
+
not_before: input.not_before,
|
|
209
|
+
expires_at: input.expires_at,
|
|
210
|
+
sequence: input.sequence,
|
|
211
|
+
previous_hash: input.previous_hash,
|
|
212
|
+
signature_algorithm: SIGNATURE_ALGORITHM,
|
|
213
|
+
};
|
|
214
|
+
assertUnsignedEnvelope(unsigned);
|
|
215
|
+
const privateKey = createPrivateKey(privateKeyPem);
|
|
216
|
+
if (privateKey.asymmetricKeyType !== "ed25519")
|
|
217
|
+
throw new TypeError("signing key must be Ed25519");
|
|
218
|
+
const signature = ed25519Sign(null, envelopeSigningBytes(unsigned), privateKey).toString("base64");
|
|
219
|
+
return { ...unsigned, signature };
|
|
220
|
+
}
|
|
221
|
+
function validateTrustStore(store, expectedNamespace) {
|
|
222
|
+
if (!store || store.namespace !== expectedNamespace || !Array.isArray(store.keys)) {
|
|
223
|
+
throw new TypeError("trust store namespace does not match its configured slot");
|
|
224
|
+
}
|
|
225
|
+
const keys = new Map();
|
|
226
|
+
for (const key of store.keys) {
|
|
227
|
+
if (!key || typeof key !== "object")
|
|
228
|
+
throw new TypeError("invalid trusted key");
|
|
229
|
+
exactObjectKeys(key, [
|
|
230
|
+
"key_id",
|
|
231
|
+
"public_key_pem",
|
|
232
|
+
"status",
|
|
233
|
+
"valid_from",
|
|
234
|
+
"valid_until",
|
|
235
|
+
]);
|
|
236
|
+
if (!KEY_ID.test(key.key_id) || keys.has(key.key_id))
|
|
237
|
+
throw new TypeError("duplicate or invalid trusted key id");
|
|
238
|
+
if (key.status !== "active" && key.status !== "retired" && key.status !== "revoked") {
|
|
239
|
+
throw new TypeError("invalid trusted key status");
|
|
240
|
+
}
|
|
241
|
+
timestamp(key.valid_from, "trusted key valid_from");
|
|
242
|
+
if (key.valid_until !== null) {
|
|
243
|
+
if (timestamp(key.valid_until, "trusted key valid_until") <= timestamp(key.valid_from, "trusted key valid_from")) {
|
|
244
|
+
throw new TypeError("invalid trusted key validity interval");
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const publicKey = createPublicKey(key.public_key_pem);
|
|
248
|
+
if (publicKey.asymmetricKeyType !== "ed25519")
|
|
249
|
+
throw new TypeError("trusted key must be Ed25519");
|
|
250
|
+
keys.set(key.key_id, key);
|
|
251
|
+
}
|
|
252
|
+
return keys;
|
|
253
|
+
}
|
|
254
|
+
function validateAcceptedState(state, envelope) {
|
|
255
|
+
if (state === null) {
|
|
256
|
+
if (envelope.sequence !== 1)
|
|
257
|
+
return "sequence_gap";
|
|
258
|
+
if (envelope.previous_hash !== null)
|
|
259
|
+
return "previous_hash_mismatch";
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
const shapeFailure = validateAcceptedStateShape(state, envelope);
|
|
263
|
+
if (shapeFailure)
|
|
264
|
+
return shapeFailure;
|
|
265
|
+
if (envelope.sequence < state.sequence)
|
|
266
|
+
return "rollback";
|
|
267
|
+
if (envelope.sequence === state.sequence)
|
|
268
|
+
return "replay";
|
|
269
|
+
if (envelope.sequence !== state.sequence + 1)
|
|
270
|
+
return "sequence_gap";
|
|
271
|
+
if (envelope.previous_hash !== state.envelope_hash)
|
|
272
|
+
return "previous_hash_mismatch";
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
function validateRevalidationState(state, envelope, hash) {
|
|
276
|
+
if (state === null)
|
|
277
|
+
return "invalid_sequence_state";
|
|
278
|
+
const shapeFailure = validateAcceptedStateShape(state, envelope);
|
|
279
|
+
if (shapeFailure)
|
|
280
|
+
return shapeFailure;
|
|
281
|
+
if (state.sequence !== envelope.sequence || state.envelope_hash !== hash)
|
|
282
|
+
return "invalid_sequence_state";
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
function validateAcceptedStateShape(state, envelope) {
|
|
286
|
+
try {
|
|
287
|
+
exactObjectKeys(state, [
|
|
288
|
+
"namespace",
|
|
289
|
+
"artifact_kind",
|
|
290
|
+
"artifact_id",
|
|
291
|
+
"sequence",
|
|
292
|
+
"envelope_hash",
|
|
293
|
+
]);
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
return "invalid_sequence_state";
|
|
297
|
+
}
|
|
298
|
+
if ((state.namespace !== "customer" && state.namespace !== "foo")
|
|
299
|
+
|| !ARTIFACT_KIND.test(state.artifact_kind)
|
|
300
|
+
|| typeof state.artifact_id !== "string"
|
|
301
|
+
|| !Number.isSafeInteger(state.sequence) || state.sequence < 1
|
|
302
|
+
|| !HEX_SHA256.test(state.envelope_hash)) {
|
|
303
|
+
return "invalid_sequence_state";
|
|
304
|
+
}
|
|
305
|
+
if (state.namespace !== envelope.namespace
|
|
306
|
+
|| state.artifact_kind !== envelope.artifact_kind
|
|
307
|
+
|| state.artifact_id !== envelope.artifact_id) {
|
|
308
|
+
return "invalid_sequence_state";
|
|
309
|
+
}
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
export function verifyEnvelope(envelopeValue, options) {
|
|
313
|
+
let envelope;
|
|
314
|
+
try {
|
|
315
|
+
envelope = ownedSignedEnvelope(envelopeValue);
|
|
316
|
+
timestamp(options.now, "verification now");
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
return { ok: false, reason: "malformed_envelope" };
|
|
320
|
+
}
|
|
321
|
+
if (envelope.namespace !== options.expected_namespace)
|
|
322
|
+
return { ok: false, reason: "wrong_namespace" };
|
|
323
|
+
if (envelope.artifact_kind !== options.expected_artifact_kind)
|
|
324
|
+
return { ok: false, reason: "wrong_artifact_kind" };
|
|
325
|
+
if (envelope.artifact_id !== options.expected_artifact_id)
|
|
326
|
+
return { ok: false, reason: "wrong_artifact_id" };
|
|
327
|
+
let keys;
|
|
328
|
+
try {
|
|
329
|
+
const customerKeys = validateTrustStore(options.trust_stores.customer, "customer");
|
|
330
|
+
const fooKeys = validateTrustStore(options.trust_stores.foo, "foo");
|
|
331
|
+
keys = options.expected_namespace === "customer" ? customerKeys : fooKeys;
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
return { ok: false, reason: "invalid_trust_store" };
|
|
335
|
+
}
|
|
336
|
+
const trustedKey = keys.get(envelope.key_id);
|
|
337
|
+
if (!trustedKey)
|
|
338
|
+
return { ok: false, reason: "unknown_key" };
|
|
339
|
+
if (trustedKey.status === "revoked")
|
|
340
|
+
return { ok: false, reason: "revoked_key" };
|
|
341
|
+
const issuedAt = Date.parse(envelope.issued_at);
|
|
342
|
+
const now = Date.parse(options.now);
|
|
343
|
+
if (issuedAt < Date.parse(trustedKey.valid_from))
|
|
344
|
+
return { ok: false, reason: "key_not_yet_valid" };
|
|
345
|
+
if (trustedKey.valid_until !== null && issuedAt >= Date.parse(trustedKey.valid_until)) {
|
|
346
|
+
return { ok: false, reason: "key_expired" };
|
|
347
|
+
}
|
|
348
|
+
if (now < Date.parse(envelope.not_before) || now < issuedAt)
|
|
349
|
+
return { ok: false, reason: "envelope_not_yet_valid" };
|
|
350
|
+
if (now >= Date.parse(envelope.expires_at))
|
|
351
|
+
return { ok: false, reason: "envelope_expired" };
|
|
352
|
+
let signatureOk = false;
|
|
353
|
+
try {
|
|
354
|
+
const publicKey = createPublicKey(trustedKey.public_key_pem);
|
|
355
|
+
signatureOk = ed25519Verify(null, envelopeSigningBytes(envelope), publicKey, decodeSignature(envelope.signature));
|
|
356
|
+
}
|
|
357
|
+
catch {
|
|
358
|
+
return { ok: false, reason: "invalid_trust_store" };
|
|
359
|
+
}
|
|
360
|
+
if (!signatureOk)
|
|
361
|
+
return { ok: false, reason: "signature_invalid" };
|
|
362
|
+
const hash = envelopeHash(envelope);
|
|
363
|
+
let sequenceFailure;
|
|
364
|
+
if (options.verification_mode === "accept") {
|
|
365
|
+
if (trustedKey.status === "retired")
|
|
366
|
+
return { ok: false, reason: "retired_key_for_new_acceptance" };
|
|
367
|
+
sequenceFailure = validateAcceptedState(options.accepted_state, envelope);
|
|
368
|
+
}
|
|
369
|
+
else if (options.verification_mode === "revalidate") {
|
|
370
|
+
sequenceFailure = validateRevalidationState(options.accepted_state, envelope, hash);
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
return { ok: false, reason: "invalid_sequence_state" };
|
|
374
|
+
}
|
|
375
|
+
if (sequenceFailure)
|
|
376
|
+
return { ok: false, reason: sequenceFailure };
|
|
377
|
+
const nextState = Object.freeze({
|
|
378
|
+
namespace: envelope.namespace,
|
|
379
|
+
artifact_kind: envelope.artifact_kind,
|
|
380
|
+
artifact_id: envelope.artifact_id,
|
|
381
|
+
sequence: envelope.sequence,
|
|
382
|
+
envelope_hash: hash,
|
|
383
|
+
});
|
|
384
|
+
const verifiedEnvelope = deepFreezeOwned(envelope);
|
|
385
|
+
return Object.freeze({
|
|
386
|
+
ok: true,
|
|
387
|
+
key_status: trustedKey.status,
|
|
388
|
+
envelope_hash: hash,
|
|
389
|
+
next_state: nextState,
|
|
390
|
+
verified_envelope: verifiedEnvelope,
|
|
391
|
+
});
|
|
392
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Pure, local text neutralization shared by local capture paths. This module
|
|
2
|
+
// does not fetch, approve, render, validate, or issue a trust result: it only
|
|
3
|
+
// replaces directive-shaped substrings in caller-provided text.
|
|
4
|
+
const DIRECTIVE_PATTERNS = [
|
|
5
|
+
/\b(ignore|disregard|forget|override)\b[^\n]{0,60}?\b(previous|prior|above|all|earlier)\b[^\n]{0,40}?\b(instruction|prompt|rule|direction)s?\b/gi,
|
|
6
|
+
/\b(system|developer)\s*:|\byou (must|should|are|will) now\b|\bnew instructions?\b\s*:/gi,
|
|
7
|
+
/\b(send|post|upload|transmit|email|exfiltrate|leak|curl|wget|fetch|forward)\b[^\n]{0,100}?\b(to|https?:\/\/|api|webhook|endpoint|\.env|secret|token|key|credential)\b[^\n]{0,80}/gi,
|
|
8
|
+
/\b(read|open|cat|print|reveal|show|dump|access)\b[^\n]{0,100}?\b(\.env|secret|api[_ -]?key|token|credential|password|private[_ -]?key)s?\b/gi,
|
|
9
|
+
];
|
|
10
|
+
export function neutralizeUntrustedText(content) {
|
|
11
|
+
const stripped = [];
|
|
12
|
+
let inert = content;
|
|
13
|
+
for (const pattern of DIRECTIVE_PATTERNS) {
|
|
14
|
+
inert = inert.replace(pattern, (match) => {
|
|
15
|
+
stripped.push(match.trim());
|
|
16
|
+
return "[stripped: untrusted directive — treated as inert data, never executed]";
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return { inert_text: inert, stripped_directives: stripped, triggered: stripped.length > 0 };
|
|
20
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export declare function clientVersion(): string;
|
|
2
|
+
export declare const CLIENT_VERSION_HEADER = "x-foo-client-version";
|
|
3
|
+
export declare const LATEST_VERSION_HEADER = "x-foo-latest-version";
|
|
4
|
+
export declare const UPDATE_COMMAND = "foo update";
|
|
5
|
+
export declare const CHANGELOG_URL: string;
|
|
6
|
+
export type NoticeAudience = "human" | "agent" | "none";
|
|
7
|
+
export type UpdateNoticePlan = {
|
|
8
|
+
notify: boolean;
|
|
9
|
+
line: string | null;
|
|
10
|
+
audience?: NoticeAudience;
|
|
11
|
+
};
|
|
12
|
+
export declare function updateNoticePlan(input: {
|
|
13
|
+
running: string;
|
|
14
|
+
latest: string | null;
|
|
15
|
+
lastNotifiedDay: string | null;
|
|
16
|
+
lastNotifiedVersion?: string | null;
|
|
17
|
+
lastNotifiedSession?: string | null;
|
|
18
|
+
today: string;
|
|
19
|
+
session: string;
|
|
20
|
+
audience: NoticeAudience;
|
|
21
|
+
}): UpdateNoticePlan;
|
|
22
|
+
export declare function localDay(now?: Date): string;
|
|
23
|
+
export declare function noticeAudienceIsHuman(env?: NodeJS.ProcessEnv, stderrIsTty?: boolean): boolean;
|
|
24
|
+
export declare function noticeAudience(env?: NodeJS.ProcessEnv, stderrIsTty?: boolean): NoticeAudience;
|
|
25
|
+
export declare function currentSession(env?: NodeJS.ProcessEnv): string;
|
|
26
|
+
export type PendingUpdate = {
|
|
27
|
+
running: string;
|
|
28
|
+
latest: string;
|
|
29
|
+
line: string;
|
|
30
|
+
audience: NoticeAudience;
|
|
31
|
+
};
|
|
32
|
+
export declare function resetPendingUpdate(): void;
|
|
33
|
+
export declare function pendingUpdate(): PendingUpdate | null;
|
|
34
|
+
export declare function noteLatestVersion(latest: string | null | undefined, running: string): void;
|
|
35
|
+
export declare function recordUpdateSpoken(latest: string): void;
|
|
36
|
+
export declare function recordRelaySpoken(latest: string, session?: string): void;
|
|
37
|
+
export declare function latestFromResponse(res: {
|
|
38
|
+
headers: {
|
|
39
|
+
get(name: string): string | null;
|
|
40
|
+
};
|
|
41
|
+
}): string | null;
|