agentlinkops 0.6.11 → 0.6.16

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.
Files changed (311) hide show
  1. package/.claude-plugin/plugin.json +2 -2
  2. package/.codex-plugin/plugin.json +2 -2
  3. package/.mcp.json +0 -0
  4. package/CHANGELOG.md +35 -0
  5. package/LICENSE +0 -0
  6. package/NOTICE +0 -0
  7. package/PACK-ARTIFACT.json +16 -0
  8. package/PACKS.json +591 -0
  9. package/README.md +34 -48
  10. package/bin/agentlinkops.mjs +0 -0
  11. package/bin/linktrail.mjs +0 -0
  12. package/cli/adapters/csv.js +0 -0
  13. package/cli/adapters/sqlite-crm.js +0 -0
  14. package/cli/adapters/suppliers.js +1 -1
  15. package/cli/admission.js +0 -0
  16. package/cli/agent-setup.js +101 -17
  17. package/cli/agentlinkops.mjs +0 -0
  18. package/cli/args.js +0 -0
  19. package/cli/campaign-bridge.js +325 -0
  20. package/cli/campaign-outcomes.js +107 -0
  21. package/cli/catalog.js +47686 -16398
  22. package/cli/check.js +40 -10
  23. package/cli/chronology.js +39 -0
  24. package/cli/citation.js +0 -0
  25. package/cli/client.js +2 -1
  26. package/cli/cloud-error.js +0 -0
  27. package/cli/commands.js +17 -8
  28. package/cli/config.js +0 -0
  29. package/cli/connection.js +92 -18
  30. package/cli/contact-adoption.js +91 -0
  31. package/cli/context-mcp.js +0 -0
  32. package/cli/context.js +13 -2
  33. package/cli/delivery.js +0 -0
  34. package/cli/disavow.js +0 -0
  35. package/cli/doctor.js +15 -4
  36. package/cli/env.js +0 -0
  37. package/cli/fleet.js +30 -19
  38. package/cli/gsc-browser.js +0 -0
  39. package/cli/import.js +7 -1
  40. package/cli/index-observations.js +0 -0
  41. package/cli/index-submissions.js +0 -0
  42. package/cli/key-store.js +115 -0
  43. package/cli/ledger.js +1 -0
  44. package/cli/lifecycle.js +0 -0
  45. package/cli/linktrail.mjs +0 -0
  46. package/cli/local-result.js +41 -6
  47. package/cli/main.js +85 -20
  48. package/cli/mcp-client.js +2 -2
  49. package/cli/migration.js +308 -0
  50. package/cli/mirror.js +0 -0
  51. package/cli/mix.js +0 -0
  52. package/cli/native-lock.js +0 -0
  53. package/cli/oauth-credentials.js +0 -0
  54. package/cli/oauth-flow.js +0 -0
  55. package/cli/placement-projection.js +106 -0
  56. package/cli/platforms.js +0 -0
  57. package/cli/prompt.js +25 -0
  58. package/cli/public-http.js +168 -0
  59. package/cli/receipt-performance.js +0 -0
  60. package/cli/receipts.js +0 -0
  61. package/cli/report.js +51 -19
  62. package/cli/setup-plan.js +0 -0
  63. package/cli/skill.js +0 -0
  64. package/cli/state.js +76 -29
  65. package/cli/status.js +14 -18
  66. package/cli/sync-lock.js +0 -0
  67. package/cli/sync.js +23 -4
  68. package/package.json +7 -8
  69. package/references/citation-panels.md +2 -0
  70. package/references/cli.md +2 -2
  71. package/references/disavow.md +1 -1
  72. package/references/discovery-handoff.md +1 -1
  73. package/references/local-crm-draft-handoff.md +1 -1
  74. package/references/portable-ledger-handoff.md +11 -10
  75. package/references/recipes/catalog.json +263 -7
  76. package/references/recipes/command-notes.md +5 -3
  77. package/references/recipes/internal-link-improvement.md +25 -0
  78. package/references/recipes/placement-reconciliation.md +53 -1
  79. package/references/recipes/qualified-campaign-handoff.md +0 -0
  80. package/references/recipes/site-context-brief.md +0 -0
  81. package/references/recipes/sourced-linkable-asset.md +0 -0
  82. package/references/reports.md +0 -0
  83. package/scripts/agentlinkops.py +0 -0
  84. package/scripts/disavow.py +0 -0
  85. package/scripts/linktrail.py +0 -0
  86. package/scripts/migrations/001_crm.sql +0 -0
  87. package/scripts/migrations/002_cloud_events.sql +0 -0
  88. package/scripts/migrations/003_disavow.sql +0 -0
  89. package/scripts/migrations/004_placement_costs.sql +0 -0
  90. package/scripts/outreach-crm-draft.py +0 -0
  91. package/scripts/portable-handoff.py +17 -4
  92. package/scripts/report.py +0 -0
  93. package/shared/admission-contract.js +3 -2
  94. package/shared/admission-report-contract.js +3 -2
  95. package/shared/agent-clients.js +0 -0
  96. package/shared/analytics-history-contract.js +15 -0
  97. package/shared/campaign-outcome-contract.js +12 -0
  98. package/shared/campaign-prospect-contract.js +23 -0
  99. package/shared/citation-evidence-contract.js +0 -0
  100. package/shared/classification-contract.js +54 -0
  101. package/shared/contact-record-contract.js +27 -0
  102. package/shared/disavow-contract.js +37 -9
  103. package/shared/exec-grammar.js +0 -0
  104. package/shared/index-observation-contract.js +5 -4
  105. package/shared/internal-links-contract.js +143 -0
  106. package/shared/lifecycle-contract.js +0 -0
  107. package/shared/lifecycle-money.js +0 -0
  108. package/shared/link-category-contract.js +35 -0
  109. package/shared/link-category.js +138 -0
  110. package/shared/link-labels.js +26 -0
  111. package/shared/link-summary-contract.js +17 -0
  112. package/shared/monitoring-chronology-export.js +57 -0
  113. package/shared/monitoring-read-contract.js +41 -0
  114. package/shared/observation-event.js +1 -1
  115. package/shared/onboarding.js +20 -0
  116. package/shared/opportunity-record.js +0 -0
  117. package/shared/response-contract.js +41 -5
  118. package/shared/search-connection-contract.js +2 -1
  119. package/shared/table-page-contract.js +13 -0
  120. package/skills/agentlinkops-assets/SKILL-DEPS.json +290 -0
  121. package/skills/agentlinkops-assets/SKILL.md +3 -1
  122. package/skills/agentlinkops-assets/agents/openai.yaml +1 -1
  123. package/skills/agentlinkops-assets/references/portable/references/cli.md +220 -0
  124. package/skills/agentlinkops-assets/references/portable/references/disavow.md +73 -0
  125. package/skills/agentlinkops-assets/references/portable/references/reports.md +53 -0
  126. package/skills/agentlinkops-assets/references/portable/scripts/agentlinkops.py +676 -0
  127. package/skills/agentlinkops-assets/references/portable/scripts/disavow.py +207 -0
  128. package/skills/agentlinkops-assets/references/portable/scripts/migrations/001_crm.sql +45 -0
  129. package/skills/agentlinkops-assets/references/portable/scripts/migrations/002_cloud_events.sql +13 -0
  130. package/skills/agentlinkops-assets/references/portable/scripts/migrations/003_disavow.sql +20 -0
  131. package/skills/agentlinkops-assets/references/portable/scripts/migrations/004_placement_costs.sql +2 -0
  132. package/skills/agentlinkops-assets/references/portable/scripts/report.py +113 -0
  133. package/skills/agentlinkops-assets/references/portable/templates/campaigns.v1.json +65 -0
  134. package/skills/agentlinkops-campaigns/SKILL-DEPS.json +336 -0
  135. package/skills/agentlinkops-campaigns/SKILL.md +7 -5
  136. package/skills/agentlinkops-campaigns/agents/openai.yaml +1 -1
  137. package/skills/agentlinkops-campaigns/references/first-campaign.md +31 -0
  138. package/skills/agentlinkops-campaigns/references/mailbox-campaign.md +46 -0
  139. package/skills/agentlinkops-campaigns/references/portable/references/cli.md +220 -0
  140. package/skills/agentlinkops-campaigns/references/portable/references/disavow.md +73 -0
  141. package/skills/agentlinkops-campaigns/references/portable/references/reports.md +53 -0
  142. package/skills/agentlinkops-campaigns/references/portable/scripts/agentlinkops.py +676 -0
  143. package/skills/agentlinkops-campaigns/references/portable/scripts/disavow.py +207 -0
  144. package/skills/agentlinkops-campaigns/references/portable/scripts/migrations/001_crm.sql +45 -0
  145. package/skills/agentlinkops-campaigns/references/portable/scripts/migrations/002_cloud_events.sql +13 -0
  146. package/skills/agentlinkops-campaigns/references/portable/scripts/migrations/003_disavow.sql +20 -0
  147. package/skills/agentlinkops-campaigns/references/portable/scripts/migrations/004_placement_costs.sql +2 -0
  148. package/skills/agentlinkops-campaigns/references/portable/scripts/report.py +113 -0
  149. package/skills/agentlinkops-campaigns/references/portable/templates/campaigns.v1.json +65 -0
  150. package/skills/agentlinkops-connect/SKILL-DEPS.json +104 -0
  151. package/skills/agentlinkops-connect/SKILL.md +2 -0
  152. package/skills/agentlinkops-connect/agents/openai.yaml +1 -1
  153. package/skills/agentlinkops-connect/references/agentlinkops.md +164 -61
  154. package/skills/agentlinkops-connect/references/placement-workflows.md +19 -0
  155. package/skills/agentlinkops-crm/SKILL-DEPS.json +381 -0
  156. package/skills/agentlinkops-crm/SKILL.md +26 -9
  157. package/skills/agentlinkops-crm/agents/openai.yaml +1 -1
  158. package/skills/agentlinkops-crm/references/portable/references/cli.md +220 -0
  159. package/skills/agentlinkops-crm/references/portable/references/disavow.md +73 -0
  160. package/skills/agentlinkops-crm/references/portable/references/local-crm-draft-handoff.md +76 -0
  161. package/skills/agentlinkops-crm/references/portable/references/portable-ledger-handoff.md +51 -0
  162. package/skills/agentlinkops-crm/references/portable/references/reports.md +53 -0
  163. package/skills/agentlinkops-crm/references/portable/scripts/agentlinkops.py +676 -0
  164. package/skills/agentlinkops-crm/references/portable/scripts/disavow.py +207 -0
  165. package/skills/agentlinkops-crm/references/portable/scripts/migrations/001_crm.sql +45 -0
  166. package/skills/agentlinkops-crm/references/portable/scripts/migrations/002_cloud_events.sql +13 -0
  167. package/skills/agentlinkops-crm/references/portable/scripts/migrations/003_disavow.sql +20 -0
  168. package/skills/agentlinkops-crm/references/portable/scripts/migrations/004_placement_costs.sql +2 -0
  169. package/skills/agentlinkops-crm/references/portable/scripts/outreach-crm-draft.py +121 -0
  170. package/skills/agentlinkops-crm/references/portable/scripts/portable-handoff.py +197 -0
  171. package/skills/agentlinkops-crm/references/portable/scripts/report.py +113 -0
  172. package/skills/agentlinkops-crm/references/portable/templates/campaigns.v1.json +65 -0
  173. package/skills/agentlinkops-discovery/SKILL-DEPS.json +301 -0
  174. package/skills/agentlinkops-discovery/SKILL.md +4 -2
  175. package/skills/agentlinkops-discovery/references/portable/references/cli.md +220 -0
  176. package/skills/agentlinkops-discovery/references/portable/references/disavow.md +73 -0
  177. package/skills/agentlinkops-discovery/references/portable/references/discovery-handoff.md +32 -0
  178. package/skills/agentlinkops-discovery/references/portable/references/reports.md +53 -0
  179. package/skills/agentlinkops-discovery/references/portable/scripts/agentlinkops.py +676 -0
  180. package/skills/agentlinkops-discovery/references/portable/scripts/disavow.py +207 -0
  181. package/skills/agentlinkops-discovery/references/portable/scripts/migrations/001_crm.sql +45 -0
  182. package/skills/agentlinkops-discovery/references/portable/scripts/migrations/002_cloud_events.sql +13 -0
  183. package/skills/agentlinkops-discovery/references/portable/scripts/migrations/003_disavow.sql +20 -0
  184. package/skills/agentlinkops-discovery/references/portable/scripts/migrations/004_placement_costs.sql +2 -0
  185. package/skills/agentlinkops-discovery/references/portable/scripts/report.py +113 -0
  186. package/skills/agentlinkops-discovery/references/portable/templates/campaigns.v1.json +65 -0
  187. package/skills/agentlinkops-discovery/references/saved-backlink-workflows.md +23 -0
  188. package/src/citations/adapter.js +0 -0
  189. package/src/citations/alert.js +0 -0
  190. package/src/citations/browser/chatgpt-citations.js +0 -0
  191. package/src/citations/browser/engine.js +0 -0
  192. package/src/citations/browser/gen/core/providers/_shared/rawSourcesDom.js +0 -0
  193. package/src/citations/browser/gen/core/providers/_shared/resetProviderPage.js +0 -0
  194. package/src/citations/browser/gen/core/providers/_shared/sourceUtils.js +0 -0
  195. package/src/citations/browser/gen/core/providers/ai-overview/index.js +0 -0
  196. package/src/citations/browser/gen/core/providers/ai-overview/lib/expand.js +0 -0
  197. package/src/citations/browser/gen/core/providers/ai-overview/lib/extractSources.js +0 -0
  198. package/src/citations/browser/gen/core/providers/ai-overview/lib/pageLifecycle.js +0 -0
  199. package/src/citations/browser/gen/core/providers/ai-overview/lib/session.js +0 -0
  200. package/src/citations/browser/gen/core/providers/chatgpt/index.js +0 -0
  201. package/src/citations/browser/gen/core/providers/chatgpt/lib/dismissAuthModal.js +0 -0
  202. package/src/citations/browser/gen/core/providers/chatgpt/lib/extractSources.js +0 -0
  203. package/src/citations/browser/gen/core/providers/chatgpt/lib/pageLifecycle.js +0 -0
  204. package/src/citations/browser/gen/core/providers/claude/index.js +0 -0
  205. package/src/citations/browser/gen/core/providers/claude/lib/extractSources.js +0 -0
  206. package/src/citations/browser/gen/core/providers/claude/lib/pageLifecycle.js +0 -0
  207. package/src/citations/browser/gen/core/providers/gemini/index.js +0 -0
  208. package/src/citations/browser/gen/core/providers/gemini/lib/extractSources.js +0 -0
  209. package/src/citations/browser/gen/core/providers/gemini/lib/pageLifecycle.js +0 -0
  210. package/src/citations/browser/gen/core/providers/gemini/lib/session.js +0 -0
  211. package/src/citations/browser/gen/core/providers/index.js +0 -0
  212. package/src/citations/browser/gen/core/providers/perplexity/index.js +0 -0
  213. package/src/citations/browser/gen/core/providers/perplexity/lib/extractSources.js +0 -0
  214. package/src/citations/browser/gen/core/providers/perplexity/lib/navigation.js +0 -0
  215. package/src/citations/browser/gen/lib/browser/camoufox.js +0 -0
  216. package/src/citations/browser/gen/lib/browser/display.js +0 -0
  217. package/src/citations/browser/gen/lib/browser/domOps.js +0 -0
  218. package/src/citations/browser/gen/lib/browser/humanBehavior.js +0 -0
  219. package/src/citations/browser/gen/lib/browser/navigate.js +0 -0
  220. package/src/citations/browser/gen/lib/extraction/sourceUtils.js +0 -0
  221. package/src/citations/browser/gen/lib/input/editor/clearInput.js +0 -0
  222. package/src/citations/browser/gen/lib/input/editor/findEditor.js +0 -0
  223. package/src/citations/browser/gen/lib/input/editor/promptInput.js +0 -0
  224. package/src/citations/browser/gen/lib/input/markdown/converter.js +0 -0
  225. package/src/citations/browser/gen/lib/input/markdown/toMarkdown.js +0 -0
  226. package/src/citations/browser/gen/lib/input/response/detectBotPage.js +0 -0
  227. package/src/citations/browser/gen/lib/input/response/isGenerating.js +0 -0
  228. package/src/citations/browser/gen/lib/input/response/waitForFinish.js +0 -0
  229. package/src/citations/browser/gen/lib/input/sources/findButton.js +0 -0
  230. package/src/citations/browser/gen/lib/input/sources/openPanel.js +0 -0
  231. package/src/citations/browser/gen/utils/agent-constants.js +0 -0
  232. package/src/citations/browser/providers.js +0 -0
  233. package/src/citations/browser/proxy-admission.js +0 -0
  234. package/src/citations/browser/shims/errors.js +0 -0
  235. package/src/citations/browser/shims/types.js +0 -0
  236. package/src/citations/browser/shims/utils.js +0 -0
  237. package/src/citations/contract.js +0 -0
  238. package/src/citations/egress-receipts.js +0 -0
  239. package/src/citations/evidence-integrity.js +0 -0
  240. package/src/citations/inventory.js +0 -0
  241. package/src/citations/login.js +0 -0
  242. package/src/citations/match.js +0 -0
  243. package/src/citations/panel-builder.js +0 -0
  244. package/src/citations/report.js +0 -0
  245. package/src/citations/runner.js +0 -0
  246. package/src/citations/stats.js +0 -0
  247. package/src/citations/sync.js +0 -0
  248. package/src/citations/watch.js +0 -0
  249. package/src/competitors/contract.js +17 -1
  250. package/src/competitors/domain-mix.js +0 -0
  251. package/src/context/ga4.js +0 -0
  252. package/src/context/gsc.js +0 -0
  253. package/src/context/site-profile.js +34 -121
  254. package/src/context/util.js +0 -0
  255. package/src/context-tool-registry.js +0 -0
  256. package/src/corpus/acquire.js +68 -32
  257. package/src/corpus/extract.js +147 -0
  258. package/src/corpus/frontier.js +151 -0
  259. package/src/corpus/seeds.js +0 -0
  260. package/src/discovery/contract.js +8 -0
  261. package/src/discovery/import.js +13 -1
  262. package/src/evidence-reference.js +0 -0
  263. package/src/index-observation.js +0 -0
  264. package/src/locator.js +0 -0
  265. package/src/mentions.js +193 -0
  266. package/src/page-acquisition/acquire.js +50 -0
  267. package/src/page-acquisition/bytes.js +138 -0
  268. package/src/page-acquisition/capture.js +11 -0
  269. package/src/page-acquisition/contracts.js +67 -0
  270. package/src/page-acquisition/documents.js +166 -0
  271. package/src/page-acquisition/http.js +307 -0
  272. package/src/page-acquisition/policy.js +20 -0
  273. package/src/page-acquisition/recovery.js +164 -0
  274. package/src/page-acquisition/render-failure.js +82 -0
  275. package/src/page-extraction/html.js +359 -0
  276. package/src/page-extraction/index.js +30 -0
  277. package/src/page-extraction/metadata.js +16 -0
  278. package/src/response-shaping.js +4 -3
  279. package/src/tool-views.js +23 -12
  280. package/src/verifier/fetch.js +4 -339
  281. package/src/verifier/index.js +81 -23
  282. package/src/verifier/observation-kind.js +0 -0
  283. package/src/verifier/page-quality.js +14 -0
  284. package/src/verifier/parser.js +2 -241
  285. package/src/verifier/readiness.js +64 -13
  286. package/src/verifier/render-evidence.js +25 -2
  287. package/src/verifier/robots.js +0 -0
  288. package/src/verifier/target.js +201 -0
  289. package/src/verifier/transition.js +18 -3
  290. package/src/verifier/url.js +31 -0
  291. package/src/watch-crawlability.js +144 -0
  292. package/templates/SITE.template.md +0 -0
  293. package/templates/campaigns.v1.json +0 -0
  294. package/skills/content-defingerprinting/SKILL.md +0 -29
  295. package/skills/content-defingerprinting/scripts/watermarks/README.md +0 -102
  296. package/skills/content-defingerprinting/scripts/watermarks/UPSTREAM.json +0 -26
  297. package/skills/content-defingerprinting/scripts/watermarks/revendor.sh +0 -52
  298. package/skills/content-defingerprinting/scripts/watermarks/vendor/LICENSE.upstream +0 -21
  299. package/skills/content-defingerprinting/scripts/watermarks/vendor/clean_text.py +0 -96
  300. package/skills/content-defingerprinting/scripts/watermarks/vendor/common.py +0 -432
  301. package/skills/content-defingerprinting/scripts/watermarks/vendor/inspect_text.py +0 -86
  302. package/skills/content-defingerprinting/scripts/watermarks/vendor/score_stylometry.py +0 -460
  303. package/skills/content-defingerprinting/scripts/watermarks/vendor/text_unicode.py +0 -651
  304. package/skills/content-defingerprinting/scripts/watermarks/verify-vendor.py +0 -27
  305. package/skills/content-defingerprinting/scripts/watermarks/wm-scan.py +0 -212
  306. package/skills/internal-linking-optimizer/SKILL.md +0 -56
  307. package/skills/internal-linking-optimizer/references/source-prompt.md +0 -76
  308. package/skills/pre-publish-review/SKILL.md +0 -179
  309. package/skills/source-cited-content-builder/SKILL.md +0 -56
  310. package/skills/source-cited-content-builder/references/source-prompt.md +0 -19
  311. package/skills/source-cited-humanizer/SKILL.md +0 -20
