Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "agentlinkops",
"version": "0.6.3",
"version": "0.6.4",
"description": "Connect an agent to AgentLinkOps and run backlink work with it: the connect skill (views, the three discovery verbs, evidence rules), local ledger and campaign skills, a portable CRM, the source-cited content pipeline, user-managed disavow lists, HTML reports and monitoring-event sync.",
"author": {
"name": "AgentLinkOps"
Expand Down
2 changes: 1 addition & 1 deletion .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "agentlinkops",
"version": "0.6.3",
"version": "0.6.4",
"description": "Connect an agent to AgentLinkOps and run backlink work with it: the connect skill (views, the three discovery verbs, evidence rules), local ledger and campaign skills, a portable CRM, the source-cited content pipeline, user-managed disavow lists, HTML reports and monitoring-event sync.",
"author": {
"name": "AgentLinkOps"
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
All notable changes to the `agentlinkops` package are recorded here. The version follows the
plugin manifests in `.claude-plugin/` and `.codex-plugin/`.

## 0.6.4 (2026-09-20)

- Import and export immutable local IndexNow submission receipts, with hashes and replay checks. These commands send no submissions.
- Join each submission attempt to later retained index observations for the exact URL and project. JSON and CSV preserve source, date, reason and unknown results.
- Submission responses remain received, validation pending, failed or unknown. Later Google observations do not verify indexing by an IndexNow participant or establish that submission caused indexing.

## 0.6.3 (2026-09-20)

- Agent installation preserves customer edits and supports previewed removal and interrupted-install recovery.
Expand Down
2 changes: 1 addition & 1 deletion cli/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {restResponseContract} from '../shared/response-contract.js';
// A small HTTP client for the cloud. Scoped keys only; no human session ever reaches here.
// DP-0036-T13: the CLI names itself so the usage counters can tell it from other REST callers.
// The version follows the plugin manifests; bump it with them.
export const CLI_VERSION = '0.6.3';
export const CLI_VERSION = '0.6.4';
export const CLI_USER_AGENT = `agentlinkops-cli/${CLI_VERSION}`;
export class CloudError extends Error {
constructor(code, status, details = null) {
Expand Down
3 changes: 2 additions & 1 deletion cli/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const DEFAULTS = Object.freeze({
receipts: 'receipts.jsonl',
observations: 'observations.jsonl',
indexObservations: 'index-observations.jsonl',
indexSubmissions: 'index-submissions.jsonl',
events: 'events.jsonl',
candidates: 'candidates.jsonl',
state: 'state.json',
Expand Down Expand Up @@ -99,7 +100,7 @@ export async function loadConfig({ cwd = process.cwd(), root = null } = {}) {
defaults: { ...DEFAULTS, ...file.defaults },
paths: {
ledger: at('ledger'), observations: at('observations'), events: at('events'),
candidates: at('candidates'), state: at('state'), receipts: at('receipts'), indexObservations: at('indexObservations'),
candidates: at('candidates'), state: at('state'), receipts: at('receipts'), indexObservations: at('indexObservations'), indexSubmissions: at('indexSubmissions'),
},
};
}
16 changes: 15 additions & 1 deletion cli/index-observations.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {boundedSubmissionText,readSubmissions,saveSubmissions,reconcileSubmissions,submissionCsv} from './index-submissions.js';
import {readFile,writeFile,open,rename,unlink,mkdir,stat} from 'node:fs/promises';
import {dirname,resolve} from 'node:path';
import {createHash} from 'node:crypto';
Expand All @@ -9,7 +10,7 @@ import {indexEvidenceReference,describeEvidenceReference} from '../src/evidence-
import {indexReceiptResponse} from '../shared/index-observation-contract.js';
const sha=value=>createHash('sha256').update(value).digest('hex');
const fail=message=>{throw new ConfigError(message);};
export const INDEX_CLI_HELP='agentlinkops index pull --watch-id ID [--limit 50] [--before ID] | import RECEIPTS.json | export [--out FILE] | csv URLS.csv [--out FILE] | check-csv URLS.csv --connection-id ID --request-id ID --out FILE';
export const INDEX_CLI_HELP='agentlinkops index pull --watch-id ID [--limit 50] [--before ID] | import RECEIPTS.json | export [--out FILE] | csv URLS.csv [--out FILE] | check-csv URLS.csv --connection-id ID --request-id ID --out FILE | submissions-import FILE | submissions-export --project-id ID [--out FILE] | reconcile-submissions --project-id ID [--format json|csv] [--out FILE]';
function validate(row){
if(row?.v!==1||! /^[\w-]{1,128}$/.test(row.project_id??'')||! /^[\w-]{1,128}$/.test(row.watch_id??''))fail('Invalid local index receipt scope');
const receipt=indexReceiptResponse.parse(row.receipt);
Expand Down Expand Up @@ -61,6 +62,19 @@ function csvInput(text){
}
export async function indexMain(args,{cwd=process.cwd(),env=process.env,out=console.log,fetchImpl=globalThis.fetch}={}){
const command=args._[1],config=await loadConfig({cwd}),path=config.paths.indexObservations;
if(['submissions-import','submissions-export','reconcile-submissions'].includes(command)){
const allowed=new Set(['_','tag','out','project-id','format']);
if(args.tag?.length||Object.keys(args).some(key=>!allowed.has(key))||args._.length!==(command==='submissions-import'?3:2))fail(INDEX_CLI_HELP);
if(command==='submissions-import'){
if(args.out||args['project-id']||args.format)fail(INDEX_CLI_HELP);
out(JSON.stringify(await saveSubmissions(config.paths.indexSubmissions,JSON.parse(await boundedSubmissionText(resolve(cwd,args._[2]))))));return 0;
}
if(args.format&&!['json','csv'].includes(args.format))fail('Submission format must be json or csv');
if(command==='submissions-export'&&args.format)fail(INDEX_CLI_HELP);
const report=reconcileSubmissions(await readSubmissions(config.paths.indexSubmissions),command==='reconcile-submissions'?await readIndexReceipts(path):[],args['project-id']);
const text=command==='submissions-export'?JSON.stringify(report.submissions.map(({outcome,observations,verification,...row})=>row),null,2):args.format==='csv'?submissionCsv(report):JSON.stringify(report,null,2);
if(args.out){if(typeof args.out!=='string')fail('--out requires a file');await atomicOutput(resolve(cwd,args.out),text);}else out(text);return 0;
}
const allowed=new Set(['_','tag','out','watch-id','limit','before','connection-id','request-id']);if(args.tag?.length||Object.keys(args).some(key=>!allowed.has(key)))fail(INDEX_CLI_HELP);
if(command==='import'){
if(!args._[2])fail(INDEX_CLI_HELP);const parsed=JSON.parse(await boundedText(resolve(cwd,args._[2])));if(!Array.isArray(parsed)||parsed.length>100)fail('Import requires an array of at most 100 scoped index receipts');out(JSON.stringify(await saveIndexReceipts(path,parsed)));return 0;
Expand Down
50 changes: 50 additions & 0 deletions cli/index-submissions.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions cli/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ const USAGE = `agentlinkops — a backlink ledger that lives in your repository
agentlinkops admission list|add|update|remove|evaluate|reevaluate|receipt --project ID
agentlinkops disavow list|history|propose|import|export|approve|reject|delete
agentlinkops index import FILE | export [--out FILE] | csv URLS.csv [--out FILE]
agentlinkops index submissions-import FILE | submissions-export --project-id ID [--out FILE]
agentlinkops index reconcile-submissions --project-id ID [--format json|csv] [--out FILE]
agentlinkops index check-csv URLS.csv --connection-id ID --request-id ID --out FILE
agentlinkops import FILE --target DOMAIN [--from SUPPLIER] [--map source=COL,target=COL]
[--exact-url] [--no-subdomains] [--generated-at ISO] [--json]
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "agentlinkops",
"version": "0.6.3",
"version": "0.6.4",
"description": "A backlink and AI citation ledger that lives in your repository: local link and citation checks with dated evidence, supplier-export imports and optional sync to the hosted AgentLinkOps service.",
"author": "AgentLinkOps (https://agentlinkops.com)",
"license": "Apache-2.0",
Expand Down
44 changes: 44 additions & 0 deletions test/index-submissions-cli.test.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading