agentlinkops 0.6.13 → 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.
- package/.claude-plugin/plugin.json +2 -2
- package/.codex-plugin/plugin.json +2 -2
- package/CHANGELOG.md +25 -2
- package/PACK-ARTIFACT.json +16 -0
- package/PACKS.json +591 -0
- package/README.md +34 -48
- package/cli/adapters/suppliers.js +1 -1
- package/cli/agent-setup.js +101 -17
- package/cli/campaign-bridge.js +325 -0
- package/cli/campaign-outcomes.js +107 -0
- package/cli/catalog.js +45702 -18674
- package/cli/check.js +40 -10
- package/cli/client.js +2 -1
- package/cli/commands.js +17 -8
- package/cli/connection.js +92 -18
- package/cli/contact-adoption.js +91 -0
- package/cli/context.js +13 -2
- package/cli/doctor.js +15 -4
- package/cli/import.js +7 -1
- package/cli/key-store.js +115 -0
- package/cli/ledger.js +1 -0
- package/cli/local-result.js +41 -6
- package/cli/main.js +51 -12
- package/cli/mcp-client.js +2 -2
- package/cli/migration.js +308 -0
- package/cli/prompt.js +25 -0
- package/cli/public-http.js +168 -0
- package/cli/state.js +14 -0
- package/cli/sync.js +1 -1
- package/package.json +7 -8
- package/references/citation-panels.md +2 -0
- package/references/cli.md +2 -2
- package/references/disavow.md +1 -1
- package/references/discovery-handoff.md +1 -1
- package/references/local-crm-draft-handoff.md +1 -1
- package/references/portable-ledger-handoff.md +11 -10
- package/references/recipes/catalog.json +263 -7
- package/references/recipes/command-notes.md +5 -3
- package/references/recipes/internal-link-improvement.md +25 -0
- package/references/recipes/placement-reconciliation.md +53 -1
- package/scripts/portable-handoff.py +17 -4
- package/shared/admission-contract.js +3 -2
- package/shared/admission-report-contract.js +3 -2
- package/shared/analytics-history-contract.js +15 -0
- package/shared/campaign-outcome-contract.js +12 -0
- package/shared/campaign-prospect-contract.js +23 -0
- package/shared/classification-contract.js +54 -0
- package/shared/contact-record-contract.js +27 -0
- package/shared/disavow-contract.js +37 -9
- package/shared/index-observation-contract.js +5 -4
- package/shared/internal-links-contract.js +143 -0
- package/shared/link-category-contract.js +35 -0
- package/shared/link-category.js +138 -0
- package/shared/link-labels.js +26 -0
- package/shared/link-summary-contract.js +17 -0
- package/shared/observation-event.js +1 -1
- package/shared/onboarding.js +20 -0
- package/shared/response-contract.js +38 -5
- package/shared/search-connection-contract.js +2 -1
- package/shared/table-page-contract.js +13 -0
- package/skills/agentlinkops-assets/SKILL-DEPS.json +290 -0
- package/skills/agentlinkops-assets/SKILL.md +3 -1
- package/skills/agentlinkops-assets/agents/openai.yaml +1 -1
- package/skills/agentlinkops-assets/references/portable/references/cli.md +220 -0
- package/skills/agentlinkops-assets/references/portable/references/disavow.md +73 -0
- package/skills/agentlinkops-assets/references/portable/references/reports.md +53 -0
- package/skills/agentlinkops-assets/references/portable/scripts/agentlinkops.py +676 -0
- package/skills/agentlinkops-assets/references/portable/scripts/disavow.py +207 -0
- package/skills/agentlinkops-assets/references/portable/scripts/migrations/001_crm.sql +45 -0
- package/skills/agentlinkops-assets/references/portable/scripts/migrations/002_cloud_events.sql +13 -0
- package/skills/agentlinkops-assets/references/portable/scripts/migrations/003_disavow.sql +20 -0
- package/skills/agentlinkops-assets/references/portable/scripts/migrations/004_placement_costs.sql +2 -0
- package/skills/agentlinkops-assets/references/portable/scripts/report.py +113 -0
- package/skills/agentlinkops-assets/references/portable/templates/campaigns.v1.json +65 -0
- package/skills/agentlinkops-campaigns/SKILL-DEPS.json +336 -0
- package/skills/agentlinkops-campaigns/SKILL.md +7 -5
- package/skills/agentlinkops-campaigns/agents/openai.yaml +1 -1
- package/skills/agentlinkops-campaigns/references/first-campaign.md +31 -0
- package/skills/agentlinkops-campaigns/references/mailbox-campaign.md +46 -0
- package/skills/agentlinkops-campaigns/references/portable/references/cli.md +220 -0
- package/skills/agentlinkops-campaigns/references/portable/references/disavow.md +73 -0
- package/skills/agentlinkops-campaigns/references/portable/references/reports.md +53 -0
- package/skills/agentlinkops-campaigns/references/portable/scripts/agentlinkops.py +676 -0
- package/skills/agentlinkops-campaigns/references/portable/scripts/disavow.py +207 -0
- package/skills/agentlinkops-campaigns/references/portable/scripts/migrations/001_crm.sql +45 -0
- package/skills/agentlinkops-campaigns/references/portable/scripts/migrations/002_cloud_events.sql +13 -0
- package/skills/agentlinkops-campaigns/references/portable/scripts/migrations/003_disavow.sql +20 -0
- package/skills/agentlinkops-campaigns/references/portable/scripts/migrations/004_placement_costs.sql +2 -0
- package/skills/agentlinkops-campaigns/references/portable/scripts/report.py +113 -0
- package/skills/agentlinkops-campaigns/references/portable/templates/campaigns.v1.json +65 -0
- package/skills/agentlinkops-connect/SKILL-DEPS.json +104 -0
- package/skills/agentlinkops-connect/SKILL.md +2 -0
- package/skills/agentlinkops-connect/agents/openai.yaml +1 -1
- package/skills/agentlinkops-connect/references/agentlinkops.md +164 -62
- package/skills/agentlinkops-connect/references/placement-workflows.md +19 -0
- package/skills/agentlinkops-crm/SKILL-DEPS.json +381 -0
- package/skills/agentlinkops-crm/SKILL.md +26 -9
- package/skills/agentlinkops-crm/agents/openai.yaml +1 -1
- package/skills/agentlinkops-crm/references/portable/references/cli.md +220 -0
- package/skills/agentlinkops-crm/references/portable/references/disavow.md +73 -0
- package/skills/agentlinkops-crm/references/portable/references/local-crm-draft-handoff.md +76 -0
- package/skills/agentlinkops-crm/references/portable/references/portable-ledger-handoff.md +51 -0
- package/skills/agentlinkops-crm/references/portable/references/reports.md +53 -0
- package/skills/agentlinkops-crm/references/portable/scripts/agentlinkops.py +676 -0
- package/skills/agentlinkops-crm/references/portable/scripts/disavow.py +207 -0
- package/skills/agentlinkops-crm/references/portable/scripts/migrations/001_crm.sql +45 -0
- package/skills/agentlinkops-crm/references/portable/scripts/migrations/002_cloud_events.sql +13 -0
- package/skills/agentlinkops-crm/references/portable/scripts/migrations/003_disavow.sql +20 -0
- package/skills/agentlinkops-crm/references/portable/scripts/migrations/004_placement_costs.sql +2 -0
- package/skills/agentlinkops-crm/references/portable/scripts/outreach-crm-draft.py +121 -0
- package/skills/agentlinkops-crm/references/portable/scripts/portable-handoff.py +197 -0
- package/skills/agentlinkops-crm/references/portable/scripts/report.py +113 -0
- package/skills/agentlinkops-crm/references/portable/templates/campaigns.v1.json +65 -0
- package/skills/agentlinkops-discovery/SKILL-DEPS.json +301 -0
- package/skills/agentlinkops-discovery/SKILL.md +4 -2
- package/skills/agentlinkops-discovery/references/portable/references/cli.md +220 -0
- package/skills/agentlinkops-discovery/references/portable/references/disavow.md +73 -0
- package/skills/agentlinkops-discovery/references/portable/references/discovery-handoff.md +32 -0
- package/skills/agentlinkops-discovery/references/portable/references/reports.md +53 -0
- package/skills/agentlinkops-discovery/references/portable/scripts/agentlinkops.py +676 -0
- package/skills/agentlinkops-discovery/references/portable/scripts/disavow.py +207 -0
- package/skills/agentlinkops-discovery/references/portable/scripts/migrations/001_crm.sql +45 -0
- package/skills/agentlinkops-discovery/references/portable/scripts/migrations/002_cloud_events.sql +13 -0
- package/skills/agentlinkops-discovery/references/portable/scripts/migrations/003_disavow.sql +20 -0
- package/skills/agentlinkops-discovery/references/portable/scripts/migrations/004_placement_costs.sql +2 -0
- package/skills/agentlinkops-discovery/references/portable/scripts/report.py +113 -0
- package/skills/agentlinkops-discovery/references/portable/templates/campaigns.v1.json +65 -0
- package/skills/agentlinkops-discovery/references/saved-backlink-workflows.md +23 -0
- package/src/competitors/contract.js +17 -1
- package/src/context/site-profile.js +34 -121
- package/src/corpus/acquire.js +68 -32
- package/src/corpus/extract.js +147 -0
- package/src/corpus/frontier.js +151 -0
- package/src/discovery/contract.js +8 -0
- package/src/discovery/import.js +13 -1
- package/src/mentions.js +193 -0
- package/src/page-acquisition/acquire.js +50 -0
- package/src/page-acquisition/bytes.js +138 -0
- package/src/page-acquisition/capture.js +11 -0
- package/src/page-acquisition/contracts.js +67 -0
- package/src/page-acquisition/documents.js +166 -0
- package/src/page-acquisition/http.js +307 -0
- package/src/page-acquisition/policy.js +20 -0
- package/src/page-acquisition/recovery.js +164 -0
- package/src/page-acquisition/render-failure.js +82 -0
- package/src/page-extraction/html.js +359 -0
- package/src/page-extraction/index.js +30 -0
- package/src/page-extraction/metadata.js +16 -0
- package/src/response-shaping.js +4 -3
- package/src/tool-views.js +22 -12
- package/src/verifier/fetch.js +4 -339
- package/src/verifier/index.js +81 -23
- package/src/verifier/page-quality.js +14 -0
- package/src/verifier/parser.js +2 -241
- package/src/verifier/readiness.js +64 -13
- package/src/verifier/render-evidence.js +25 -2
- package/src/verifier/target.js +201 -0
- package/src/verifier/transition.js +18 -3
- package/src/verifier/url.js +31 -0
- package/src/watch-crawlability.js +144 -0
- package/skills/content-defingerprinting/SKILL.md +0 -29
- package/skills/content-defingerprinting/scripts/watermarks/README.md +0 -102
- package/skills/content-defingerprinting/scripts/watermarks/UPSTREAM.json +0 -26
- package/skills/content-defingerprinting/scripts/watermarks/revendor.sh +0 -52
- package/skills/content-defingerprinting/scripts/watermarks/vendor/LICENSE.upstream +0 -21
- package/skills/content-defingerprinting/scripts/watermarks/vendor/clean_text.py +0 -96
- package/skills/content-defingerprinting/scripts/watermarks/vendor/common.py +0 -432
- package/skills/content-defingerprinting/scripts/watermarks/vendor/inspect_text.py +0 -86
- package/skills/content-defingerprinting/scripts/watermarks/vendor/score_stylometry.py +0 -460
- package/skills/content-defingerprinting/scripts/watermarks/vendor/text_unicode.py +0 -651
- package/skills/content-defingerprinting/scripts/watermarks/verify-vendor.py +0 -27
- package/skills/content-defingerprinting/scripts/watermarks/wm-scan.py +0 -212
- package/skills/internal-linking-optimizer/SKILL.md +0 -56
- package/skills/internal-linking-optimizer/references/source-prompt.md +0 -76
- package/skills/pre-publish-review/SKILL.md +0 -179
- package/skills/source-cited-content-builder/SKILL.md +0 -56
- package/skills/source-cited-content-builder/references/source-prompt.md +0 -19
- package/skills/source-cited-humanizer/SKILL.md +0 -20
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# agentlinkops
|
|
2
2
|
|
|
3
|
+
<!-- generated:customer-catalog begin -->
|
|
4
|
+
Pack version 0.6.16. 156 commands in 13 toolsets.
|
|
5
|
+
<!-- generated:customer-catalog end -->
|
|
6
|
+
|
|
3
7
|
A backlink ledger that lives in your repository. `agentlinkops check` fetches a source page,
|
|
4
8
|
looks for the link you declared and records an observation with the HTTP status, redirect
|
|
5
9
|
chain, robots posture and a SHA-256 of the fetched bytes. Page HTML never enters the repository.
|
|
@@ -30,7 +34,8 @@ the installed version's help before selecting guidance.
|
|
|
30
34
|
`https://app.agentlinkops.com/mcp` as a remote HTTP MCP server in your client.
|
|
31
35
|
On a version whose help supports selection, use `agentlinkops agent setup --skill ID`
|
|
32
36
|
or `--recipe ID` for optional guidance; repeat either flag for several choices.
|
|
33
|
-
Choose `--all-skills` only for
|
|
37
|
+
Choose `--all-skills` only for this product pack's skills. Separate optional packs
|
|
38
|
+
require `--pack DIR`. Customer instructions and records
|
|
34
39
|
remain the customer's choice.
|
|
35
40
|
|
|
36
41
|
Preview `agentlinkops agent remove --recipe ID` or `--skill ID` before adding
|
|
@@ -58,8 +63,8 @@ the installed version's help before selecting guidance.
|
|
|
58
63
|
|
|
59
64
|
The skill is a short door: the first time your agent uses AgentLinkOps in a session it reads
|
|
60
65
|
the full agent reference, which `agentlinkops skill` prints and
|
|
61
|
-
https://agentlinkops.com/SKILL.md serves. Hosted monitoring is
|
|
62
|
-
local commands below need no account. The package is `agentlinkops` on npm, published by the
|
|
66
|
+
https://agentlinkops.com/SKILL.md serves. Hosted monitoring is a private pilot, and access opens
|
|
67
|
+
in stages; the local commands below need no account. The package is `agentlinkops` on npm, published by the
|
|
63
68
|
`agentlinkops` account; the source is https://github.com/sunlover7/agentlinkops-cli.
|
|
64
69
|
|
|
65
70
|
## Install the CLI on its own
|
|
@@ -168,46 +173,16 @@ agentlinkops sync --dry-run
|
|
|
168
173
|
agentlinkops sync
|
|
169
174
|
```
|
|
170
175
|
|
|
171
|
-
Hosted access
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
## Citation watches
|
|
175
|
-
|
|
176
|
-
An AI citation is a placement: a reference to your asset on a surface you do not control,
|
|
177
|
-
re-decided by the model on every ask. So the CLI watches it the way it watches a link — a prompt
|
|
178
|
-
panel, run against engine APIs on cadence, with dated evidence and honest statistics. A citation
|
|
179
|
-
is never reported as "removed": each epoch states the citation rate with a Wilson interval and
|
|
180
|
-
its sample size, classified against the previous epoch as declined, grown or
|
|
181
|
-
not_distinguishable, or `insufficient_data` when n is too thin to interpret.
|
|
176
|
+
Hosted access opens in stages: anyone can create an account at https://app.agentlinkops.com/sign-up,
|
|
177
|
+
and an account outside the pilot is told it is waiting for access. The hosted checks, evidence store
|
|
178
|
+
and MCP server are a separate service and are not part of this package.
|
|
182
179
|
|
|
183
|
-
|
|
184
|
-
agentlinkops citation panel --domain example.com --brand Example --out panel.json
|
|
185
|
-
agentlinkops citation run panel.json --max-usd 1.00
|
|
186
|
-
```
|
|
180
|
+
## Citation commands
|
|
187
181
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
The runner reserves estimated cost before each call and retry. A reported supplier
|
|
193
|
-
overrun stops later calls; estimates do not enforce a provider invoice cap. A budget
|
|
194
|
-
abort leaves a partial-epoch receipt and exit code 2. A cell
|
|
195
|
-
that declined with complete evidence exits 1, like an expected link observed absent.
|
|
196
|
-
|
|
197
|
-
Browser measurement requires `AGENTLINKOPS_PROXY_URL` and refuses direct fallback.
|
|
198
|
-
For an explicitly authorized direct diagnostic, set `AGENTLINKOPS_BROWSER_EGRESS=direct-diagnostic`
|
|
199
|
-
and leave the proxy unset. The standard URL proxy provides no actual usage meter;
|
|
200
|
-
its cost remains an estimate. Browser login and live account acceptance are separate.
|
|
201
|
-
|
|
202
|
-
Browser epochs retain supplied usage and cleanup receipts in
|
|
203
|
-
`.agentlinkops/citations/egress/<epochId>.jsonl`. Unknown meter values stay null. A missing
|
|
204
|
-
final record means the journal is incomplete; it does not establish complete costs or cleanup.
|
|
205
|
-
Journals stop at 10,000 records or 8 MiB, and a failed write stops further samples.
|
|
206
|
-
|
|
207
|
-
Frozen inventories and evidence uploads check files against their retained observation hashes.
|
|
208
|
-
Historical hashes verify the recorded answer fields and screenshot filename; they do not prove
|
|
209
|
-
when an answer was captured or authenticate old screenshot pixels. Hosted uploads retain a
|
|
210
|
-
separate hash of the exact uploaded file bytes.
|
|
182
|
+
The local `agentlinkops citation` commands stay in this package for existing panels.
|
|
183
|
+
AI citation measurement is not part of the AgentLinkOps offer, and the hosted service does
|
|
184
|
+
not list citation commands for customers. `agentlinkops citation help` describes the local
|
|
185
|
+
commands.
|
|
211
186
|
|
|
212
187
|
## Environment variables
|
|
213
188
|
|
|
@@ -228,13 +203,24 @@ ledger directory are still accepted with a one-line notice on stderr until 2026-
|
|
|
228
203
|
## Agent plugin
|
|
229
204
|
|
|
230
205
|
The package doubles as a plugin: `.claude-plugin/plugin.json` and `.codex-plugin/plugin.json`
|
|
231
|
-
point at
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
206
|
+
point at five product skills in `skills/`: connect, discovery, campaigns, CRM and assets.
|
|
207
|
+
`agentlinkops agent setup` keeps the connection-only default; `--skill` and `--recipe`
|
|
208
|
+
add explicit selections. `--all-skills` selects this pack's five skills. Upgrades retain
|
|
209
|
+
older optional selections and customer edits until explicit removal.
|
|
210
|
+
|
|
211
|
+
The separate optional content pack contains builder, humanizer, text cleanup,
|
|
212
|
+
internal-linking and pre-publish guidance. Its source-rights and portable-dependency
|
|
213
|
+
qualification remain unresolved; the product build preserves its
|
|
214
|
+
older published index and archives without qualifying them. When a separately qualified
|
|
215
|
+
pack is available, `--pack DIR` selects its local skills directory. An optional pack alone
|
|
216
|
+
does not configure AgentLinkOps MCP. The existing product discovery index and content
|
|
217
|
+
index paths remain unchanged.
|
|
218
|
+
|
|
219
|
+
Each standalone product archive includes its required references and Python resources.
|
|
220
|
+
Its dependency receipt declares the separately installed CLI, selected recipes and
|
|
221
|
+
customer tools. Node.js runs the CLI; Python 3.10 or later runs the optional CRM helper.
|
|
222
|
+
Inspect the installed version and help before using a local feature. Refreshing the
|
|
223
|
+
hosted command catalog does not install a newer CLI.
|
|
238
224
|
|
|
239
225
|
## What it does not do
|
|
240
226
|
|
|
@@ -69,7 +69,7 @@ export const SUPPLIERS = Object.freeze({
|
|
|
69
69
|
},
|
|
70
70
|
google_search_console: {
|
|
71
71
|
label: 'Google Search Console linking pages export',
|
|
72
|
-
source: alias('Linking page', 'Linking Page', 'Source page'),
|
|
72
|
+
source: alias('Linking page', 'Linking Page', 'Source page', 'Links', 'Source URL'),
|
|
73
73
|
target: alias('Target page', 'Target Page', 'Linked page'),
|
|
74
74
|
last_seen: alias('Last crawled', 'Last crawled date'),
|
|
75
75
|
// GSC's headline exports are COUNTS PER SITE, not placements. Importing one would mean
|
package/cli/agent-setup.js
CHANGED
|
@@ -17,13 +17,15 @@ import { parseArgs } from "./args.js";
|
|
|
17
17
|
import { ConfigError } from "./config.js";
|
|
18
18
|
import { AGENT_CLIENTS, clientById } from "../shared/agent-clients.js";
|
|
19
19
|
import { NUDGE } from "./skill.js";
|
|
20
|
+
import { STORED_KEY } from "./key-store.js";
|
|
20
21
|
|
|
21
22
|
// DP-0036-T09: `agentlinkops agent setup` (Stripe's `stripe agent setup` shape). It detects the
|
|
22
23
|
// agent clients on this machine, installs the skill pack at each client's path with digest
|
|
23
24
|
// verification, and writes the MCP entry for the view that client should use, from the one
|
|
24
|
-
// client table in shared/agent-clients.js. It stores no credential: OAuth happens in the
|
|
25
|
-
// client
|
|
26
|
-
//
|
|
25
|
+
// client table in shared/agent-clients.js. It stores no credential itself: OAuth happens in the
|
|
26
|
+
// client, and on a terminal the caller may offer to connect the CLI too (connectCli), which saves
|
|
27
|
+
// a pasted key outside the repository. It is idempotent, never overwrites a file it did not write,
|
|
28
|
+
// and prints exactly what changed. `agent status` reports what is installed without touching anything.
|
|
27
29
|
const USAGE =
|
|
28
30
|
"agent setup [--client ID ...] [--scope project|user] [--origin URL] [--pack DIR] [--skill ID ...] [--recipe ID ...] [--all-skills] [--dry-run] [--json] | agent status [--json] | agent remove [--skill ID ...] [--recipe ID ...] [--scope project|user] [--apply] [--json] | agent recover [--scope project|user] [--apply] [--json]";
|
|
29
31
|
export const DEFAULT_ORIGIN = "https://app.agentlinkops.com";
|
|
@@ -31,6 +33,7 @@ export const SERVER_NAME = "agentlinkops";
|
|
|
31
33
|
const RECEIPT = ".agentlinkops/agent-setup.json";
|
|
32
34
|
const SHARED = Symbol("package siblings");
|
|
33
35
|
const SELECTION = Symbol("selected payloads");
|
|
36
|
+
const ARTIFACT = Symbol("pack artifact contract");
|
|
34
37
|
const CORE_SKILL = "agentlinkops-connect";
|
|
35
38
|
const selectionIds = (value, flag) => {
|
|
36
39
|
const ids = value === undefined ? [] : [].concat(value);
|
|
@@ -187,6 +190,38 @@ export async function readPack(dir) {
|
|
|
187
190
|
"Skill package exceeds the 10000-file/32MiB install bound.",
|
|
188
191
|
);
|
|
189
192
|
Object.defineProperty(skills, SHARED, { value: siblings });
|
|
193
|
+
await safePath(join(dir, "..", "PACK-ARTIFACT.json"));
|
|
194
|
+
await safePath(join(dir, "..", "PACKS.json"));
|
|
195
|
+
const artifactBytes = await bytesAt(join(dir, "..", "PACK-ARTIFACT.json"));
|
|
196
|
+
const contractBytes = await bytesAt(join(dir, "..", "PACKS.json"));
|
|
197
|
+
if (artifactBytes || contractBytes) {
|
|
198
|
+
let artifact, contract;
|
|
199
|
+
try {
|
|
200
|
+
contract = JSON.parse(contractBytes);
|
|
201
|
+
// The maintained source tree has PACKS but no generated artifact receipt.
|
|
202
|
+
// Its explicit default still uses additive product selection; missing
|
|
203
|
+
// generated metadata must not restore the old prune-everything mode.
|
|
204
|
+
artifact = artifactBytes ? JSON.parse(artifactBytes) : {
|
|
205
|
+
schemaVersion:1,pack:contract.defaultPack,skills:contract.packs?.[contract.defaultPack]?.skills,
|
|
206
|
+
defaultCliSkills:contract.packs?.[contract.defaultPack]?.defaultCliSkills,
|
|
207
|
+
mcp:contract.packs?.[contract.defaultPack]?.mcp,contractSha256:sha256(JSON.stringify(contract)),
|
|
208
|
+
selectionSource:'maintained manifest; no generated artifact qualification',
|
|
209
|
+
};
|
|
210
|
+
} catch { throw new ConfigError("The selected pack metadata is unavailable or invalid."); }
|
|
211
|
+
const selected = contract?.packs?.[artifact?.pack];
|
|
212
|
+
if (!artifact || artifact.schemaVersion !== 1 || contract?.schemaVersion !== 1 || !["product", "content"].includes(artifact.pack)
|
|
213
|
+
|| !selected || selected.sourcePolicy !== (artifact.pack === "product" ? "maintained-product-source" : "reviewed-portable-source")
|
|
214
|
+
|| artifact.contractSha256 !== sha256(JSON.stringify(contract))
|
|
215
|
+
|| !Array.isArray(artifact.skills) || !Array.isArray(artifact.defaultCliSkills)
|
|
216
|
+
|| !isDeepStrictEqual(artifact.skills, selected.skills) || !isDeepStrictEqual([...artifact.skills].sort(), Object.keys(skills).sort())
|
|
217
|
+
|| !isDeepStrictEqual(artifact.defaultCliSkills, selected.defaultCliSkills) || artifact.mcp !== selected.mcp
|
|
218
|
+
|| artifact.mcp !== (artifact.pack === "product")
|
|
219
|
+
|| !isDeepStrictEqual(artifact.defaultCliSkills, artifact.pack === "product" ? [CORE_SKILL] : [])
|
|
220
|
+
|| artifact.defaultCliSkills.some(id => !Object.hasOwn(skills,id))
|
|
221
|
+
|| artifact.skills.some(id => contract.dependencies?.[id]?.unresolved?.length))
|
|
222
|
+
throw new ConfigError("The selected pack metadata disagrees with its files or source hold.");
|
|
223
|
+
Object.defineProperty(skills, ARTIFACT, {value:artifact});
|
|
224
|
+
}
|
|
190
225
|
return skills;
|
|
191
226
|
}
|
|
192
227
|
|
|
@@ -195,12 +230,20 @@ export async function readPack(dir) {
|
|
|
195
230
|
export function selectPack(pack, { skills = [], recipes = [], all = false } = {}) {
|
|
196
231
|
if (all && (skills.length || recipes.length))
|
|
197
232
|
throw new ConfigError("Choose --all-skills or named --skill/--recipe selections.");
|
|
198
|
-
|
|
233
|
+
const artifact = pack[ARTIFACT], connects = artifact?.mcp !== false;
|
|
234
|
+
if (connects && (!Object.hasOwn(pack, CORE_SKILL) || !pack[CORE_SKILL]["SKILL.md"]?.bytes.length))
|
|
199
235
|
throw new ConfigError("The core connection skill is missing from the package.");
|
|
200
|
-
if (!pack[CORE_SKILL]["references/agentlinkops.md"]?.bytes.length)
|
|
236
|
+
if (connects && !pack[CORE_SKILL]["references/agentlinkops.md"]?.bytes.length)
|
|
201
237
|
throw new ConfigError("The core connection reference is missing from the package.");
|
|
202
|
-
if (all)
|
|
203
|
-
|
|
238
|
+
if (all) {
|
|
239
|
+
// A new product-only artifact must not prune optional payloads installed by
|
|
240
|
+
// an older mixed bundle. Legacy packs retain their existing explicit mode.
|
|
241
|
+
if (artifact) Object.defineProperty(pack, SELECTION, {value:true});
|
|
242
|
+
return { pack, selection: { mode: "all", skills: Object.keys(pack), recipes: [] } };
|
|
243
|
+
}
|
|
244
|
+
const selected = new Set([...(artifact?.defaultCliSkills ?? [CORE_SKILL]), ...skills]);
|
|
245
|
+
if (!connects && (!selected.size || recipes.length))
|
|
246
|
+
throw new ConfigError("Select optional pack skills with --skill ID or --all-skills; product recipes are a separate selection.");
|
|
204
247
|
const siblings = pack[SHARED] ?? {};
|
|
205
248
|
let catalog = null;
|
|
206
249
|
if (recipes.length) {
|
|
@@ -239,6 +282,7 @@ export function selectPack(pack, { skills = [], recipes = [], all = false } = {}
|
|
|
239
282
|
}
|
|
240
283
|
Object.defineProperty(result, SHARED, { value: support });
|
|
241
284
|
Object.defineProperty(result, SELECTION, { value: true });
|
|
285
|
+
if (artifact) Object.defineProperty(result, ARTIFACT, {value:artifact});
|
|
242
286
|
return { pack: result, selection: { mode: "selected", skills: [...selected], recipes: chosenRecipes } };
|
|
243
287
|
}
|
|
244
288
|
|
|
@@ -900,6 +944,7 @@ export async function agentMain(
|
|
|
900
944
|
packDir = null,
|
|
901
945
|
now = () => new Date().toISOString(),
|
|
902
946
|
afterOperation,
|
|
947
|
+
connectCli = null,
|
|
903
948
|
} = {},
|
|
904
949
|
) {
|
|
905
950
|
const args = parseArgs(argv, ["client", "skill", "recipe"]);
|
|
@@ -1109,9 +1154,9 @@ export async function agentMain(
|
|
|
1109
1154
|
scope,
|
|
1110
1155
|
receipt,
|
|
1111
1156
|
});
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1157
|
+
const mcp = pack[ARTIFACT]?.mcp === false
|
|
1158
|
+
? {action:"not selected",file:null,url:null}
|
|
1159
|
+
: await planMcp(client, { cwd, home, origin, scope });
|
|
1115
1160
|
const entry = {
|
|
1116
1161
|
id: client.id,
|
|
1117
1162
|
name: client.name,
|
|
@@ -1130,6 +1175,19 @@ export async function agentMain(
|
|
|
1130
1175
|
...(mcp.snippet ? { snippet: mcp.snippet } : {}),
|
|
1131
1176
|
},
|
|
1132
1177
|
};
|
|
1178
|
+
// Autodetection is a convenience, not one shared permission boundary. Leave a
|
|
1179
|
+
// conflicting client's files and receipt ownership alone while installing
|
|
1180
|
+
// independent clients. An explicit multi-client selection remains atomic.
|
|
1181
|
+
if (!only && (skills.conflicts.length || mcp.action === "conflict")) {
|
|
1182
|
+
entry.skipped = true;
|
|
1183
|
+
if (mcp.action === "write") entry.mcp.action = "not applied";
|
|
1184
|
+
if (mcp.action === "conflict") report.next.push(`${client.name}: ${mcp.note}`);
|
|
1185
|
+
for (const c of skills.conflicts) report.next.push(`${client.name}: ${c.target} ${c.note}`);
|
|
1186
|
+
report.clients.push(entry);
|
|
1187
|
+
continue;
|
|
1188
|
+
}
|
|
1189
|
+
for (const path of skills.desired) desired.add(path);
|
|
1190
|
+
for (const path of skills.roots) selectedRoots.add(path);
|
|
1133
1191
|
for (const item of skills.install) {
|
|
1134
1192
|
operations.set(item.target, {
|
|
1135
1193
|
path: item.target,
|
|
@@ -1191,9 +1249,11 @@ export async function agentMain(
|
|
|
1191
1249
|
const conflicts = report.clients.some(
|
|
1192
1250
|
(c) => c.skills.conflicts.length || c.mcp.action === "conflict",
|
|
1193
1251
|
);
|
|
1252
|
+
const readyClients = report.clients.filter(c => !c.skipped);
|
|
1253
|
+
const blocked = conflicts && (Boolean(only) || readyClients.length === 0);
|
|
1194
1254
|
if (
|
|
1195
1255
|
!report.dryRun &&
|
|
1196
|
-
!
|
|
1256
|
+
!blocked &&
|
|
1197
1257
|
(operations.size || ownershipChanged || !receipt)
|
|
1198
1258
|
) {
|
|
1199
1259
|
const oldReceipt = await bytesAt(paths.receipt),
|
|
@@ -1203,7 +1263,7 @@ export async function agentMain(
|
|
|
1203
1263
|
installedAt: now(),
|
|
1204
1264
|
origin,
|
|
1205
1265
|
scope,
|
|
1206
|
-
clients:
|
|
1266
|
+
clients: readyClients.map((c) => c.id),
|
|
1207
1267
|
lastSetupSelection: selection,
|
|
1208
1268
|
files: ownedFiles,
|
|
1209
1269
|
mcp: ownedMcp,
|
|
@@ -1225,7 +1285,7 @@ export async function agentMain(
|
|
|
1225
1285
|
for (const c of report.clients)
|
|
1226
1286
|
if (c.mcp.action === "planned") c.mcp.action = "written";
|
|
1227
1287
|
}
|
|
1228
|
-
if (
|
|
1288
|
+
if (blocked) {
|
|
1229
1289
|
report.pruned = [];
|
|
1230
1290
|
report.next.push(
|
|
1231
1291
|
"Conflicts prevented all installation writes. Resolve them and retry.",
|
|
@@ -1235,14 +1295,38 @@ export async function agentMain(
|
|
|
1235
1295
|
if (c.mcp.action === "planned") c.mcp.action = "not applied";
|
|
1236
1296
|
}
|
|
1237
1297
|
}
|
|
1238
|
-
report.next.push(
|
|
1239
|
-
|
|
1298
|
+
if (conflicts && !blocked) report.next.push(
|
|
1299
|
+
report.dryRun
|
|
1300
|
+
? "Conflicting clients would be skipped; other clients can be installed."
|
|
1301
|
+
: "Conflicting clients were skipped; other clients were installed. Retry a skipped client with --client ID after resolving its conflicts.",
|
|
1302
|
+
);
|
|
1303
|
+
const connects = pack[ARTIFACT]?.mcp !== false;
|
|
1304
|
+
if (!blocked && connects) {
|
|
1305
|
+
// The plain next step: where the sign-in happens, then where to approve it.
|
|
1306
|
+
for (const c of readyClients)
|
|
1307
|
+
report.next.push(c.id === "claude-code"
|
|
1308
|
+
? `Claude Code: open it${scope === "project" ? " in this folder" : ""}, run /mcp, select ${SERVER_NAME} and choose Authenticate.`
|
|
1309
|
+
: `${c.name}: sign in when it first connects to ${SERVER_NAME}; the client opens the sign-in page.`);
|
|
1310
|
+
report.next.push(`Then approve the agent's request on the Agent page: ${origin}/#/access`);
|
|
1311
|
+
}
|
|
1312
|
+
const offerCli = connects && !json && !report.dryRun && !blocked && typeof connectCli === "function";
|
|
1313
|
+
if (connects) report.next.push(
|
|
1314
|
+
`For a chosen hosted task, sign in through the client's MCP OAuth flow. Local supplied-link checks require no AgentLinkOps account; ${offerCli
|
|
1315
|
+
? "this command stores a credential only if you paste a CLI key below."
|
|
1316
|
+
: "no credential was stored by this command."}`,
|
|
1240
1317
|
);
|
|
1318
|
+
const cliReady = connects && (["AGENTLINKOPS_API_KEY", "AGENTLINKOPS_TOKEN", "LINKTRAIL_API_KEY", "LINKTRAIL_TOKEN"].some((name) => env[name]) ||
|
|
1319
|
+
!!env[STORED_KEY]?.(origin));
|
|
1320
|
+
if (connects && !cliReady && !offerCli)
|
|
1321
|
+
report.next.push(`To use the agentlinkops CLI too, run \`npx -y agentlinkops connect\` in a terminal and paste an API key from ${origin}/#/access.`);
|
|
1241
1322
|
if (report.retainedExisting.length)
|
|
1242
1323
|
report.next.push("Earlier payloads outside this selection were retained. Preview agent remove --skill ID or --recipe ID to remove them explicitly.");
|
|
1243
|
-
report.next.push(
|
|
1244
|
-
|
|
1324
|
+
report.next.push(connects
|
|
1325
|
+
? "Select available product skills or recipes with --skill ID or --recipe ID. --all-skills installs this pack's skills; separate optional packs require --pack DIR."
|
|
1326
|
+
: "This optional selection does not configure AgentLinkOps MCP. Use --pack DIR and --skill ID or --all-skills to select its available skills.");
|
|
1327
|
+
if (connects) report.next.push(NUDGE);
|
|
1245
1328
|
out(json ? JSON.stringify(report, null, 2) : renderSetup(report));
|
|
1329
|
+
if (offerCli) await connectCli(origin);
|
|
1246
1330
|
return report.clients.some(
|
|
1247
1331
|
(c) => c.skills.conflicts.length || c.mcp.action === "conflict",
|
|
1248
1332
|
)
|