@@ -0,0 +1,220 @@
1
+ # Local CRM contract
2
+
3
+ Version 0.2.0; SQLite schema version 4. Python 3.10+ is the only prerequisite.
4
+ Resolve example helper paths from the plugin root for shared references, or from
5
+ the skill directory for a standalone archive's bundled references. Choose a database
6
+ path in the user's working directory, outside the replaceable plugin cache.
7
+
8
+ ```sh
9
+ python3 references/portable/scripts/agentlinkops.py --db ./agentlinkops.sqlite init --workspace local-example
10
+ python3 references/portable/scripts/agentlinkops.py --db ./agentlinkops.sqlite status
11
+ python3 references/portable/scripts/agentlinkops.py templates
12
+ python3 references/portable/scripts/agentlinkops.py --db ./agentlinkops.sqlite product upsert --file product.json
13
+ python3 references/portable/scripts/agentlinkops.py --db ./agentlinkops.sqlite opportunity list
14
+ python3 references/portable/scripts/agentlinkops.py --db ./agentlinkops.sqlite export --format json --output crm-export.json
15
+ python3 references/portable/scripts/agentlinkops.py --db ./agentlinkops.sqlite export --format csv --entity placement --output placements.csv
16
+ ```
17
+
18
+ `init` binds this database to exactly one workspace ID. Use an actual cloud
19
+ workspace ID if cloud sync is planned. For a purely offline CRM, choose a
20
+ stable local name. Workspace and cloud-origin binding cannot be silently
21
+ changed. A separate local database is required for a different workspace.
22
+
23
+ `migrate` creates a SQLite backup before applying pending migrations. It
24
+ refuses unrelated databases, newer schema versions and changed migration
25
+ checksums. Initialization is idempotent for the same workspace. The CLI has
26
+ no destructive reset command. SQLite is not encrypted; rely on the user's
27
+ device security and store the file in an appropriate location.
28
+
29
+ `upsert` reads one object or an atomic array of objects from a file or stdin
30
+ (`--file -`, the default). IDs are explicit, stable strings; updating an
31
+ existing record changes only supplied fields. References must already exist;
32
+ insert product, campaign/contact, opportunity, then activity/placement.
33
+
34
+ | Entity | Required for a new record, plus `id` | Optional editable fields |
35
+ |---|---|---|
36
+ | product | `name`, `url` | `audience`, `description`, `target_pages` (URL array), `notes` |
37
+ | campaign | `name`, `type` | `product_id`, `template_version`, `status`, `goal`, `target_url`, `notes` |
38
+ | contact | `publisher_url` | `name`, `email`, `contact_url`, `source_url`, `observed_at`, `confidence`, `notes` |
39
+ | opportunity | `source_url`, `target_url` | `campaign_id`, `contact_id`, `type`, `status`, `evidence` (object), `notes` |
40
+ | activity | `kind`, `occurred_at` | `opportunity_id`, `contact_id`, `campaign_id`, `channel`, `external_message_id`, `outcome`, `notes` |
41
+ | placement | `source_url`, `target_url` | `opportunity_id`, `project_id`, `watch_id`, `status`, `notes`, `cost_amount`, `cost_currency` |
42
+
43
+ Timestamps require ISO 8601 with a timezone. Link fields require HTTP(S) URLs.
44
+ Null clears an optional relationship ID. Passwords, API keys, tokens,
45
+ mailbox credentials and email/message body fields are rejected. Never put
46
+ those values into free-text notes either. Contact confidence describes the
47
+ evidence, not a promise of email delivery.
48
+
49
+ Example `product.json`:
50
+
51
+ ```json
52
+ {"id":"product-example","name":"Example Resource","url":"https://example.com/","audience":"Resource-page readers","target_pages":["https://example.com/guide"]}
53
+ ```
54
+
55
+ Example opportunity evidence:
56
+
57
+ ```json
58
+ {"id":"opportunity-example","source_url":"https://publisher.example/resources","target_url":"https://example.com/guide","type":"resource_addition","evidence":{"source_url":"https://publisher.example/resources","observed_at":"2026-09-09T12:00:00Z","context":"Relevant resources section; candidate requires browser review."}}
59
+ ```
60
+
61
+ Use `entity list --id ID` to retrieve a specific record. JSON exports preserve
62
+ all fields, raw events, the sync cursor, disavow rules and their source provenance. They are portable data exports,
63
+ not an automatic full-database restore format. CSV exports select one entity
64
+ and prefix possible spreadsheet formulas with `'`; JSON is lossless.
65
+ Export files are created with restrictive permissions and never overwritten.
66
+ For a restorable database backup, use SQLite's backup API or the SQLite CLI
67
+ `.backup` command; do not copy a live database file while a write is running.
68
+
69
+ ## Disavow lists and placement reports
70
+
71
+ For user-selected disavow files, read [the disavow contract](disavow.md).
72
+ The `disavow import/upsert/list/export` commands work offline, require an
73
+ explicit URL-prefix property and preserve source provenance. No command
74
+ uploads to Google or chooses rules from automated risk labels.
75
+
76
+ For branded HTML output, read [the report contract](reports.md). The `report`
77
+ command supports a project filter and optional local notes/cost fields. Notes
78
+ and costs are excluded unless explicitly included. Record a placement cost
79
+ as a decimal string paired with an uppercase currency label, for example
80
+ `"cost_amount":"12.50","cost_currency":"USD"`. Clear both fields with null.
81
+ The CLI validates decimal precision and reports each currency separately.
82
+ It does not verify currency labels against a current exchange-rate service.
83
+
84
+ Existing CRM databases require `migrate` before using version 0.2.0. Schema
85
+ versions 3 and 4 add disavow provenance tables and optional placement cost
86
+ fields. Existing placements, notes, event history and workspace bindings are
87
+ preserved; the helper creates a restorable database backup before upgrading.
88
+
89
+ ## Connect the ledger CLI to an existing project
90
+
91
+ Local ledger commands work without an account. Cloud sync uses a separate scoped
92
+ API key. MCP OAuth credentials stay in the MCP client and cannot authorize REST.
93
+
94
+ 1. Open the invited account's workspace at `https://app.agentlinkops.com/app`.
95
+ An owner or admin selects **Agent access > Create API key**. Choose the
96
+ existing project and the scopes `projects:read`, `watches:read`,
97
+ `watches:write`, `events:read`, and `exports:create`.
98
+ 2. Save the once-shown credential in the user's secret store. Supply it through
99
+ `AGENTLINKOPS_API_KEY` in the CLI process environment. The ledger CLI also accepts
100
+ `AGENTLINKOPS_TOKEN`; if both are set, they must agree. The `LINKTRAIL_` spellings are
101
+ still read, with a one-line warning, during the pilot compatibility window. Never paste credentials
102
+ into chat, command arguments, or ledger files.
103
+ 3. Run the installed CLI from the directory containing the existing `.agentlinkops/` (or `.linktrail/`)
104
+ ledger. Replace the script path and IDs below with the installed package path
105
+ and IDs returned by `get_workspace` and `list_projects`.
106
+
107
+ ```sh
108
+ node /path/to/installed/plugin/cli/agentlinkops.mjs connect --origin https://app.agentlinkops.com --workspace WORKSPACE_ID --project-id PROJECT_ID --selection selected-links.json
109
+ node /path/to/installed/plugin/cli/agentlinkops.mjs sync --dry-run
110
+ node /path/to/installed/plugin/cli/agentlinkops.mjs sync --pull-only
111
+ ```
112
+
113
+ `selected-links.json` contains an array of existing ledger IDs, such as
114
+ `["local-placement-1", "local-placement-2"]`. A mapping file with
115
+ `entries: [{"local_id": "local-placement-1"}]` also works. Save an explicit
116
+ selection when only part of an existing ledger belongs in cloud monitoring.
117
+
118
+ `connect` verifies workspace identity, project access and required scopes through
119
+ read-only REST calls. It saves connection metadata and the selection in
120
+ `.agentlinkops/config.json`, without saving the credential or creating cloud records.
121
+ `sync --dry-run` reads local files only and previews uploads. `sync --pull-only`
122
+ retrieves cloud history without creating watches. After reviewing the plan, run
123
+ `sync` when the user has authorized its proposed monitoring. A saved selection
124
+ limits uploads; without one, sync selects expected or earned entries and pauses mapped retired
125
+ entries. Wanted entries require `--include-wanted`.
126
+
127
+ The cloud stores normalized source URLs: `https://example.com` becomes
128
+ `https://example.com/`. Send the ledger ID as `localReference` and join returned
129
+ rows using `local_reference`. Raw URL string equality can miss an existing watch.
130
+ Copy watch IDs from tool results without shortening them.
131
+
132
+ The Python CRM helper has its own environment settings and workspace binding,
133
+ described in [optional cloud sync](#optional-cloud-sync). Ledger `connect` does
134
+ not configure that helper. If account access or permission to create a credential
135
+ is unavailable, use the existing MCP connection for cloud work and keep local
136
+ records intact.
137
+
138
+ ## Optional cloud sync
139
+
140
+ Set `AGENTLINKOPS_API_URL` to the actual service origin and
141
+ `AGENTLINKOPS_API_KEY` using the shell's existing secure environment mechanism.
142
+ Create the scoped credential in **Agent access > Create API key** as described
143
+ [above](#connect-the-ledger-cli-to-an-existing-project). For this read-only event
144
+ sync, select `events:read` and the projects whose events the CRM should receive.
145
+ This toolkit does not bundle credentials or create an account. Never paste keys into chat or command-line arguments.
146
+
147
+ ```sh
148
+ python3 references/portable/scripts/agentlinkops.py --db ./agentlinkops.sqlite sync --limit 100 --max-pages 100
149
+ ```
150
+
151
+ The client requests `GET /v1/events?limit=100&cursor=...`. Production origins
152
+ require HTTPS. Loopback HTTP is supported for local development. Redirects
153
+ are rejected so bearer credentials never follow an endpoint redirect.
154
+ Requests time out after 30 seconds and responses are limited to 4 MiB.
155
+
156
+ Expected response:
157
+
158
+ ```json
159
+ {
160
+ "workspace_id": "workspace-example",
161
+ "events": [{
162
+ "id": "event-example", "workspace_id": "workspace-example",
163
+ "cursor": "opaque-cursor", "type": "watch.state_changed",
164
+ "project_id": "project-example", "watch_id": "watch-example",
165
+ "created_at": "2026-09-09T12:00:00Z",
166
+ "data": {
167
+ "before": {"state": "unknown"},
168
+ "after": {"state": "present", "source_url": "https://publisher.example/resources", "target_url": "https://example.com/guide"},
169
+ "observation_id": "observation-example", "evidence_key": null
170
+ }
171
+ }],
172
+ "next_cursor": "opaque-cursor", "has_more": false
173
+ }
174
+ ```
175
+
176
+ Each page commits new events, placement overlays and its cursor in one
177
+ transaction. Duplicate event IDs are ignored only when the payload is
178
+ identical. A changed payload, malformed event or mismatched workspace rolls
179
+ back the entire page. Earlier successful pages remain committed if a later
180
+ page fails; rerunning resumes at the last committed cursor. Two concurrent
181
+ syncs detect a cursor conflict and require a retry.
182
+
183
+ Events update placement `cloud_state`, `cloud_observed_at` and
184
+ `cloud_event_id`. Existing local `status`, `notes` and opportunity links are
185
+ preserved. URLs follow the cloud watch snapshot. A previously unseen watch
186
+ creates `id: cloud:<watch_id>` only when the snapshot supplies source and
187
+ destination URLs. Partial snapshots without a URL pair and unknown event types are retained in the event
188
+ feed for later interpretation; they do not fabricate a placement.
189
+
190
+ HTTP 410 reports `CURSOR_EXPIRED` and does not advance the cursor. Preserve
191
+ the database and export first; obtain a current watch snapshot and recover
192
+ into a separate database before replaying retained events. Automated snapshot
193
+ recovery is not implemented in this version. Removing the cursor alone would
194
+ hide a history gap and is not a supported recovery procedure.
195
+
196
+ All success output is JSON except requested CSV export. Errors are JSON on
197
+ stderr with nonzero exit status. HTTP response bodies and credential values
198
+ are never included in errors. Local operation has no telemetry.
199
+
200
+ ## Writer-lock recovery candidate
201
+
202
+ The maintained development source adds verified-owner recovery for `sync.lock`
203
+ and the ledger's secondary writer lock. This source acceptance is separate from
204
+ an installed release and hosted/client acceptance. A persistent OS advisory gate
205
+ serializes cooperating writers; valid same-user/same-boot metadata for a departed
206
+ writer can be recovered. Marker age alone never authorizes removal. Live, reused,
207
+ legacy empty, malformed or unverifiable markers are preserved with a refusal.
208
+
209
+ Keep the local ledger, mirror and state files. Read-only `status`/`report` remain
210
+ useful while writer ownership is unresolved. Inspect a copy; do not delete a live
211
+ lock or signal a process you did not create. An uncertain cloud request still
212
+ needs its original operation/readback before any write is retried; releasing a
213
+ local lock does not prove the remote request failed.
214
+
215
+ The candidate uses pinned prebuilt native assets for Darwin/Linux, with no compiler
216
+ fallback in the package build. Actual crash and relocated-package acceptance is
217
+ recorded for Darwin arm64; other architectures, Windows, musl and network-file
218
+ systems require separate proof. Unsupported/native-unavailable writers fail
219
+ closed with actionable setup diagnostics. Native lock acceptance does not enable
220
+ Darwin OAuth custody or establish customer credentials/grants.
@@ -0,0 +1,73 @@
1
+ # User-managed disavow lists
2
+
3
+ This is an offline record/file tool. It does not evaluate whether a link
4
+ should be disavowed, connect to Search Console, or upload a file. Record the
5
+ user's explicit choices and their provenance.
6
+
7
+ Google's documented file format uses one page URL or `domain:hostname` per
8
+ line, UTF-8 or ASCII `.txt` files, and `#` comment lines. The limits are 2,048
9
+ characters per URL, 100,000 lines including comments/blanks, and 2 MB. Domain
10
+ and subdomain rules are supported; a path prefix is not a domain-wide rule.
11
+ The tool does not support Search Console Domain properties. Uploading a new
12
+ list replaces the list for the selected property; local merging here does
13
+ not establish what Google currently holds. Google says most sites do not
14
+ need disavowals. [Official format and usage guidance](https://support.google.com/webmasters/answer/2648487?hl=en),
15
+ checked September 9, 2026.
16
+
17
+ ## Commands
18
+
19
+ Resolve example helper paths from the plugin root for shared references, or from the skill directory for a standalone archive's bundled references. Keep the database outside the installation directory:
20
+
21
+ ```sh
22
+ python3 references/portable/scripts/agentlinkops.py --db ./agentlinkops.sqlite migrate
23
+ python3 references/portable/scripts/agentlinkops.py --db ./agentlinkops.sqlite disavow import --property https://my-product.com/ --source "User-reviewed existing list" --file existing-list.txt
24
+ python3 references/portable/scripts/agentlinkops.py --db ./agentlinkops.sqlite disavow list --property https://my-product.com/
25
+ python3 references/portable/scripts/agentlinkops.py --db ./agentlinkops.sqlite disavow export --property https://my-product.com/ --output reviewed-disavow.txt
26
+ ```
27
+
28
+ Every command requires the exact URL-prefix property. HTTP and HTTPS remain
29
+ separate; a subdomain or path prefix remains separate from its parent. The
30
+ helper records this local scope; it does not verify property ownership.
31
+
32
+ Imports merge rules transactionally. Any invalid line rejects the entire
33
+ file with no new rules or source record. Duplicates reuse stable rule IDs.
34
+ Each source retains its label, original text and SHA-256; provenance records
35
+ the original line, line number and associated comments. Reimporting identical
36
+ content under the same source label is idempotent. A new source label adds
37
+ provenance without duplicating the rule. Imports never overwrite local notes
38
+ or reactivate a rule the user disabled.
39
+
40
+ To add or explicitly change a rule, prepare `rule.json` and run:
41
+
42
+ ```sh
43
+ python3 references/portable/scripts/agentlinkops.py --db ./agentlinkops.sqlite disavow upsert --property https://my-product.com/ --source "User decision after review" --file rule.json
44
+ ```
45
+
46
+ ```json
47
+ {"kind":"domain","value":"publisher.example","active":false,"notes":"User removed this rule after reviewing the source."}
48
+ ```
49
+
50
+ `kind` is `domain` or `url`. `active` defaults to the existing value, or true
51
+ for a new rule. Omitted notes are preserved. Deactivation is reversible and
52
+ retains provenance; there is no destructive delete command.
53
+
54
+ Domain normalization changes case and a terminal DNS dot only. Hostnames
55
+ must be ASCII or already Punycode; the helper does not guess IDN conversion.
56
+ URLs preserve path case, query strings and fragments. A path ending in `/`
57
+ is still one exact page URL; wildcard patterns are rejected. URL credentials
58
+ and malformed domains are rejected. UTF-8 paths, LF/CRLF and an optional UTF-8
59
+ BOM are accepted. Original source text remains available in the full JSON
60
+ CRM export.
61
+
62
+ `.txt` exports contain active rules for one property, sorted by rule kind and
63
+ value with stable header comments. They omit timestamps and private notes,
64
+ so unchanged rules produce identical bytes. The export is checked against
65
+ the documented line limit and a conservative 2,000,000-byte ceiling, and never
66
+ overwrites an existing file. The same byte ceiling applies to imports.
67
+ Without `--output`, it writes the same text to stdout. The local list can be
68
+ empty; an empty export is not evidence that a Google-side list was cleared.
69
+
70
+ `disavow list --active-only` filters the view. The general `export` command
71
+ includes all disavow rules, sources and provenance in the local JSON backup.
72
+ No monitoring result, authority metric or contact inspection automatically
73
+ creates a rule.
@@ -0,0 +1,76 @@
1
+ # Local Outreach CRM draft handoff
2
+
3
+ Use only when the customer selects a local Outreach CRM with an existing
4
+ `projects/<crm_project>/config.json` and `drafts/` directory. The config must contain
5
+ that exact `project` and a `site` URL with the same hostname as the request.
6
+ This adapter writes an inert body and companion manifest under that project's
7
+ drafts directory. It does not write contacts, replies, suppression, send logs or
8
+ sequence state. Native vendor APIs remain unsupported by this adapter.
9
+
10
+ Resolve example helper paths from the plugin root for shared references, or from the skill directory for a standalone archive's bundled references:
11
+
12
+ ```sh
13
+ python3 references/portable/scripts/outreach-crm-draft.py --crm-root /path/to/customer-crm --request request.json --body-file reviewed-body.txt
14
+ python3 references/portable/scripts/outreach-crm-draft.py --crm-root /path/to/customer-crm --request request.json --body-file reviewed-body.txt --readback
15
+ ```
16
+
17
+ `request.json` contains exactly these fields; use actual saved identities rather
18
+ than copying the example values:
19
+
20
+ ```json
21
+ {
22
+ "version": 1,
23
+ "crm_project": "customer-project",
24
+ "workspace_id": "saved-workspace",
25
+ "project_id": "saved-project",
26
+ "site": "https://customer.invalid",
27
+ "operation_id": "original-operation",
28
+ "original_request": {
29
+ "destination": "saved-service-destination",
30
+ "workspace_id": "saved-workspace",
31
+ "project_id": "saved-project",
32
+ "command": "saved-command",
33
+ "arguments": {"saved-non-secret-argument": "saved-value"},
34
+ "idempotency_key": "original-request-key"
35
+ },
36
+ "local_id": "saved-local-record",
37
+ "external_id": "saved-crm-record",
38
+ "observation": "unknown",
39
+ "reply": "unknown",
40
+ "suppression": "unknown",
41
+ "subject": "Reviewed draft subject",
42
+ "body_sha256": "exact-sha256-of-reviewed-body-bytes"
43
+ }
44
+ ```
45
+
46
+ Preserve the original request tuple from its checkpoint, including its exact
47
+ non-secret arguments. Never place credentials in it. Workspace/project binding
48
+ is customer supplied; this helper does not authenticate it against hosted grants
49
+ or confirm that an external ID exists in CRM contacts. Its local config check
50
+ prevents an explicit site/project mismatch, not a forged customer mapping.
51
+
52
+ `observation` is `unknown`, `present`, `provisional_loss` or `confirmed_loss`;
53
+ `reply` is `unknown`, `none` or `replied`; `suppression` is `unknown`, `clear` or
54
+ `suppressed`. These are retained observations, not refreshed sender decisions.
55
+ Drafts always have `send_eligible: false`, including apparently clear rows.
56
+ Follow the qualified campaign recipe's content review before saving a real draft.
57
+
58
+ The original operation and local/external identities determine one stable draft
59
+ directory. The helper verifies the supplied UTF-8 body hash, publishes `body.txt`
60
+ and `manifest.json`, then rereads their exact bytes. An identical retry reports
61
+ `identical_readback`. If interrupted between files, repeat the original save;
62
+ it completes the missing artifact without replacing the existing body. A changed
63
+ subject, body, request or mapping at that identity refuses rather than overwrites.
64
+ Inspect and preserve the conflicting original artifact; a legitimate new operation
65
+ requires a separately recorded identity, not a fabricated replay key.
66
+
67
+ `--readback` verifies both artifacts and refuses missing or partial saves. It does
68
+ not create a draft directory. Keep the receipt's operation, destination directory,
69
+ body and manifest hashes with the customer's checkpoint. Saving here is actual
70
+ local filesystem destination readback, not a mailbox/vendor acknowledgement.
71
+
72
+ Publication reuses the portable helper's cooperating-writer locking and ExFAT
73
+ fallback. Destination symlinks are refused. This does not sandbox arbitrary local
74
+ applications or prove power-loss durability. No email system or network dependency
75
+ is installed. Current sender reply/suppression/quota/mailbox gates must still be
76
+ checked by the customer's existing sender at its separately authorized send step.
@@ -0,0 +1,51 @@
1
+ # Portable ledger handoff
2
+
3
+ Capability: local draft-only file. Native vendor acceptance: untested. No network
4
+ or messages. Workspace/project identities are supplied by the customer and are
5
+ not verified against hosted grants by this helper.
6
+
7
+ Run the CLI's frozen report first:
8
+
9
+ ```sh
10
+ agentlinkops report --json --as-of 2026-09-30T12:00:00Z --out report.json
11
+ python3 references/portable/scripts/portable-handoff.py --report report.json --mapping mapping.json --out handoff.json
12
+ ```
13
+
14
+ Use your own observation cutoff, rather than copying this example's date.
15
+ `mapping.json` requires version 1, workspace_id, project_id, site (an explicit URL),
16
+ destination `portable-file`, source_report_sha256 and records. Compute the report
17
+ hash from JSON encoded with sorted keys, compact separators and UTF-8; the helper's
18
+ `digest` function defines it. This local command prints the value:
19
+
20
+ ```sh
21
+ python3 -c 'import hashlib,json; r=json.load(open("report.json")); print(hashlib.sha256(json.dumps(r,sort_keys=True,separators=(",",":"),ensure_ascii=False).encode()).hexdigest())'
22
+ ```
23
+
24
+ Each records key is an actual local ledger ID, with
25
+ external_id, reply (`unknown`, `none`, `replied`) and suppression (`unknown`,
26
+ `suppressed`, `clear`). Supply these from your own tools; an empty field never
27
+ means permission to send. Every report row needs an explicit mapping. Unknown
28
+ customer IDs must stay unknown; do not fabricate a mapping to make export pass.
29
+
30
+ The handoff carries the original report state, observation date and evidence block,
31
+ including any hash and locator, plus a stable operation ID. Notes and optional
32
+ commercial references are not copied. `ref` is private by default in the report;
33
+ this helper uses your explicit mapping instead. Identical reruns read the original
34
+ file. A different report, site or existing output refuses before replacement.
35
+ Keep a distinct output for a new snapshot; this is a dated artifact, not a monitor.
36
+
37
+ Read the saved JSON back before claiming persistence. Its destination outcome is
38
+ `not_attempted`, and `send_eligible` remains false on every row. Passing this helper
39
+ does not prove a CRM import or sequencer readback. Before a destination write,
40
+ retain the exact mapping and request, then read the destination back when authorized.
41
+ After a response-lost import, preserve its actual operation identity and unknown
42
+ outcome; do not replace it with this file's operation ID or repeat a create to
43
+ repair an artifact error. Keep monitoring facts apart from sent/replied/suppressed
44
+ records. For additional guidance, select `agentlinkops agent setup --recipe qualified-campaign-handoff`
45
+ or `agentlinkops agent setup --recipe placement-reconciliation` and read the selected
46
+ recipe. Neither is required to prepare this local file.
47
+
48
+ Resolve example helper paths from the plugin root for shared references, or from
49
+ the skill directory for a standalone archive's bundled references. Unsupported native
50
+ vendors, mailbox access and automatic suppression synchronization remain outside
51
+ this helper. No account, credential or send action is installed by it.
@@ -0,0 +1,53 @@
1
+ # Branded HTML placement reports
2
+
3
+ Reports are generated entirely from the local CRM. They make no network
4
+ requests, run no additional checks, and do not imply broader backlink-index
5
+ coverage. The output is standalone HTML with embedded styles and print
6
+ styling; it is not a generated PDF.
7
+
8
+ ```sh
9
+ python3 references/portable/scripts/agentlinkops.py --db ./agentlinkops.sqlite report --project project-id --brand "My Agency" --title "Placement monitoring report" --output placements.html
10
+ ```
11
+
12
+ Omit `--project` only when the requested report covers every local placement.
13
+ By default, a report includes source/destination URLs, cloud-observed state,
14
+ observation timestamp, report timestamp and aggregate placement counts.
15
+ It excludes local notes, local costs, contacts, outreach activity, credentials,
16
+ disavow rules, workspace IDs and raw event payloads. URLs are the selected
17
+ records' actual URLs; review the selection before sharing the file.
18
+
19
+ `--include-notes` and `--include-costs` explicitly add those private local
20
+ fields. Use these only when the user requests that content. Included cost
21
+ totals are calculated separately per currency without exchange rates.
22
+ Local CRM status is not substituted for a verified cloud observation.
23
+
24
+ Record a cost with an ordinary partial placement upsert:
25
+
26
+ ```json
27
+ {"id":"placement-id","cost_amount":"12.50","cost_currency":"USD"}
28
+ ```
29
+
30
+ Amounts are nonnegative decimal strings with at most six decimal places,
31
+ not JSON floating-point numbers. Both amount and currency are required for
32
+ a newly recorded cost. The currency is an uppercase three-letter label;
33
+ the helper does not perform a current currency-directory lookup. Updating
34
+ other fields or synchronizing cloud events preserves local costs. Set both
35
+ cost fields to null to clear a cost explicitly.
36
+
37
+ The generator escapes all text/attributes and creates clickable links only
38
+ for HTTP(S) URLs without credentials. It loads no external scripts, fonts,
39
+ logos or tracking pixels. The title and brand are user-selected text labels.
40
+ Local notes remain escaped text, even if they contain HTML.
41
+
42
+ Rows have a stable ordering. Supply a fixed `--generated-at` ISO 8601
43
+ timestamp to reproduce identical output from the same data and options:
44
+
45
+ ```sh
46
+ python3 references/portable/scripts/agentlinkops.py --db ./agentlinkops.sqlite report --title "Placement report" --generated-at 2026-09-09T14:00:00Z --output reproducible.html
47
+ ```
48
+
49
+ Otherwise the timestamp is the current time. Coverage reports the range of
50
+ stored observation times separately from report generation. Unknown and
51
+ unobserved states remain distinct from confirmed loss. Existing output files
52
+ are never overwritten. Browser printing, PDF generation and automated report
53
+ delivery are separate capabilities and are not performed by this helper.