affiliate-networks-mcp 0.1.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/LICENCE +21 -0
- package/README.md +167 -0
- package/dist/cli/doctor.d.ts +42 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +74 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/setup.d.ts +35 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +254 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/cli/test.d.ts +18 -0
- package/dist/cli/test.d.ts.map +1 -0
- package/dist/cli/test.js +112 -0
- package/dist/cli/test.js.map +1 -0
- package/dist/cli/wizard/envfile.d.ts +24 -0
- package/dist/cli/wizard/envfile.d.ts.map +1 -0
- package/dist/cli/wizard/envfile.js +69 -0
- package/dist/cli/wizard/envfile.js.map +1 -0
- package/dist/cli/wizard/paths.d.ts +17 -0
- package/dist/cli/wizard/paths.d.ts.map +1 -0
- package/dist/cli/wizard/paths.js +19 -0
- package/dist/cli/wizard/paths.js.map +1 -0
- package/dist/cli/wizard/prompts.d.ts +54 -0
- package/dist/cli/wizard/prompts.d.ts.map +1 -0
- package/dist/cli/wizard/prompts.js +164 -0
- package/dist/cli/wizard/prompts.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +99 -0
- package/dist/index.js.map +1 -0
- package/dist/networks/awin/adapter.d.ts +374 -0
- package/dist/networks/awin/adapter.d.ts.map +1 -0
- package/dist/networks/awin/adapter.js +903 -0
- package/dist/networks/awin/adapter.js.map +1 -0
- package/dist/networks/awin/auth.d.ts +98 -0
- package/dist/networks/awin/auth.d.ts.map +1 -0
- package/dist/networks/awin/auth.js +209 -0
- package/dist/networks/awin/auth.js.map +1 -0
- package/dist/networks/awin/client.d.ts +65 -0
- package/dist/networks/awin/client.d.ts.map +1 -0
- package/dist/networks/awin/client.js +141 -0
- package/dist/networks/awin/client.js.map +1 -0
- package/dist/networks/awin/setup.d.ts +30 -0
- package/dist/networks/awin/setup.d.ts.map +1 -0
- package/dist/networks/awin/setup.js +55 -0
- package/dist/networks/awin/setup.js.map +1 -0
- package/dist/networks/cj/adapter.d.ts +306 -0
- package/dist/networks/cj/adapter.d.ts.map +1 -0
- package/dist/networks/cj/adapter.js +899 -0
- package/dist/networks/cj/adapter.js.map +1 -0
- package/dist/networks/cj/auth.d.ts +82 -0
- package/dist/networks/cj/auth.d.ts.map +1 -0
- package/dist/networks/cj/auth.js +190 -0
- package/dist/networks/cj/auth.js.map +1 -0
- package/dist/networks/cj/client.d.ts +130 -0
- package/dist/networks/cj/client.d.ts.map +1 -0
- package/dist/networks/cj/client.js +226 -0
- package/dist/networks/cj/client.js.map +1 -0
- package/dist/networks/cj/setup.d.ts +25 -0
- package/dist/networks/cj/setup.d.ts.map +1 -0
- package/dist/networks/cj/setup.js +51 -0
- package/dist/networks/cj/setup.js.map +1 -0
- package/dist/networks/ebay/adapter.d.ts +266 -0
- package/dist/networks/ebay/adapter.d.ts.map +1 -0
- package/dist/networks/ebay/adapter.js +797 -0
- package/dist/networks/ebay/adapter.js.map +1 -0
- package/dist/networks/ebay/auth.d.ts +101 -0
- package/dist/networks/ebay/auth.d.ts.map +1 -0
- package/dist/networks/ebay/auth.js +272 -0
- package/dist/networks/ebay/auth.js.map +1 -0
- package/dist/networks/ebay/client.d.ts +73 -0
- package/dist/networks/ebay/client.d.ts.map +1 -0
- package/dist/networks/ebay/client.js +140 -0
- package/dist/networks/ebay/client.js.map +1 -0
- package/dist/networks/ebay/setup.d.ts +21 -0
- package/dist/networks/ebay/setup.d.ts.map +1 -0
- package/dist/networks/ebay/setup.js +58 -0
- package/dist/networks/ebay/setup.js.map +1 -0
- package/dist/networks/impact/adapter.d.ts +251 -0
- package/dist/networks/impact/adapter.d.ts.map +1 -0
- package/dist/networks/impact/adapter.js +910 -0
- package/dist/networks/impact/adapter.js.map +1 -0
- package/dist/networks/impact/auth.d.ts +60 -0
- package/dist/networks/impact/auth.d.ts.map +1 -0
- package/dist/networks/impact/auth.js +171 -0
- package/dist/networks/impact/auth.js.map +1 -0
- package/dist/networks/impact/client.d.ts +69 -0
- package/dist/networks/impact/client.d.ts.map +1 -0
- package/dist/networks/impact/client.js +162 -0
- package/dist/networks/impact/client.js.map +1 -0
- package/dist/networks/impact/setup.d.ts +11 -0
- package/dist/networks/impact/setup.d.ts.map +1 -0
- package/dist/networks/impact/setup.js +37 -0
- package/dist/networks/impact/setup.js.map +1 -0
- package/dist/networks/index.d.ts +21 -0
- package/dist/networks/index.d.ts.map +1 -0
- package/dist/networks/index.js +21 -0
- package/dist/networks/index.js.map +1 -0
- package/dist/networks/rakuten/adapter.d.ts +284 -0
- package/dist/networks/rakuten/adapter.d.ts.map +1 -0
- package/dist/networks/rakuten/adapter.js +768 -0
- package/dist/networks/rakuten/adapter.js.map +1 -0
- package/dist/networks/rakuten/auth.d.ts +115 -0
- package/dist/networks/rakuten/auth.d.ts.map +1 -0
- package/dist/networks/rakuten/auth.js +301 -0
- package/dist/networks/rakuten/auth.js.map +1 -0
- package/dist/networks/rakuten/client.d.ts +78 -0
- package/dist/networks/rakuten/client.d.ts.map +1 -0
- package/dist/networks/rakuten/client.js +150 -0
- package/dist/networks/rakuten/client.js.map +1 -0
- package/dist/networks/rakuten/setup.d.ts +24 -0
- package/dist/networks/rakuten/setup.d.ts.map +1 -0
- package/dist/networks/rakuten/setup.js +60 -0
- package/dist/networks/rakuten/setup.js.map +1 -0
- package/dist/server.d.ts +18 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +100 -0
- package/dist/server.js.map +1 -0
- package/dist/shared/config.d.ts +63 -0
- package/dist/shared/config.d.ts.map +1 -0
- package/dist/shared/config.js +135 -0
- package/dist/shared/config.js.map +1 -0
- package/dist/shared/diagnostic.d.ts +47 -0
- package/dist/shared/diagnostic.d.ts.map +1 -0
- package/dist/shared/diagnostic.js +120 -0
- package/dist/shared/diagnostic.js.map +1 -0
- package/dist/shared/errors.d.ts +41 -0
- package/dist/shared/errors.d.ts.map +1 -0
- package/dist/shared/errors.js +94 -0
- package/dist/shared/errors.js.map +1 -0
- package/dist/shared/logging.d.ts +15 -0
- package/dist/shared/logging.d.ts.map +1 -0
- package/dist/shared/logging.js +56 -0
- package/dist/shared/logging.js.map +1 -0
- package/dist/shared/registry.d.ts +14 -0
- package/dist/shared/registry.d.ts.map +1 -0
- package/dist/shared/registry.js +25 -0
- package/dist/shared/registry.js.map +1 -0
- package/dist/shared/resilience.d.ts +52 -0
- package/dist/shared/resilience.d.ts.map +1 -0
- package/dist/shared/resilience.js +235 -0
- package/dist/shared/resilience.js.map +1 -0
- package/dist/shared/types.d.ts +258 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +27 -0
- package/dist/shared/types.js.map +1 -0
- package/dist/tools/generate.d.ts +28 -0
- package/dist/tools/generate.d.ts.map +1 -0
- package/dist/tools/generate.js +202 -0
- package/dist/tools/generate.js.map +1 -0
- package/package.json +49 -0
- package/src/skills/affiliate-earnings-report/SKILL.md +62 -0
- package/src/skills/affiliate-earnings-report/examples/last-30-days.md +37 -0
- package/src/skills/affiliate-network-setup-help/SKILL.md +65 -0
- package/src/skills/affiliate-network-setup-help/examples/awin-walkthrough.md +19 -0
- package/src/skills/affiliate-network-status/SKILL.md +69 -0
- package/src/skills/affiliate-network-status/examples/mixed-health.md +21 -0
- package/src/skills/audit-affiliate-links/SKILL.md +67 -0
- package/src/skills/audit-affiliate-links/examples/sitemap-audit.md +24 -0
- package/templates/new-network/README.md +53 -0
- package/templates/new-network/adapter.ts +406 -0
- package/templates/new-network/auth.ts +53 -0
- package/templates/new-network/client.ts +52 -0
- package/templates/new-network/network.json +40 -0
- package/templates/new-network/setup.ts +47 -0
- package/templates/new-network/tests/fixtures/.gitkeep +0 -0
package/LICENCE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Robert Berrisford
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# affiliate-mcp
|
|
2
|
+
|
|
3
|
+
> An MCP server for affiliate networks. Bring your own keys.
|
|
4
|
+
|
|
5
|
+
**Status:** pre-launch. The five bundled adapters ship as `claim_status: partial` (or `experimental`, for the most recent addition) until they have been exercised against real publisher accounts.
|
|
6
|
+
|
|
7
|
+
## What this is
|
|
8
|
+
|
|
9
|
+
`affiliate-mcp` is a Model Context Protocol server that exposes affiliate
|
|
10
|
+
network APIs as MCP tools. It bundles five networks — Awin, CJ Affiliate,
|
|
11
|
+
eBay Partner Network, Impact, and Rakuten Advertising — so a publisher can ask
|
|
12
|
+
an MCP client (Claude Desktop, Claude Code, others) questions like "which
|
|
13
|
+
programmes are still pending after 90 days?" across all of them without
|
|
14
|
+
opening five dashboards.
|
|
15
|
+
|
|
16
|
+
The server runs locally. There is no hosted service, no account, and no
|
|
17
|
+
telemetry. Credentials live in `~/.affiliate-mcp/.env` at file mode `0600`
|
|
18
|
+
and never leave your host. You bring your own publisher keys for each
|
|
19
|
+
network you want wired in.
|
|
20
|
+
|
|
21
|
+
For per-network capability, known limitations, and the editorial baseline,
|
|
22
|
+
see [`REPORT.md`](./REPORT.md). It is regenerated from each adapter's
|
|
23
|
+
`network.json` and findings docs, so it stays in step with the code.
|
|
24
|
+
|
|
25
|
+
## Quick-start
|
|
26
|
+
|
|
27
|
+
Requires Node.js 20 or newer.
|
|
28
|
+
|
|
29
|
+
Run the interactive setup wizard:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
npx affiliate-networks-mcp setup
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The wizard walks one network at a time, validates each credential against the
|
|
36
|
+
live API as you enter it, and writes the configuration to
|
|
37
|
+
`~/.affiliate-mcp/.env` at file mode `0600`.
|
|
38
|
+
|
|
39
|
+
Check that everything is wired up:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
npx affiliate-networks-mcp test
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Then point your MCP client at the server. A sample Claude Desktop config
|
|
46
|
+
lives at [`examples/claude-desktop-config.json`](./examples/claude-desktop-config.json)
|
|
47
|
+
(with notes at [`examples/claude-desktop-config.md`](./examples/claude-desktop-config.md)):
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"mcpServers": {
|
|
52
|
+
"affiliate": {
|
|
53
|
+
"command": "npx",
|
|
54
|
+
"args": ["affiliate-networks-mcp"]
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Restart your client. The configured networks appear as tool calls prefixed
|
|
61
|
+
`affiliate_<network>_…`.
|
|
62
|
+
|
|
63
|
+
## Networks
|
|
64
|
+
|
|
65
|
+
<!-- AFFILIATE_MCP_NETWORK_TABLE_START -->
|
|
66
|
+
| Network | Setup time | Approval required | Supported ops | Notes |
|
|
67
|
+
| --- | ---: | --- | ---: | --- |
|
|
68
|
+
| Awin | 5 min | no | 6 / 7 | no clicks |
|
|
69
|
+
| CJ Affiliate | 8 min | no | 6 / 7 | no clicks |
|
|
70
|
+
| eBay Partner Network | 10 min | yes (~3 days) | 7 / 7 | see notes |
|
|
71
|
+
| Impact | 6 min | no | 7 / 7 | upstream variability |
|
|
72
|
+
| Rakuten Advertising | 12 min | yes (~5 days) | 6 / 7 | clicks gated |
|
|
73
|
+
<!-- AFFILIATE_MCP_NETWORK_TABLE_END -->
|
|
74
|
+
|
|
75
|
+
The table above is regenerated from each adapter's `network.json` by
|
|
76
|
+
`npm run generate:readme`. For a per-network breakdown — operation-level
|
|
77
|
+
support, latency, known limitations, and full findings prose — see
|
|
78
|
+
[`REPORT.md`](./REPORT.md).
|
|
79
|
+
|
|
80
|
+
## Per-network setup
|
|
81
|
+
|
|
82
|
+
Each bundled network has a short setup document covering dashboard navigation,
|
|
83
|
+
credential locations, and common stumbling blocks:
|
|
84
|
+
|
|
85
|
+
- [Awin](./docs/networks/awin.md) — API token + publisher ID.
|
|
86
|
+
- [CJ Affiliate](./docs/networks/cj.md) — Developer Key (GraphQL).
|
|
87
|
+
- [eBay Partner Network](./docs/networks/ebay.md) — OAuth client + secret + campaign ID; approval required.
|
|
88
|
+
- [Impact](./docs/networks/impact.md) — Account SID + Auth Token.
|
|
89
|
+
- [Rakuten Advertising](./docs/networks/rakuten.md) — OAuth client + SID; approval required.
|
|
90
|
+
|
|
91
|
+
## Tool surface
|
|
92
|
+
|
|
93
|
+
Each registered network exposes the seven canonical publisher operations as
|
|
94
|
+
MCP tools, named `affiliate_<network>_<snake_case_op>`:
|
|
95
|
+
|
|
96
|
+
- `list_programmes`, `get_programme` — programmes (joined or available).
|
|
97
|
+
- `list_transactions`, `get_earnings_summary` — transactions and aggregates.
|
|
98
|
+
- `list_clicks` — click-level data, where the network exposes it.
|
|
99
|
+
- `generate_tracking_link` — mint or construct a deeplink.
|
|
100
|
+
- `verify_auth` — confirm credentials and surface the publisher identity.
|
|
101
|
+
|
|
102
|
+
Two meta tools are always present: `affiliate_list_networks` and
|
|
103
|
+
`affiliate_run_diagnostic`. They let a client enumerate the active adapters
|
|
104
|
+
and check live capabilities in a single call.
|
|
105
|
+
|
|
106
|
+
## Skills
|
|
107
|
+
|
|
108
|
+
Four packaged skills wrap common workflows so the client picks the right
|
|
109
|
+
tools without you naming them. Each lives under `src/skills/<name>/`:
|
|
110
|
+
|
|
111
|
+
- [`affiliate-earnings-report`](./src/skills/affiliate-earnings-report/SKILL.md)
|
|
112
|
+
— consolidated period earnings across every configured network.
|
|
113
|
+
- [`affiliate-network-status`](./src/skills/affiliate-network-status/SKILL.md)
|
|
114
|
+
— health check: auth, reachability, supported operations.
|
|
115
|
+
- [`affiliate-network-setup-help`](./src/skills/affiliate-network-setup-help/SKILL.md)
|
|
116
|
+
— guides the user through setup for a specific network.
|
|
117
|
+
- [`audit-affiliate-links`](./src/skills/audit-affiliate-links/SKILL.md)
|
|
118
|
+
— checks that affiliate links on a page or sitemap still resolve to active
|
|
119
|
+
programmes.
|
|
120
|
+
|
|
121
|
+
## Status report
|
|
122
|
+
|
|
123
|
+
[`REPORT.md`](./REPORT.md) is the editorial position: per-network capability,
|
|
124
|
+
known limitations, and where the upstream API surprised us. Regenerated on
|
|
125
|
+
every adapter merge. Treat it as the source of truth before opening an issue.
|
|
126
|
+
|
|
127
|
+
## For developers
|
|
128
|
+
|
|
129
|
+
Contributions are welcome — especially new network adapters. Start with
|
|
130
|
+
[`CONTRIBUTING.md`](./CONTRIBUTING.md) for the human-side workflow, then read
|
|
131
|
+
[`AGENTS.md`](./AGENTS.md) (the primer for AI coding agents — file layout,
|
|
132
|
+
conventions, "what not to do") and `.claude/skills/contribute/SKILL.md` (the
|
|
133
|
+
playbook a Claude Code session loads automatically when you open this repo).
|
|
134
|
+
[`templates/new-network/`](./templates/new-network/) is the scaffold to copy.
|
|
135
|
+
[`WANTED.md`](./WANTED.md) lists networks and ideas explicitly on the
|
|
136
|
+
roadmap, and [`REPORT.md`](./REPORT.md) is the editorial baseline for any
|
|
137
|
+
new claim about a network's API.
|
|
138
|
+
|
|
139
|
+
Local development:
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
npm install
|
|
143
|
+
npm test
|
|
144
|
+
npm run typecheck
|
|
145
|
+
npm run lint
|
|
146
|
+
npm run build
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Generators:
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
npm run generate:report # writes REPORT.md
|
|
153
|
+
npm run generate:readme # updates the table block in this README
|
|
154
|
+
npm run generate:report-image # renders the summary table as a PNG (needs Playwright)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Licence
|
|
158
|
+
|
|
159
|
+
MIT. See [`LICENCE`](./LICENCE).
|
|
160
|
+
|
|
161
|
+
## Acknowledgements
|
|
162
|
+
|
|
163
|
+
This project is only possible because the engineering teams at Awin, CJ
|
|
164
|
+
Affiliate, eBay Partner Network, Impact, and Rakuten Advertising publish
|
|
165
|
+
public, documented APIs for their publisher data. The adapters here read
|
|
166
|
+
those APIs; they do not scrape, simulate, or work around any rate or access
|
|
167
|
+
limits.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `affiliate-mcp doctor [slug]` — verbose diagnostic.
|
|
3
|
+
*
|
|
4
|
+
* Same data as `test` but JSON, plus environment context (Node version,
|
|
5
|
+
* platform, config path + presence, masked variable list, per-operation
|
|
6
|
+
* resilience config). Designed for users to paste into a GitHub issue.
|
|
7
|
+
*
|
|
8
|
+
* Critical: variable VALUES must never be printed. Only NAMES and presence.
|
|
9
|
+
* The Pino redactor protects logs; this surface enforces the same rule
|
|
10
|
+
* explicitly because it dumps JSON to stdout for human consumption.
|
|
11
|
+
*/
|
|
12
|
+
import { runDiagnostic } from '../shared/diagnostic.js';
|
|
13
|
+
import type { ResilienceConfigMap } from '../shared/types.js';
|
|
14
|
+
export interface DoctorOptions {
|
|
15
|
+
slug?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface DoctorReport {
|
|
18
|
+
generatedAt: string;
|
|
19
|
+
environment: {
|
|
20
|
+
nodeVersion: string;
|
|
21
|
+
platform: string;
|
|
22
|
+
arch: string;
|
|
23
|
+
affiliateMcpVersion: string;
|
|
24
|
+
};
|
|
25
|
+
config: {
|
|
26
|
+
path: string;
|
|
27
|
+
present: boolean;
|
|
28
|
+
/** Names only — never values. */
|
|
29
|
+
keys: string[];
|
|
30
|
+
};
|
|
31
|
+
adapters: Array<{
|
|
32
|
+
slug: string;
|
|
33
|
+
name: string;
|
|
34
|
+
claimStatus: string;
|
|
35
|
+
knownLimitations: string[];
|
|
36
|
+
resilience: ResilienceConfigMap;
|
|
37
|
+
}>;
|
|
38
|
+
diagnostic: Awaited<ReturnType<typeof runDiagnostic>>;
|
|
39
|
+
}
|
|
40
|
+
export declare function buildReport(opts?: DoctorOptions): Promise<DoctorReport>;
|
|
41
|
+
export declare function runDoctor(opts?: DoctorOptions): Promise<number>;
|
|
42
|
+
//# sourceMappingURL=doctor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAIxD,OAAO,KAAK,EAAkB,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAM9E,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE;QACX,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,mBAAmB,EAAE,MAAM,CAAC;KAC7B,CAAC;IACF,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,OAAO,CAAC;QACjB,iCAAiC;QACjC,IAAI,EAAE,MAAM,EAAE,CAAC;KAChB,CAAC;IACF,QAAQ,EAAE,KAAK,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,gBAAgB,EAAE,MAAM,EAAE,CAAC;QAC3B,UAAU,EAAE,mBAAmB,CAAC;KACjC,CAAC,CAAC;IACH,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC,CAAC;CACvD;AAED,wBAAsB,WAAW,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAyCjF;AAED,wBAAsB,SAAS,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAQzE"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `affiliate-mcp doctor [slug]` — verbose diagnostic.
|
|
3
|
+
*
|
|
4
|
+
* Same data as `test` but JSON, plus environment context (Node version,
|
|
5
|
+
* platform, config path + presence, masked variable list, per-operation
|
|
6
|
+
* resilience config). Designed for users to paste into a GitHub issue.
|
|
7
|
+
*
|
|
8
|
+
* Critical: variable VALUES must never be printed. Only NAMES and presence.
|
|
9
|
+
* The Pino redactor protects logs; this surface enforces the same rule
|
|
10
|
+
* explicitly because it dumps JSON to stdout for human consumption.
|
|
11
|
+
*/
|
|
12
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
13
|
+
import { runDiagnostic } from '../shared/diagnostic.js';
|
|
14
|
+
import { getAdapter, getAdapters } from '../shared/registry.js';
|
|
15
|
+
import { resolveConfigPaths } from './wizard/paths.js';
|
|
16
|
+
import { parseEnvFile } from '../shared/config.js';
|
|
17
|
+
function out(line) {
|
|
18
|
+
process.stdout.write(line.endsWith('\n') ? line : `${line}\n`);
|
|
19
|
+
}
|
|
20
|
+
export async function buildReport(opts = {}) {
|
|
21
|
+
const paths = resolveConfigPaths();
|
|
22
|
+
const present = existsSync(paths.envFile);
|
|
23
|
+
let keys = [];
|
|
24
|
+
if (present) {
|
|
25
|
+
try {
|
|
26
|
+
const text = readFileSync(paths.envFile, 'utf8');
|
|
27
|
+
keys = Object.keys(parseEnvFile(text)).sort();
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
keys = [];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const targets = opts.slug
|
|
34
|
+
? [getAdapter(opts.slug)].filter((a) => Boolean(a))
|
|
35
|
+
: getAdapters();
|
|
36
|
+
const diagnostic = await runDiagnostic(opts.slug);
|
|
37
|
+
return {
|
|
38
|
+
generatedAt: new Date().toISOString(),
|
|
39
|
+
environment: {
|
|
40
|
+
nodeVersion: process.version,
|
|
41
|
+
platform: process.platform,
|
|
42
|
+
arch: process.arch,
|
|
43
|
+
affiliateMcpVersion: readPackageVersion(),
|
|
44
|
+
},
|
|
45
|
+
config: {
|
|
46
|
+
path: paths.envFile,
|
|
47
|
+
present,
|
|
48
|
+
keys,
|
|
49
|
+
},
|
|
50
|
+
adapters: targets.map((a) => ({
|
|
51
|
+
slug: a.slug,
|
|
52
|
+
name: a.name,
|
|
53
|
+
claimStatus: a.meta.claimStatus,
|
|
54
|
+
knownLimitations: a.meta.knownLimitations,
|
|
55
|
+
resilience: a.resilienceConfig,
|
|
56
|
+
})),
|
|
57
|
+
diagnostic,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export async function runDoctor(opts = {}) {
|
|
61
|
+
const report = await buildReport(opts);
|
|
62
|
+
out(JSON.stringify(report, null, 2));
|
|
63
|
+
// Exit code: 0 if every diagnostic result has capabilities (no error) and
|
|
64
|
+
// every supported op is actually supported; 1 otherwise.
|
|
65
|
+
const anyError = report.diagnostic.results.some((r) => r.error);
|
|
66
|
+
return anyError ? 1 : 0;
|
|
67
|
+
}
|
|
68
|
+
function readPackageVersion() {
|
|
69
|
+
// Best-effort. We don't import package.json directly because the rootDir
|
|
70
|
+
// is `src` and the build would not copy it into dist. Use AFFILIATE_MCP_VERSION
|
|
71
|
+
// if the launcher set it; otherwise "unknown".
|
|
72
|
+
return process.env['AFFILIATE_MCP_VERSION'] ?? 'unknown';
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `affiliate-mcp setup` — interactive setup wizard.
|
|
3
|
+
*
|
|
4
|
+
* Per PRD §4.3, setup is part of the product, not a side activity. Every
|
|
5
|
+
* surface here is user-facing copy. Errors name the network, name the field,
|
|
6
|
+
* and surface the verbatim underlying reason. No retries hidden, no silent
|
|
7
|
+
* fallbacks.
|
|
8
|
+
*
|
|
9
|
+
* Flow:
|
|
10
|
+
* 1. Detect first-run vs existing config. First-run jumps straight into
|
|
11
|
+
* network selection; existing config offers setup / reset / add / quit.
|
|
12
|
+
* 2. Pick networks (multi-select).
|
|
13
|
+
* 3. For each picked network: walk `setupSteps()`, prompt per step, call
|
|
14
|
+
* `validateCredential` on entry where requested, run `verifyAuth()` at
|
|
15
|
+
* the end and merge any `derivedValues` from the underlying result.
|
|
16
|
+
* 4. Write `~/.affiliate-mcp/.env` (or `$AFFILIATE_MCP_CONFIG_DIR/.env`),
|
|
17
|
+
* mode 0600. Reset replaces; add merges.
|
|
18
|
+
* 5. Print the absolute path written and a pointer to `affiliate-mcp test`.
|
|
19
|
+
*
|
|
20
|
+
* Output channel: this module writes to STDOUT because the wizard is an
|
|
21
|
+
* interactive surface — the JSON-RPC protocol is not active during `setup`.
|
|
22
|
+
* The shared Pino logger continues to write to stderr only.
|
|
23
|
+
*/
|
|
24
|
+
import { type Prompter } from './wizard/prompts.js';
|
|
25
|
+
export interface SetupOptions {
|
|
26
|
+
/** Override the prompter (tests). Falls back to the shared singleton. */
|
|
27
|
+
prompter?: Prompter;
|
|
28
|
+
/**
|
|
29
|
+
* When true, skip the interactive top-level menu and go straight into
|
|
30
|
+
* configuration. Used by tests and by future flags like `--add`.
|
|
31
|
+
*/
|
|
32
|
+
mode?: 'auto' | 'setup' | 'reset' | 'add';
|
|
33
|
+
}
|
|
34
|
+
export declare function runSetup(opts?: SetupOptions): Promise<number>;
|
|
35
|
+
//# sourceMappingURL=setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/cli/setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAMH,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAsBjE,MAAM,WAAW,YAAY;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,CAAC;CAC3C;AAED,wBAAsB,QAAQ,CAAC,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CA+EvE"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `affiliate-mcp setup` — interactive setup wizard.
|
|
3
|
+
*
|
|
4
|
+
* Per PRD §4.3, setup is part of the product, not a side activity. Every
|
|
5
|
+
* surface here is user-facing copy. Errors name the network, name the field,
|
|
6
|
+
* and surface the verbatim underlying reason. No retries hidden, no silent
|
|
7
|
+
* fallbacks.
|
|
8
|
+
*
|
|
9
|
+
* Flow:
|
|
10
|
+
* 1. Detect first-run vs existing config. First-run jumps straight into
|
|
11
|
+
* network selection; existing config offers setup / reset / add / quit.
|
|
12
|
+
* 2. Pick networks (multi-select).
|
|
13
|
+
* 3. For each picked network: walk `setupSteps()`, prompt per step, call
|
|
14
|
+
* `validateCredential` on entry where requested, run `verifyAuth()` at
|
|
15
|
+
* the end and merge any `derivedValues` from the underlying result.
|
|
16
|
+
* 4. Write `~/.affiliate-mcp/.env` (or `$AFFILIATE_MCP_CONFIG_DIR/.env`),
|
|
17
|
+
* mode 0600. Reset replaces; add merges.
|
|
18
|
+
* 5. Print the absolute path written and a pointer to `affiliate-mcp test`.
|
|
19
|
+
*
|
|
20
|
+
* Output channel: this module writes to STDOUT because the wizard is an
|
|
21
|
+
* interactive surface — the JSON-RPC protocol is not active during `setup`.
|
|
22
|
+
* The shared Pino logger continues to write to stderr only.
|
|
23
|
+
*/
|
|
24
|
+
import { existsSync } from 'node:fs';
|
|
25
|
+
import { getAdapters } from '../shared/registry.js';
|
|
26
|
+
import { getPrompter } from './wizard/prompts.js';
|
|
27
|
+
import { resolveConfigPaths } from './wizard/paths.js';
|
|
28
|
+
import { filterOutKeys, mergeEnv, readEnv, writeEnv } from './wizard/envfile.js';
|
|
29
|
+
function out(line = '') {
|
|
30
|
+
process.stdout.write(line.endsWith('\n') ? line : `${line}\n`);
|
|
31
|
+
}
|
|
32
|
+
function banner() {
|
|
33
|
+
out('');
|
|
34
|
+
out(' affiliate-mcp — setup wizard');
|
|
35
|
+
out(' ----------------------------');
|
|
36
|
+
out(' This wizard collects API credentials for each affiliate network you want');
|
|
37
|
+
out(' to use and writes them to a single config file. Tokens are validated as');
|
|
38
|
+
out(' you enter them so problems surface immediately, not at first call.');
|
|
39
|
+
out('');
|
|
40
|
+
}
|
|
41
|
+
export async function runSetup(opts = {}) {
|
|
42
|
+
const prompter = opts.prompter ?? getPrompter();
|
|
43
|
+
const paths = resolveConfigPaths();
|
|
44
|
+
const hasConfig = existsSync(paths.envFile);
|
|
45
|
+
banner();
|
|
46
|
+
// Decide the top-level action.
|
|
47
|
+
let mode = opts.mode ?? 'auto';
|
|
48
|
+
if (mode === 'auto') {
|
|
49
|
+
if (!hasConfig) {
|
|
50
|
+
out(`No config file at ${paths.envFile}.`);
|
|
51
|
+
out('');
|
|
52
|
+
mode = 'setup';
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
out(`Existing config at ${paths.envFile}.`);
|
|
56
|
+
const choice = await prompter.menu('What would you like to do?', [
|
|
57
|
+
{ key: 'add', label: 'add — configure an additional network' },
|
|
58
|
+
{ key: 'reset', label: 'reset — wipe and start over' },
|
|
59
|
+
{ key: 'quit', label: 'quit — exit without changes' },
|
|
60
|
+
]);
|
|
61
|
+
if (choice === 'quit') {
|
|
62
|
+
out('No changes made.');
|
|
63
|
+
return 0;
|
|
64
|
+
}
|
|
65
|
+
mode = choice;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const adapters = getAdapters();
|
|
69
|
+
if (adapters.length === 0) {
|
|
70
|
+
out('No network adapters are registered. This is a build problem, not a config one.');
|
|
71
|
+
out('Reinstall affiliate-mcp or report this at https://github.com/atolls/affiliate-mcp.');
|
|
72
|
+
return 1;
|
|
73
|
+
}
|
|
74
|
+
// Network picker.
|
|
75
|
+
const selected = await pickNetworks(prompter, adapters);
|
|
76
|
+
if (selected.length === 0) {
|
|
77
|
+
out('No networks selected. No changes made.');
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
80
|
+
// Existing env — read once. Reset drops the chosen networks' keys; add keeps
|
|
81
|
+
// every existing entry.
|
|
82
|
+
let existing = readEnv(paths.envFile);
|
|
83
|
+
if (mode === 'reset') {
|
|
84
|
+
const drop = [];
|
|
85
|
+
for (const adapter of selected) {
|
|
86
|
+
for (const step of adapter.setupSteps())
|
|
87
|
+
drop.push(step.field);
|
|
88
|
+
}
|
|
89
|
+
existing = filterOutKeys(existing, drop);
|
|
90
|
+
}
|
|
91
|
+
// Walk each network.
|
|
92
|
+
const newEntries = {};
|
|
93
|
+
for (const adapter of selected) {
|
|
94
|
+
out('');
|
|
95
|
+
out(`Configuring ${adapter.name} (${adapter.slug})`);
|
|
96
|
+
out('-'.repeat(`Configuring ${adapter.name} (${adapter.slug})`.length));
|
|
97
|
+
if (adapter.meta.setupRequiresApproval) {
|
|
98
|
+
const days = adapter.meta.setupApprovalDaysTypical ?? 0;
|
|
99
|
+
out(`Note: ${adapter.name} requires partner approval before API access is granted.` +
|
|
100
|
+
(days > 0 ? ` Typical turnaround is ~${days} business days.` : ''));
|
|
101
|
+
}
|
|
102
|
+
const captured = await runNetworkSetup(prompter, adapter);
|
|
103
|
+
Object.assign(newEntries, captured);
|
|
104
|
+
}
|
|
105
|
+
// Merge and write.
|
|
106
|
+
const merged = mergeEnv(existing, newEntries);
|
|
107
|
+
writeEnv(paths.envFile, merged);
|
|
108
|
+
out('');
|
|
109
|
+
out(`Wrote ${paths.envFile}.`);
|
|
110
|
+
out('You are set up. Test with `affiliate-mcp test`.');
|
|
111
|
+
return 0;
|
|
112
|
+
}
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
// Network picker
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
async function pickNetworks(prompter, adapters) {
|
|
117
|
+
const choices = adapters.map((a) => {
|
|
118
|
+
const minutes = a.meta.setupTimeEstimateMinutes;
|
|
119
|
+
const approval = a.meta.setupRequiresApproval ? ', approval required' : '';
|
|
120
|
+
return {
|
|
121
|
+
key: a.slug,
|
|
122
|
+
label: `${a.name} — ~${minutes} min${approval}`,
|
|
123
|
+
};
|
|
124
|
+
});
|
|
125
|
+
const slugs = await prompter.selectMany('Which networks would you like to configure?', choices);
|
|
126
|
+
const set = new Set(slugs);
|
|
127
|
+
return adapters.filter((a) => set.has(a.slug));
|
|
128
|
+
}
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
// Per-network walk
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
/**
|
|
133
|
+
* Drive a single network adapter through its steps. Returns the captured
|
|
134
|
+
* field → value map. Calls `verifyAuth()` at the end and merges any
|
|
135
|
+
* `derivedValues` from the underlying result.
|
|
136
|
+
*/
|
|
137
|
+
async function runNetworkSetup(prompter, adapter) {
|
|
138
|
+
const captured = {};
|
|
139
|
+
const steps = adapter.setupSteps();
|
|
140
|
+
for (const step of steps) {
|
|
141
|
+
const value = await runStep(prompter, adapter, step, captured);
|
|
142
|
+
if (value === null) {
|
|
143
|
+
out(`Skipped ${step.field}. You can edit it later in the config file.`);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
captured[step.field] = value;
|
|
147
|
+
// Stash into process.env so the subsequent verifyAuth() picks it up.
|
|
148
|
+
process.env[step.field] = value;
|
|
149
|
+
}
|
|
150
|
+
// End-to-end auth verify + derived values merge.
|
|
151
|
+
out('');
|
|
152
|
+
out(`Verifying ${adapter.name} credentials…`);
|
|
153
|
+
try {
|
|
154
|
+
const result = await adapter.verifyAuth();
|
|
155
|
+
if (result.ok) {
|
|
156
|
+
const id = 'identity' in result && result.identity ? ` (${result.identity})` : '';
|
|
157
|
+
out(`Verified${id}.`);
|
|
158
|
+
// Duck-type derivedValues — the public adapter interface does not
|
|
159
|
+
// expose them, but the underlying auth module returns them. See the
|
|
160
|
+
// wizard handoff for why this is an Option B duck-type rather than an
|
|
161
|
+
// interface change.
|
|
162
|
+
const derived = result
|
|
163
|
+
.derivedValues;
|
|
164
|
+
if (derived) {
|
|
165
|
+
for (const [k, v] of Object.entries(derived)) {
|
|
166
|
+
if (typeof v !== 'string' || v === '')
|
|
167
|
+
continue;
|
|
168
|
+
if (captured[k] && captured[k] === v)
|
|
169
|
+
continue;
|
|
170
|
+
captured[k] = v;
|
|
171
|
+
process.env[k] = v;
|
|
172
|
+
out(`Derived ${k} = ${v} from your credentials (no manual entry needed).`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
out(`${adapter.name} verifyAuth failed: ${result.reason}`);
|
|
178
|
+
out('Credentials saved anyway so you can edit and re-run `affiliate-mcp test`.');
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
183
|
+
out(`${adapter.name} verifyAuth raised an error: ${msg}`);
|
|
184
|
+
out('Credentials saved anyway so you can edit and re-run `affiliate-mcp test`.');
|
|
185
|
+
}
|
|
186
|
+
return captured;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Run a single step: render description verbatim, prompt for input, call
|
|
190
|
+
* `validateOnEntry` if present, offer retry/edit/skip on failure.
|
|
191
|
+
*
|
|
192
|
+
* Returns the captured value, or null if the user skipped.
|
|
193
|
+
*/
|
|
194
|
+
async function runStep(prompter, adapter, step, _captured) {
|
|
195
|
+
out('');
|
|
196
|
+
// Verbatim description — written by the network's adapter author.
|
|
197
|
+
for (const line of step.description.split('\n'))
|
|
198
|
+
out(line);
|
|
199
|
+
for (;;) {
|
|
200
|
+
const value = await promptForStep(prompter, step);
|
|
201
|
+
if (value === '') {
|
|
202
|
+
out(`${step.field} is empty. Skipping is allowed; you can add it later.`);
|
|
203
|
+
const skip = await prompter.confirm('Skip this field?');
|
|
204
|
+
if (skip)
|
|
205
|
+
return null;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
// validateOnEntry — if defined, run it and surface the verbatim reason.
|
|
209
|
+
if (step.validateOnEntry) {
|
|
210
|
+
let result;
|
|
211
|
+
try {
|
|
212
|
+
result = await step.validateOnEntry(value);
|
|
213
|
+
}
|
|
214
|
+
catch (err) {
|
|
215
|
+
result = {
|
|
216
|
+
ok: false,
|
|
217
|
+
message: err instanceof Error ? err.message : String(err),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
if (!result.ok) {
|
|
221
|
+
// PRD §4.1: name the network, name the field, surface the reason.
|
|
222
|
+
out(`${adapter.name} rejected ${step.field}: ${result.message ?? 'invalid value'}`);
|
|
223
|
+
if (result.hint)
|
|
224
|
+
out(`Hint: ${result.hint}`);
|
|
225
|
+
const next = await prompter.menu('What next?', [
|
|
226
|
+
{ key: 'retry', label: 'retry — enter again' },
|
|
227
|
+
{ key: 'skip', label: 'skip — leave blank for now' },
|
|
228
|
+
]);
|
|
229
|
+
if (next === 'skip')
|
|
230
|
+
return null;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
if (result.message)
|
|
234
|
+
out(`OK: ${result.message}`);
|
|
235
|
+
}
|
|
236
|
+
return value;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
async function promptForStep(prompter, step) {
|
|
240
|
+
const label = step.label;
|
|
241
|
+
if (step.type === 'password') {
|
|
242
|
+
return await prompter.password(label);
|
|
243
|
+
}
|
|
244
|
+
if (step.type === 'number') {
|
|
245
|
+
const example = step.example;
|
|
246
|
+
const n = await prompter.number(label, example ? { example } : undefined);
|
|
247
|
+
return String(n);
|
|
248
|
+
}
|
|
249
|
+
const opts = {};
|
|
250
|
+
if (step.example)
|
|
251
|
+
opts.example = step.example;
|
|
252
|
+
return await prompter.text(label, opts);
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/cli/setup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `affiliate-mcp test [slug]` — friendly diagnostic.
|
|
3
|
+
*
|
|
4
|
+
* Drives `runDiagnostic` and prints a human-readable summary. Designed for the
|
|
5
|
+
* common "is it working?" question — no JSON, no stack traces, just one line
|
|
6
|
+
* per network plus an extra line per failing operation.
|
|
7
|
+
*
|
|
8
|
+
* Output is to stdout (user-facing CLI text on an interactive surface). Pino
|
|
9
|
+
* still logs to stderr.
|
|
10
|
+
*/
|
|
11
|
+
import { type DiagnosticResult } from '../shared/diagnostic.js';
|
|
12
|
+
export interface TestOptions {
|
|
13
|
+
slug?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function runTest(opts?: TestOptions): Promise<number>;
|
|
16
|
+
/** Exposed for tests so they can assert the formatter output directly. */
|
|
17
|
+
export declare function formatDiagnostic(result: DiagnosticResult): string;
|
|
18
|
+
//# sourceMappingURL=test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../src/cli/test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAiB,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAO/E,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,OAAO,CAAC,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BrE;AA6DD,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAgBjE"}
|