@nebulacomponents/citable 1.13.1 → 1.15.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 +312 -0
- package/README.md +44 -6
- package/dist/universal/.agents/skills/citable/SKILL.md +22 -8
- package/dist/universal/.agents/skills/citable/VERSION +1 -1
- package/dist/universal/.agents/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.agents/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.agents/skills/citable/commands/README.md +4 -1
- package/dist/universal/.agents/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.agents/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.agents/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.agents/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.agents/skills/citable/manifest.json +29 -16
- package/dist/universal/.agents/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.agents/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.agents/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.agents/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.agents/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.agents/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.agents/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.agents/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.agents/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.agents/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.agents/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.agents/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.agents/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.claude/agents/citable/citable-auditor.md +64 -0
- package/dist/universal/.claude/agents/citable/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.claude/agents/citable/manifest.json +12 -0
- package/dist/universal/.claude/skills/citable/SKILL.md +22 -8
- package/dist/universal/.claude/skills/citable/VERSION +1 -1
- package/dist/universal/.claude/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.claude/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.claude/skills/citable/commands/README.md +4 -1
- package/dist/universal/.claude/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.claude/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.claude/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.claude/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.claude/skills/citable/manifest.json +34 -16
- package/dist/universal/.claude/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.claude/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.claude/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.claude/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.claude/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.claude/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.claude/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.claude/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.claude/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.claude/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.claude/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.claude/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.claude/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.cursor/skills/citable/SKILL.md +22 -8
- package/dist/universal/.cursor/skills/citable/VERSION +1 -1
- package/dist/universal/.cursor/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.cursor/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.cursor/skills/citable/commands/README.md +4 -1
- package/dist/universal/.cursor/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.cursor/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.cursor/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.cursor/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.cursor/skills/citable/manifest.json +29 -16
- package/dist/universal/.cursor/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.cursor/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.cursor/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.cursor/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.cursor/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.cursor/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.cursor/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.cursor/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.cursor/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.cursor/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.cursor/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.cursor/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.cursor/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.gemini/skills/citable/SKILL.md +22 -8
- package/dist/universal/.gemini/skills/citable/VERSION +1 -1
- package/dist/universal/.gemini/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.gemini/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.gemini/skills/citable/commands/README.md +4 -1
- package/dist/universal/.gemini/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.gemini/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.gemini/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.gemini/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.gemini/skills/citable/manifest.json +29 -16
- package/dist/universal/.gemini/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.gemini/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.gemini/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.gemini/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.gemini/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.gemini/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.gemini/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.gemini/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.gemini/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.gemini/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.gemini/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.gemini/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.gemini/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.github/skills/citable/SKILL.md +22 -8
- package/dist/universal/.github/skills/citable/VERSION +1 -1
- package/dist/universal/.github/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.github/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.github/skills/citable/commands/README.md +4 -1
- package/dist/universal/.github/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.github/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.github/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.github/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.github/skills/citable/manifest.json +29 -16
- package/dist/universal/.github/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.github/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.github/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.github/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.github/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.github/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.github/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.github/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.github/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.github/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.github/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.github/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.github/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.kiro/skills/citable/SKILL.md +22 -8
- package/dist/universal/.kiro/skills/citable/VERSION +1 -1
- package/dist/universal/.kiro/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.kiro/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.kiro/skills/citable/commands/README.md +4 -1
- package/dist/universal/.kiro/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.kiro/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.kiro/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.kiro/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.kiro/skills/citable/manifest.json +29 -16
- package/dist/universal/.kiro/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.kiro/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.kiro/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.kiro/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.kiro/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.kiro/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.kiro/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.kiro/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.kiro/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.kiro/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.kiro/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.kiro/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.kiro/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.opencode/skills/citable/SKILL.md +22 -8
- package/dist/universal/.opencode/skills/citable/VERSION +1 -1
- package/dist/universal/.opencode/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.opencode/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.opencode/skills/citable/commands/README.md +4 -1
- package/dist/universal/.opencode/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.opencode/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.opencode/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.opencode/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.opencode/skills/citable/manifest.json +29 -16
- package/dist/universal/.opencode/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.opencode/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.opencode/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.opencode/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.opencode/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.opencode/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.opencode/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.opencode/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.opencode/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.opencode/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.opencode/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.opencode/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.opencode/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.pi/agent/skills/citable/SKILL.md +22 -8
- package/dist/universal/.pi/agent/skills/citable/VERSION +1 -1
- package/dist/universal/.pi/agent/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.pi/agent/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.pi/agent/skills/citable/commands/README.md +4 -1
- package/dist/universal/.pi/agent/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.pi/agent/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.pi/agent/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.pi/agent/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.pi/agent/skills/citable/manifest.json +29 -16
- package/dist/universal/.pi/agent/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.pi/agent/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.pi/agent/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.pi/agent/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.pi/agent/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.qoder/skills/citable/SKILL.md +22 -8
- package/dist/universal/.qoder/skills/citable/VERSION +1 -1
- package/dist/universal/.qoder/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.qoder/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.qoder/skills/citable/commands/README.md +4 -1
- package/dist/universal/.qoder/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.qoder/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.qoder/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.qoder/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.qoder/skills/citable/manifest.json +29 -16
- package/dist/universal/.qoder/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.qoder/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.qoder/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.qoder/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.qoder/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.qoder/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.qoder/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.qoder/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.qoder/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.qoder/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.qoder/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.qoder/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.qoder/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.rovodev/skills/citable/SKILL.md +22 -8
- package/dist/universal/.rovodev/skills/citable/VERSION +1 -1
- package/dist/universal/.rovodev/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.rovodev/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.rovodev/skills/citable/commands/README.md +4 -1
- package/dist/universal/.rovodev/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.rovodev/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.rovodev/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.rovodev/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.rovodev/skills/citable/manifest.json +29 -16
- package/dist/universal/.rovodev/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.rovodev/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.rovodev/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.rovodev/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.rovodev/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.rovodev/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.rovodev/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.rovodev/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.rovodev/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.rovodev/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.rovodev/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.rovodev/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.rovodev/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.trae/skills/citable/SKILL.md +22 -8
- package/dist/universal/.trae/skills/citable/VERSION +1 -1
- package/dist/universal/.trae/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.trae/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.trae/skills/citable/commands/README.md +4 -1
- package/dist/universal/.trae/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.trae/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.trae/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.trae/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.trae/skills/citable/manifest.json +29 -16
- package/dist/universal/.trae/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.trae/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.trae/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.trae/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.trae/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.trae/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.trae/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.trae/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.trae/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.trae/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.trae/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.trae/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.trae/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.trae-cn/skills/citable/SKILL.md +22 -8
- package/dist/universal/.trae-cn/skills/citable/VERSION +1 -1
- package/dist/universal/.trae-cn/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.trae-cn/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.trae-cn/skills/citable/commands/README.md +4 -1
- package/dist/universal/.trae-cn/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.trae-cn/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.trae-cn/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.trae-cn/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.trae-cn/skills/citable/manifest.json +29 -16
- package/dist/universal/.trae-cn/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.trae-cn/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.trae-cn/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.trae-cn/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.trae-cn/skills/citable/scripts/README.md +3 -1
- package/dist/universal/manifest.json +306 -195
- package/package.json +1 -1
- package/schemas/action-plan.schema.json +13 -0
- package/schemas/browser-evidence-plan.schema.json +14 -0
- package/schemas/canonical-index-import.schema.json +30 -0
- package/schemas/common.defs.json +12 -1
- package/schemas/corroboration-import.schema.json +48 -0
- package/schemas/entity-graph.schema.json +75 -0
- package/schemas/entity.schema.json +13 -0
- package/schemas/observation.schema.json +1 -1
- package/schemas/page.schema.json +3 -0
- package/schemas/regional-network-import.schema.json +55 -0
- package/schemas/schedule.schema.json +1 -1
- package/schemas/source-identity-chain.schema.json +83 -0
- package/scripts/build-dist.js +36 -3
- package/skill/SKILL.md +22 -8
- package/skill/agents/citable-auditor.md +64 -0
- package/skill/agents/citable-semantic-reviewer.md +66 -0
- package/skill/commands/README.md +4 -1
- package/skill/commands/action-findings.md +9 -1
- package/skill/commands/observe-and-act.md +62 -8
- package/skill/commands/plan-audit.md +35 -0
- package/skill/commands/reporting.md +57 -0
- package/src/cli/index.js +115 -15
- package/src/commands/actionPlan.js +12 -1
- package/src/commands/audit.js +4 -0
- package/src/commands/connect.js +29 -0
- package/src/commands/context.js +15 -0
- package/src/commands/delivery.js +86 -2
- package/src/commands/demo.js +36 -0
- package/src/commands/fixtures/demo/.citable/claims.yaml +27 -0
- package/src/commands/fixtures/demo/.citable/competitors.yaml +3 -0
- package/src/commands/fixtures/demo/.citable/config.yaml +18 -0
- package/src/commands/fixtures/demo/.citable/crawlers.yaml +30 -0
- package/src/commands/fixtures/demo/.citable/entities.yaml +28 -0
- package/src/commands/fixtures/demo/.citable/evidence.yaml +19 -0
- package/src/commands/fixtures/demo/.citable/experiments.yaml +3 -0
- package/src/commands/fixtures/demo/.citable/pages.yaml +54 -0
- package/src/commands/fixtures/demo/.citable/prompts.yaml +11 -0
- package/src/commands/fixtures/demo/.citable/queries.yaml +10 -0
- package/src/commands/fixtures/demo/site/index.html +12 -0
- package/src/commands/fixtures/demo/site/learn/execution-governance/index.html +14 -0
- package/src/commands/fixtures/demo/site/pricing/index.html +10 -0
- package/src/commands/fixtures/demo/site/products/gatekeeper/index.html +15 -0
- package/src/commands/fixtures/demo/site/robots.txt +7 -0
- package/src/commands/fixtures/demo/site/sitemap.xml +7 -0
- package/src/commands/init.js +5 -2
- package/src/commands/monitor.js +35 -0
- package/src/commands/observe.js +315 -15
- package/src/commands/planAudit.js +151 -0
- package/src/commands/reportDashboard.js +21 -0
- package/src/commands/reportShareOfVoice.js +60 -0
- package/src/commands/schemaCmd.js +5 -1
- package/src/commands/substantiate.js +22 -10
- package/src/connectors/http.js +6 -3
- package/src/connectors/index.js +3 -1
- package/src/connectors/webflow.js +224 -0
- package/src/connectors/wordpress.js +226 -0
- package/src/crawler/fetch.js +34 -6
- package/src/detectors/geoReco.js +101 -0
- package/src/extractor/page.js +17 -0
- package/src/installer/index.js +282 -28
- package/src/installer/providers.js +11 -0
- package/src/monitoring/alertDelivery.js +98 -0
- package/src/observations/browserJourney.js +15 -7
- package/src/observations/claimDiff.js +83 -0
- package/src/observations/common.js +25 -0
- package/src/observations/entityGraph.js +89 -0
- package/src/observations/renderParity.js +49 -0
- package/src/observations/sourceIdentity.js +62 -0
- package/src/observations/stance.js +261 -0
- package/src/registries/seed.js +95 -0
- package/src/registries/seeds/saas-pricing/claims.yaml +18 -0
- package/src/registries/seeds/saas-pricing/entities.yaml +20 -0
- package/src/registries/seeds/saas-pricing/seed.json +6 -0
- package/src/reporting/dashboard.js +254 -0
- package/src/reporting/shareOfVoice.js +362 -0
- package/src/shared/checklist.js +19 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import {
|
|
4
|
+
calculateShareOfVoice,
|
|
5
|
+
extractDomain,
|
|
6
|
+
loadCitationRuns,
|
|
7
|
+
renderShareOfVoiceHtml,
|
|
8
|
+
renderShareOfVoiceMarkdown,
|
|
9
|
+
} from '../reporting/shareOfVoice.js';
|
|
10
|
+
import { loadRegistries } from '../registries/index.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* `citable report share-of-voice` — compute first-party and competitor citation share
|
|
14
|
+
* across recorded observation runs. Never invents competitor citations; unevidenced
|
|
15
|
+
* prompts and competitors are reported plainly.
|
|
16
|
+
*/
|
|
17
|
+
export function reportShareOfVoice(root, { since, last } = {}) {
|
|
18
|
+
if (last != null && (!Number.isInteger(last) || last < 1)) {
|
|
19
|
+
throw new Error('--last must be a positive integer');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const { registries } = loadRegistries(root);
|
|
23
|
+
const competitors = registries.competitors?.entries || [];
|
|
24
|
+
|
|
25
|
+
const firstPartyDomains = new Set();
|
|
26
|
+
if (registries.config?.site?.base_url) {
|
|
27
|
+
const d = extractDomain(registries.config.site.base_url);
|
|
28
|
+
if (d) firstPartyDomains.add(d);
|
|
29
|
+
}
|
|
30
|
+
if (registries.pages?.entries) {
|
|
31
|
+
for (const p of registries.pages.entries) {
|
|
32
|
+
if (p.url) {
|
|
33
|
+
const d = extractDomain(p.url);
|
|
34
|
+
if (d) firstPartyDomains.add(d);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const { included, skipped } = loadCitationRuns(root, { since, last });
|
|
40
|
+
const data = calculateShareOfVoice(included, competitors, firstPartyDomains);
|
|
41
|
+
|
|
42
|
+
const dir = path.join(root, '.citable', 'reports');
|
|
43
|
+
const pathMd = path.join(dir, 'share-of-voice.md');
|
|
44
|
+
const pathHtml = path.join(dir, 'share-of-voice.html');
|
|
45
|
+
|
|
46
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
47
|
+
fs.writeFileSync(pathMd, renderShareOfVoiceMarkdown(data, skipped));
|
|
48
|
+
fs.writeFileSync(pathHtml, renderShareOfVoiceHtml(data, skipped));
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
dir,
|
|
52
|
+
included: included.length,
|
|
53
|
+
skipped: skipped.length,
|
|
54
|
+
competitors_evaluated: competitors.length,
|
|
55
|
+
prompts_evaluated: data.prompts.length,
|
|
56
|
+
total_citations: data.total_citations_recorded,
|
|
57
|
+
path_md: pathMd,
|
|
58
|
+
path_html: pathHtml,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { buildContext } from './context.js';
|
|
2
2
|
import { selectDetectors } from '../detectors/index.js';
|
|
3
3
|
import { runDetectors, sitePageFor } from '../detectors/framework.js';
|
|
4
|
+
import { checklistItem, toStringArray } from '../shared/checklist.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* `citable schema` — derive JSON-LD proposals from registry data and validate deployed schema.
|
|
@@ -28,7 +29,10 @@ export async function schemaCommand(root, { target, baseUrl, refDate } = {}) {
|
|
|
28
29
|
if (!e.canonical_url) missing.push('canonical_url');
|
|
29
30
|
if (!e.definition && ['proprietary_concept', 'category'].includes(e.entity_type)) missing.push('definition');
|
|
30
31
|
if (missing.length) {
|
|
31
|
-
blocked.push({
|
|
32
|
+
blocked.push({
|
|
33
|
+
entity_id: e.entity_id, status: 'blocked', reason: 'entity record incomplete; schema would require invented facts',
|
|
34
|
+
required_input: toStringArray(missing.map((field) => checklistItem(field, field))),
|
|
35
|
+
});
|
|
32
36
|
continue;
|
|
33
37
|
}
|
|
34
38
|
const id = `${e.canonical_url}#${e.entity_type}`;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { loadRegistries, saveRegistry, diffRegistries } from '../registries/index.js';
|
|
2
2
|
import { isPastDate, nowIso, parseRefDate } from '../shared/io.js';
|
|
3
|
+
import { checklistItem, toStringArray } from '../shared/checklist.js';
|
|
3
4
|
|
|
4
5
|
const REGULATED = new Set(['legal_regulatory', 'security', 'commercial']);
|
|
5
6
|
|
|
@@ -37,7 +38,7 @@ export function substantiate(root, { write = false, refDate } = {}) {
|
|
|
37
38
|
} else if (REGULATED.has(c.claim_type) && (!c.legal_status || c.legal_status === 'not_assessed')) {
|
|
38
39
|
a.outcome = 'review_required';
|
|
39
40
|
a.reasons.push(`${c.claim_type} claim requires legal/SME review before any verified status`);
|
|
40
|
-
a.required_input.push('legal or subject-matter review decision');
|
|
41
|
+
a.required_input.push(checklistItem('legal-review', 'legal or subject-matter review decision'));
|
|
41
42
|
} else if (['opinion', 'position', 'aspirational'].includes(c.claim_type)) {
|
|
42
43
|
// Opinions and aspirations are positions, not verifiable facts; they never enter the verified track.
|
|
43
44
|
a.outcome = c.status === 'candidate' ? 'candidate' : 'unverified';
|
|
@@ -54,19 +55,23 @@ export function substantiate(root, { write = false, refDate } = {}) {
|
|
|
54
55
|
} else if (live.length === 0) {
|
|
55
56
|
a.outcome = 'insufficient_evidence';
|
|
56
57
|
a.reasons.push('all attached evidence is expired, unverified, revoked, or inaccessible');
|
|
57
|
-
a.required_input.push('current, verified evidence');
|
|
58
|
+
a.required_input.push(checklistItem('current-verified-evidence', 'current, verified evidence'));
|
|
58
59
|
} else if (['performance', 'comparative', 'security'].includes(c.claim_type) && live.every((e) => e.primary_or_secondary === 'secondary')) {
|
|
59
60
|
a.outcome = 'insufficient_evidence';
|
|
60
61
|
a.reasons.push(`${c.claim_type} claims require primary evidence; only secondary evidence attached`);
|
|
61
|
-
a.required_input.push('primary evidence (test result, benchmark, specification, certification)');
|
|
62
|
+
a.required_input.push(checklistItem('primary-evidence', 'primary evidence (test result, benchmark, specification, certification)'));
|
|
62
63
|
} else if (['performance'].includes(c.claim_type) && live.every((e) => !e.methodology)) {
|
|
63
64
|
a.outcome = 'insufficient_evidence';
|
|
64
65
|
a.reasons.push('performance evidence lacks methodology');
|
|
65
|
-
a.required_input.push(
|
|
66
|
+
a.required_input.push(
|
|
67
|
+
checklistItem('methodology', 'methodology'),
|
|
68
|
+
checklistItem('test-conditions', 'test conditions'),
|
|
69
|
+
checklistItem('measurement-period', 'measurement period'),
|
|
70
|
+
);
|
|
66
71
|
} else if ((c.scope || []).length === 0 && ['capability', 'performance', 'comparative'].includes(c.claim_type)) {
|
|
67
72
|
a.outcome = 'verified_narrowed';
|
|
68
73
|
a.reasons.push('evidence exists but claim declares no scope; treat as verified only with narrowed scope pending scope definition');
|
|
69
|
-
a.required_input.push('explicit scope and exclusions');
|
|
74
|
+
a.required_input.push(checklistItem('scope-exclusions', 'explicit scope and exclusions'));
|
|
70
75
|
} else {
|
|
71
76
|
// Deterministic conditions passed. Verified status additionally requires a recorded
|
|
72
77
|
// semantic support assessment (entailment) by a named assessor — evidence linkage
|
|
@@ -82,7 +87,7 @@ export function substantiate(root, { write = false, refDate } = {}) {
|
|
|
82
87
|
} else {
|
|
83
88
|
a.outcome = 'review_required';
|
|
84
89
|
a.reasons.push('verified status held without an adequate semantic support assessment; entailment review required (evidence-strength rubric)');
|
|
85
|
-
a.required_input.push('support_assessment with named assessor');
|
|
90
|
+
a.required_input.push(checklistItem('support-assessment', 'support_assessment with named assessor'));
|
|
86
91
|
}
|
|
87
92
|
} else {
|
|
88
93
|
a.outcome = 'review_required';
|
|
@@ -90,6 +95,7 @@ export function substantiate(root, { write = false, refDate } = {}) {
|
|
|
90
95
|
}
|
|
91
96
|
}
|
|
92
97
|
}
|
|
98
|
+
a.required_input = toStringArray(a.required_input);
|
|
93
99
|
assessments.push(a);
|
|
94
100
|
}
|
|
95
101
|
|
|
@@ -114,8 +120,14 @@ export function substantiate(root, { write = false, refDate } = {}) {
|
|
|
114
120
|
}
|
|
115
121
|
|
|
116
122
|
function requiredInputFor(c) {
|
|
117
|
-
if (c.claim_type === 'performance')
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
123
|
+
if (c.claim_type === 'performance') {
|
|
124
|
+
return ['baseline', 'measurement period', 'test population/conditions', 'methodology', 'result source']
|
|
125
|
+
.map((label) => checklistItem(label.replace(/[^a-z0-9]+/gi, '-').toLowerCase(), label));
|
|
126
|
+
}
|
|
127
|
+
if (c.claim_type === 'comparative') {
|
|
128
|
+
return ['comparison set', 'comparison criteria', 'evidence for each compared dimension']
|
|
129
|
+
.map((label) => checklistItem(label.replace(/[^a-z0-9]+/gi, '-').toLowerCase(), label));
|
|
130
|
+
}
|
|
131
|
+
if (c.claim_type === 'security') return [checklistItem('security-evidence', 'test report, certification, or architecture evidence')];
|
|
132
|
+
return [checklistItem('owned-evidence', 'supporting evidence with an accountable owner')];
|
|
121
133
|
}
|
package/src/connectors/http.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
const RETRYABLE = new Set([429, 500, 502, 503, 504]);
|
|
2
2
|
|
|
3
|
-
export async function providerRequest(url, { token, fetchImpl = globalThis.fetch, method = 'GET', body, attempts = 3 } = {}) {
|
|
3
|
+
export async function providerRequest(url, { token, fetchImpl = globalThis.fetch, method = 'GET', body, headers = {}, attempts = 3, rawResponse = false } = {}) {
|
|
4
4
|
if (!token) throw Object.assign(new Error('authorization is not configured'), { connectorState: 'not_configured' });
|
|
5
5
|
let response;
|
|
6
6
|
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
7
7
|
response = await fetchImpl(url, {
|
|
8
8
|
method,
|
|
9
|
-
headers: { authorization: `Bearer ${token}`, accept: 'application/json', ...(body ? { 'content-type': 'application/json' } : {}) },
|
|
9
|
+
headers: { authorization: `Bearer ${token}`, accept: 'application/json', ...(body ? { 'content-type': 'application/json' } : {}), ...headers },
|
|
10
10
|
body: body ? JSON.stringify(body) : undefined,
|
|
11
11
|
});
|
|
12
|
-
if (response.ok)
|
|
12
|
+
if (response.ok) {
|
|
13
|
+
if (rawResponse) return response;
|
|
14
|
+
return response.json();
|
|
15
|
+
}
|
|
13
16
|
const detail = (await response.text()).slice(0, 300);
|
|
14
17
|
if (!RETRYABLE.has(response.status) || attempt === attempts) {
|
|
15
18
|
const states = { 401: 'authorization_expired', 403: 'permission_denied', 429: 'quota_limited' };
|
package/src/connectors/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { gscConnector } from './gsc.js';
|
|
2
2
|
import { ga4Connector } from './ga4.js';
|
|
3
|
+
import { wordpressConnector } from './wordpress.js';
|
|
4
|
+
import { webflowConnector } from './webflow.js';
|
|
3
5
|
|
|
4
|
-
const CONNECTORS = new Map([gscConnector, ga4Connector].map((connector) => [connector.provider, connector]));
|
|
6
|
+
const CONNECTORS = new Map([gscConnector, ga4Connector, wordpressConnector, webflowConnector].map((connector) => [connector.provider, connector]));
|
|
5
7
|
|
|
6
8
|
export function getConnector(provider) {
|
|
7
9
|
const connector = CONNECTORS.get(String(provider).toLowerCase());
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { providerRequest } from './http.js';
|
|
2
|
+
import { sha256, nowIso } from '../shared/io.js';
|
|
3
|
+
|
|
4
|
+
const BASE = 'https://api.webflow.com/v2';
|
|
5
|
+
|
|
6
|
+
const METRICS = {
|
|
7
|
+
pages: { unit: 'count', value_type: 'integer' },
|
|
8
|
+
collections: { unit: 'count', value_type: 'integer' },
|
|
9
|
+
items: { unit: 'count', value_type: 'integer' },
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function computeWebflowPayloadHash(payload) {
|
|
13
|
+
const canonical = {
|
|
14
|
+
target_id: String(payload.target_id || payload.id),
|
|
15
|
+
site_id: String(payload.site_id || ''),
|
|
16
|
+
title: String(payload.title || ''),
|
|
17
|
+
slug: String(payload.slug || ''),
|
|
18
|
+
meta_title: String(payload.meta_title || ''),
|
|
19
|
+
meta_description: String(payload.meta_description || ''),
|
|
20
|
+
open_graph_title: String(payload.open_graph_title || ''),
|
|
21
|
+
open_graph_description: String(payload.open_graph_description || ''),
|
|
22
|
+
body: String(payload.body || ''),
|
|
23
|
+
};
|
|
24
|
+
return sha256(JSON.stringify(canonical));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const webflowConnector = {
|
|
28
|
+
provider: 'webflow',
|
|
29
|
+
defaultCredentialEnv: 'WEBFLOW_API_TOKEN',
|
|
30
|
+
readOnlyScopes: ['sites:read', 'pages:read', 'cms:read'],
|
|
31
|
+
writeScopes: ['pages:write', 'cms:write'],
|
|
32
|
+
|
|
33
|
+
describeMetrics() {
|
|
34
|
+
return METRICS;
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
async discoverProperties(context) {
|
|
38
|
+
const result = await providerRequest(`${BASE}/sites`, context);
|
|
39
|
+
const sites = result.sites || [];
|
|
40
|
+
return sites.map((site) => ({
|
|
41
|
+
property_id: site.id,
|
|
42
|
+
display_name: site.displayName || site.id,
|
|
43
|
+
custom_domains: (site.customDomains || []).map((d) => d.url || d.host || d),
|
|
44
|
+
time_zone: site.timeZone || null,
|
|
45
|
+
created_on: site.createdOn || null,
|
|
46
|
+
permission: 'administrator',
|
|
47
|
+
}));
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
async validateConnection(connection, context) {
|
|
51
|
+
const site = await providerRequest(`${BASE}/sites/${encodeURIComponent(connection.property_id)}`, context);
|
|
52
|
+
const valid = Boolean(site && site.id === connection.property_id);
|
|
53
|
+
return {
|
|
54
|
+
valid,
|
|
55
|
+
site_id: site?.id || connection.property_id,
|
|
56
|
+
display_name: site?.displayName || null,
|
|
57
|
+
last_published: site?.lastPublished || null,
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
async sync(connection, metrics, { startDate, endDate, ...context }) {
|
|
62
|
+
const siteId = connection.property_id;
|
|
63
|
+
const rows = [];
|
|
64
|
+
const metricNames = new Set(metrics.map((m) => m.external_name));
|
|
65
|
+
|
|
66
|
+
if (metricNames.has('pages')) {
|
|
67
|
+
const pageResult = await providerRequest(`${BASE}/sites/${encodeURIComponent(siteId)}/pages`, context);
|
|
68
|
+
const pages = pageResult.pages || [];
|
|
69
|
+
const pageMetric = metrics.find((m) => m.external_name === 'pages');
|
|
70
|
+
rows.push({
|
|
71
|
+
metric: pageMetric,
|
|
72
|
+
value: pages.length,
|
|
73
|
+
dimensions: { date: endDate },
|
|
74
|
+
observed_at: `${endDate}T00:00:00.000Z`,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (metricNames.has('collections') || metricNames.has('items')) {
|
|
79
|
+
const collectionsResult = await providerRequest(`${BASE}/sites/${encodeURIComponent(siteId)}/collections`, context);
|
|
80
|
+
const collections = collectionsResult.collections || [];
|
|
81
|
+
|
|
82
|
+
if (metricNames.has('collections')) {
|
|
83
|
+
const colMetric = metrics.find((m) => m.external_name === 'collections');
|
|
84
|
+
rows.push({
|
|
85
|
+
metric: colMetric,
|
|
86
|
+
value: collections.length,
|
|
87
|
+
dimensions: { date: endDate },
|
|
88
|
+
observed_at: `${endDate}T00:00:00.000Z`,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (metricNames.has('items')) {
|
|
93
|
+
let totalItems = 0;
|
|
94
|
+
for (const col of collections) {
|
|
95
|
+
const itemsResult = await providerRequest(`${BASE}/collections/${encodeURIComponent(col.id)}/items?limit=100`, context);
|
|
96
|
+
totalItems += (itemsResult.items || []).length;
|
|
97
|
+
}
|
|
98
|
+
const itemMetric = metrics.find((m) => m.external_name === 'items');
|
|
99
|
+
rows.push({
|
|
100
|
+
metric: itemMetric,
|
|
101
|
+
value: totalItems,
|
|
102
|
+
dimensions: { date: endDate },
|
|
103
|
+
observed_at: `${endDate}T00:00:00.000Z`,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
rows,
|
|
110
|
+
cursor: endDate,
|
|
111
|
+
limitations: [
|
|
112
|
+
'Webflow API enforces rate limits of 60 requests per minute.',
|
|
113
|
+
'Staged CMS changes require site publish to appear on live domains.',
|
|
114
|
+
],
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Read Webflow page or collection item content and SEO metadata with hash integrity.
|
|
120
|
+
*/
|
|
121
|
+
async readContent(connection, targetId, context) {
|
|
122
|
+
if (!targetId) throw new Error('Webflow readContent requires targetId');
|
|
123
|
+
const page = await providerRequest(`${BASE}/pages/${encodeURIComponent(targetId)}`, context);
|
|
124
|
+
|
|
125
|
+
const payload = {
|
|
126
|
+
target_id: String(page.id),
|
|
127
|
+
site_id: page.siteId || connection.property_id,
|
|
128
|
+
title: page.title || '',
|
|
129
|
+
slug: page.slug || '',
|
|
130
|
+
meta_title: page.seo?.title || page.title || '',
|
|
131
|
+
meta_description: page.seo?.description || '',
|
|
132
|
+
open_graph_title: page.openGraph?.title || null,
|
|
133
|
+
open_graph_description: page.openGraph?.description || null,
|
|
134
|
+
body: page.body || '',
|
|
135
|
+
draft: Boolean(page.draft),
|
|
136
|
+
archived: Boolean(page.archived),
|
|
137
|
+
published: Boolean(page.published),
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const contentHash = computeWebflowPayloadHash(payload);
|
|
141
|
+
return { ...payload, content_hash: contentHash };
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Apply reviewed, hash-locked remediation to a Webflow page.
|
|
146
|
+
*/
|
|
147
|
+
async applyRemediation(connection, remediation, context = {}) {
|
|
148
|
+
if (!remediation.reviewer) {
|
|
149
|
+
throw new Error('CMS remediation requires a named human reviewer per governance rules');
|
|
150
|
+
}
|
|
151
|
+
if (!remediation.expected_hash) {
|
|
152
|
+
throw new Error('CMS remediation requires expected_hash for hash-locking');
|
|
153
|
+
}
|
|
154
|
+
if (!remediation.target_id) {
|
|
155
|
+
throw new Error('CMS remediation requires target_id');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const current = await this.readContent(connection, remediation.target_id, context);
|
|
159
|
+
if (current.content_hash !== remediation.expected_hash) {
|
|
160
|
+
throw new Error(`stale remediation refused for Webflow ${remediation.target_id}: content hash changed (${current.content_hash} !== ${remediation.expected_hash})`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const updates = remediation.updates || {};
|
|
164
|
+
const candidatePayload = {
|
|
165
|
+
...current,
|
|
166
|
+
title: updates.title !== undefined ? updates.title : current.title,
|
|
167
|
+
slug: updates.slug !== undefined ? updates.slug : current.slug,
|
|
168
|
+
meta_title: updates.meta_title !== undefined ? updates.meta_title : current.meta_title,
|
|
169
|
+
meta_description: updates.meta_description !== undefined ? updates.meta_description : current.meta_description,
|
|
170
|
+
open_graph_title: updates.open_graph_title !== undefined ? updates.open_graph_title : current.open_graph_title,
|
|
171
|
+
open_graph_description: updates.open_graph_description !== undefined ? updates.open_graph_description : current.open_graph_description,
|
|
172
|
+
body: updates.body !== undefined ? updates.body : current.body,
|
|
173
|
+
};
|
|
174
|
+
const simulatedHash = computeWebflowPayloadHash(candidatePayload);
|
|
175
|
+
|
|
176
|
+
// Dry-run mode by default
|
|
177
|
+
if (!context.write) {
|
|
178
|
+
return {
|
|
179
|
+
status: 'proposed',
|
|
180
|
+
dry_run: true,
|
|
181
|
+
target_id: remediation.target_id,
|
|
182
|
+
before_hash: current.content_hash,
|
|
183
|
+
after_hash: simulatedHash,
|
|
184
|
+
updates,
|
|
185
|
+
reviewer: remediation.reviewer,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Live update via PATCH
|
|
190
|
+
const body = {};
|
|
191
|
+
if (updates.title !== undefined) body.title = updates.title;
|
|
192
|
+
if (updates.slug !== undefined) body.slug = updates.slug;
|
|
193
|
+
if (updates.body !== undefined) body.body = updates.body;
|
|
194
|
+
if (updates.meta_title !== undefined || updates.meta_description !== undefined) {
|
|
195
|
+
body.seo = {
|
|
196
|
+
title: updates.meta_title !== undefined ? updates.meta_title : current.meta_title,
|
|
197
|
+
description: updates.meta_description !== undefined ? updates.meta_description : current.meta_description,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
if (updates.open_graph_title !== undefined || updates.open_graph_description !== undefined) {
|
|
201
|
+
body.openGraph = {
|
|
202
|
+
title: updates.open_graph_title !== undefined ? updates.open_graph_title : current.open_graph_title,
|
|
203
|
+
description: updates.open_graph_description !== undefined ? updates.open_graph_description : current.open_graph_description,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
await providerRequest(`${BASE}/pages/${encodeURIComponent(remediation.target_id)}`, {
|
|
208
|
+
...context,
|
|
209
|
+
method: 'PATCH',
|
|
210
|
+
body,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const updated = await this.readContent(connection, remediation.target_id, context);
|
|
214
|
+
return {
|
|
215
|
+
status: 'applied',
|
|
216
|
+
dry_run: false,
|
|
217
|
+
target_id: remediation.target_id,
|
|
218
|
+
before_hash: current.content_hash,
|
|
219
|
+
after_hash: updated.content_hash,
|
|
220
|
+
updated_at: nowIso(),
|
|
221
|
+
reviewer: remediation.reviewer,
|
|
222
|
+
};
|
|
223
|
+
},
|
|
224
|
+
};
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { providerRequest } from './http.js';
|
|
2
|
+
import { sha256, nowIso } from '../shared/io.js';
|
|
3
|
+
|
|
4
|
+
const METRICS = {
|
|
5
|
+
published_posts: { unit: 'count', value_type: 'integer' },
|
|
6
|
+
published_pages: { unit: 'count', value_type: 'integer' },
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
function normalizeBaseUrl(propertyId) {
|
|
10
|
+
if (!propertyId) throw new Error('WordPress property_id must be the base site or API URL');
|
|
11
|
+
return String(propertyId).replace(/\/+$/, '');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function computeCmsPayloadHash(payload) {
|
|
15
|
+
const canonical = {
|
|
16
|
+
target_id: String(payload.target_id || payload.id),
|
|
17
|
+
title: String(payload.title || ''),
|
|
18
|
+
content: String(payload.content || ''),
|
|
19
|
+
excerpt: String(payload.excerpt || ''),
|
|
20
|
+
slug: String(payload.slug || ''),
|
|
21
|
+
status: String(payload.status || 'publish'),
|
|
22
|
+
meta: payload.meta || {},
|
|
23
|
+
schema_data: payload.schema_data || null,
|
|
24
|
+
};
|
|
25
|
+
return sha256(JSON.stringify(canonical));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const wordpressConnector = {
|
|
29
|
+
provider: 'wordpress',
|
|
30
|
+
defaultCredentialEnv: 'WORDPRESS_AUTH_TOKEN',
|
|
31
|
+
readOnlyScopes: [
|
|
32
|
+
'https://api.wordpress.org/auth/posts.readonly',
|
|
33
|
+
'https://api.wordpress.org/auth/pages.readonly',
|
|
34
|
+
],
|
|
35
|
+
writeScopes: [
|
|
36
|
+
'https://api.wordpress.org/auth/posts.edit',
|
|
37
|
+
'https://api.wordpress.org/auth/pages.edit',
|
|
38
|
+
],
|
|
39
|
+
|
|
40
|
+
describeMetrics() {
|
|
41
|
+
return METRICS;
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
async discoverProperties(context) {
|
|
45
|
+
const targetUrl = context.property_id || context.siteUrl || context.url;
|
|
46
|
+
if (!targetUrl) {
|
|
47
|
+
return [{
|
|
48
|
+
property_id: 'https://example.test',
|
|
49
|
+
display_name: 'WordPress Site (configure with site URL)',
|
|
50
|
+
permission: 'administrator',
|
|
51
|
+
}];
|
|
52
|
+
}
|
|
53
|
+
const base = normalizeBaseUrl(targetUrl);
|
|
54
|
+
const siteInfo = await providerRequest(`${base}/wp-json`, context);
|
|
55
|
+
return [{
|
|
56
|
+
property_id: siteInfo.url || base,
|
|
57
|
+
display_name: siteInfo.name || base,
|
|
58
|
+
description: siteInfo.description || null,
|
|
59
|
+
home: siteInfo.home || null,
|
|
60
|
+
permission: 'administrator',
|
|
61
|
+
}];
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
async validateConnection(connection, context) {
|
|
65
|
+
const base = normalizeBaseUrl(connection.property_id);
|
|
66
|
+
const user = await providerRequest(`${base}/wp-json/wp/v2/users/me?context=edit`, context);
|
|
67
|
+
const valid = Boolean(user && (user.id || user.name));
|
|
68
|
+
return {
|
|
69
|
+
valid,
|
|
70
|
+
user: user?.name || null,
|
|
71
|
+
roles: user?.roles || [],
|
|
72
|
+
capabilities: user?.capabilities || {},
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
async sync(connection, metrics, { startDate, endDate, ...context }) {
|
|
77
|
+
const base = normalizeBaseUrl(connection.property_id);
|
|
78
|
+
const rows = [];
|
|
79
|
+
const metricNames = new Set(metrics.map((m) => m.external_name));
|
|
80
|
+
|
|
81
|
+
if (metricNames.has('published_posts')) {
|
|
82
|
+
const posts = await providerRequest(`${base}/wp-json/wp/v2/posts?per_page=100&status=publish`, context);
|
|
83
|
+
const postMetric = metrics.find((m) => m.external_name === 'published_posts');
|
|
84
|
+
rows.push({
|
|
85
|
+
metric: postMetric,
|
|
86
|
+
value: Array.isArray(posts) ? posts.length : 0,
|
|
87
|
+
dimensions: { date: endDate },
|
|
88
|
+
observed_at: `${endDate}T00:00:00.000Z`,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (metricNames.has('published_pages')) {
|
|
93
|
+
const pages = await providerRequest(`${base}/wp-json/wp/v2/pages?per_page=100&status=publish`, context);
|
|
94
|
+
const pageMetric = metrics.find((m) => m.external_name === 'published_pages');
|
|
95
|
+
rows.push({
|
|
96
|
+
metric: pageMetric,
|
|
97
|
+
value: Array.isArray(pages) ? pages.length : 0,
|
|
98
|
+
dimensions: { date: endDate },
|
|
99
|
+
observed_at: `${endDate}T00:00:00.000Z`,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
rows,
|
|
105
|
+
cursor: endDate,
|
|
106
|
+
limitations: [
|
|
107
|
+
'WordPress REST API collection is bounded to accessible posts and pages.',
|
|
108
|
+
'Custom post types outside standard posts and pages require dedicated endpoint parameters.',
|
|
109
|
+
],
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Read CMS page/post content, metadata, and structured data with hash integrity.
|
|
115
|
+
*/
|
|
116
|
+
async readContent(connection, targetId, context) {
|
|
117
|
+
if (!targetId) throw new Error('WordPress readContent requires targetId');
|
|
118
|
+
const base = normalizeBaseUrl(connection.property_id);
|
|
119
|
+
let item;
|
|
120
|
+
try {
|
|
121
|
+
item = await providerRequest(`${base}/wp-json/wp/v2/pages/${encodeURIComponent(targetId)}?context=edit`, context);
|
|
122
|
+
} catch {
|
|
123
|
+
// Fall back to posts if not a page
|
|
124
|
+
item = await providerRequest(`${base}/wp-json/wp/v2/posts/${encodeURIComponent(targetId)}?context=edit`, context);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const payload = {
|
|
128
|
+
target_id: String(item.id),
|
|
129
|
+
type: item.type || 'page',
|
|
130
|
+
url: item.link || '',
|
|
131
|
+
slug: item.slug || '',
|
|
132
|
+
status: item.status || 'publish',
|
|
133
|
+
title: item.title?.raw ?? item.title?.rendered ?? '',
|
|
134
|
+
content: item.content?.raw ?? item.content?.rendered ?? '',
|
|
135
|
+
excerpt: item.excerpt?.raw ?? item.excerpt?.rendered ?? '',
|
|
136
|
+
meta: item.meta || {},
|
|
137
|
+
schema_data: item.meta?.schema_data || null,
|
|
138
|
+
yoast_title: item.meta?._yoast_wpseo_title || null,
|
|
139
|
+
yoast_metadesc: item.meta?._yoast_wpseo_metadesc || null,
|
|
140
|
+
modified_gmt: item.modified_gmt || null,
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const contentHash = computeCmsPayloadHash(payload);
|
|
144
|
+
return { ...payload, content_hash: contentHash };
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Apply reviewed, hash-locked remediation to WordPress page or post.
|
|
149
|
+
*/
|
|
150
|
+
async applyRemediation(connection, remediation, context = {}) {
|
|
151
|
+
if (!remediation.reviewer) {
|
|
152
|
+
throw new Error('CMS remediation requires a named human reviewer per governance rules');
|
|
153
|
+
}
|
|
154
|
+
if (!remediation.expected_hash) {
|
|
155
|
+
throw new Error('CMS remediation requires expected_hash for hash-locking');
|
|
156
|
+
}
|
|
157
|
+
if (!remediation.target_id) {
|
|
158
|
+
throw new Error('CMS remediation requires target_id');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const current = await this.readContent(connection, remediation.target_id, context);
|
|
162
|
+
if (current.content_hash !== remediation.expected_hash) {
|
|
163
|
+
throw new Error(`stale remediation refused for WordPress ${remediation.target_id}: content hash changed (${current.content_hash} !== ${remediation.expected_hash})`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const updates = remediation.updates || {};
|
|
167
|
+
const candidatePayload = {
|
|
168
|
+
...current,
|
|
169
|
+
title: updates.title !== undefined ? updates.title : current.title,
|
|
170
|
+
content: updates.content !== undefined ? updates.content : current.content,
|
|
171
|
+
excerpt: updates.excerpt !== undefined ? updates.excerpt : current.excerpt,
|
|
172
|
+
slug: updates.slug !== undefined ? updates.slug : current.slug,
|
|
173
|
+
status: updates.status !== undefined ? updates.status : current.status,
|
|
174
|
+
meta: updates.meta !== undefined ? updates.meta : current.meta,
|
|
175
|
+
schema_data: updates.schema_data !== undefined ? updates.schema_data : current.schema_data,
|
|
176
|
+
};
|
|
177
|
+
const simulatedHash = computeCmsPayloadHash(candidatePayload);
|
|
178
|
+
|
|
179
|
+
// Dry-run mode by default
|
|
180
|
+
if (!context.write) {
|
|
181
|
+
return {
|
|
182
|
+
status: 'proposed',
|
|
183
|
+
dry_run: true,
|
|
184
|
+
target_id: remediation.target_id,
|
|
185
|
+
before_hash: current.content_hash,
|
|
186
|
+
after_hash: simulatedHash,
|
|
187
|
+
updates,
|
|
188
|
+
reviewer: remediation.reviewer,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Live update
|
|
193
|
+
const base = normalizeBaseUrl(connection.property_id);
|
|
194
|
+
const endpoint = current.type === 'post' ? 'posts' : 'pages';
|
|
195
|
+
const body = {};
|
|
196
|
+
if (updates.title !== undefined) body.title = updates.title;
|
|
197
|
+
if (updates.content !== undefined) body.content = updates.content;
|
|
198
|
+
if (updates.excerpt !== undefined) body.excerpt = updates.excerpt;
|
|
199
|
+
if (updates.slug !== undefined) body.slug = updates.slug;
|
|
200
|
+
if (updates.status !== undefined) body.status = updates.status;
|
|
201
|
+
if (updates.meta !== undefined || updates.schema_data !== undefined) {
|
|
202
|
+
body.meta = {
|
|
203
|
+
...(current.meta || {}),
|
|
204
|
+
...(updates.meta || {}),
|
|
205
|
+
...(updates.schema_data !== undefined ? { schema_data: updates.schema_data } : {}),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
await providerRequest(`${base}/wp-json/wp/v2/${endpoint}/${encodeURIComponent(remediation.target_id)}`, {
|
|
210
|
+
...context,
|
|
211
|
+
method: 'POST',
|
|
212
|
+
body,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const updated = await this.readContent(connection, remediation.target_id, context);
|
|
216
|
+
return {
|
|
217
|
+
status: 'applied',
|
|
218
|
+
dry_run: false,
|
|
219
|
+
target_id: remediation.target_id,
|
|
220
|
+
before_hash: current.content_hash,
|
|
221
|
+
after_hash: updated.content_hash,
|
|
222
|
+
updated_at: nowIso(),
|
|
223
|
+
reviewer: remediation.reviewer,
|
|
224
|
+
};
|
|
225
|
+
},
|
|
226
|
+
};
|