cpyany 0.2.5 → 0.2.6
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/README.md +17 -17
- package/index.mjs +71 -80
- package/package.json +8 -14
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# cpyany
|
|
2
2
|
|
|
3
|
-
One-command installer for
|
|
3
|
+
One-command installer for cpyany, an MCP for coding agents that copy websites,
|
|
4
|
+
pages, and individual elements from reference sites.
|
|
4
5
|
|
|
5
6
|
```bash
|
|
6
7
|
npx cpyany setup
|
|
@@ -8,25 +9,25 @@ npx cpyany setup
|
|
|
8
9
|
|
|
9
10
|
Auto-detects every supported AI client installed on your machine (Claude Code,
|
|
10
11
|
Claude Desktop, Cursor), opens a browser to authorize, and writes the resulting
|
|
11
|
-
bearer token into each client's MCP config from a single OAuth flow.
|
|
12
|
-
|
|
12
|
+
bearer token into each client's MCP config from a single OAuth flow.
|
|
13
|
+
|
|
14
|
+
After setup, ask your coding agent to copy from a reference:
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
Use cpyany to copy the hero section from https://example.com into this app.
|
|
18
|
+
```
|
|
13
19
|
|
|
14
20
|
## Install paths
|
|
15
21
|
|
|
16
22
|
| Client | MCP config we patch | Rule + skill we install | Auto-detected via |
|
|
17
23
|
|---|---|---|---|
|
|
18
|
-
| Claude Code | runs `claude mcp add --scope user --transport http …` (writes `~/.claude.json`) | `~/.claude/rules/
|
|
24
|
+
| Claude Code | runs `claude mcp add --scope user --transport http …` (writes `~/.claude.json`) | `~/.claude/rules/cpyany.md` + `~/.claude/skills/cpyany/SKILL.md` | `claude` on `PATH` |
|
|
19
25
|
| Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` (mac) / `%APPDATA%\Claude\claude_desktop_config.json` (win) — bridged via `mcp-remote` | _(none — Claude Desktop has no rules/skills mechanism)_ | the per-platform Claude config dir exists |
|
|
20
|
-
| Cursor | `~/.cursor/mcp.json` | `~/.cursor/rules/
|
|
26
|
+
| Cursor | `~/.cursor/mcp.json` | `~/.cursor/rules/cpyany.mdc` (`alwaysApply: true`) + `~/.cursor/skills/cpyany/SKILL.md` | `~/.cursor` exists |
|
|
21
27
|
|
|
22
|
-
Guidance is two-tier
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
files or collects a human test. Installs that predate 0.1.0 (which appended a
|
|
26
|
-
marker block to `~/.claude/CLAUDE.md`) are migrated automatically on any CLI
|
|
27
|
-
invocation. `cpyany remove` cleans everything up. The published package
|
|
28
|
-
also keeps `copyanything` and `pinghumans` as legacy binary aliases during
|
|
29
|
-
the rename.
|
|
28
|
+
Guidance is two-tier: a short always-loaded rule telling the agent when to use
|
|
29
|
+
cpyany, and a deeper `cpyany` skill with the copy-from-reference workflow.
|
|
30
|
+
`cpyany remove` cleans everything up.
|
|
30
31
|
|
|
31
32
|
## Restrict to one client
|
|
32
33
|
|
|
@@ -39,8 +40,7 @@ npx cpyany setup --client claude-code
|
|
|
39
40
|
|
|
40
41
|
## Skip the browser
|
|
41
42
|
|
|
42
|
-
If you'd rather paste the snippet manually,
|
|
43
|
-
[pinghumans.com/dashboard](https://pinghumans.com/dashboard) and copy the
|
|
43
|
+
If you'd rather paste the snippet manually, open the app dashboard and copy the
|
|
44
44
|
config block from the **MCP integration** section.
|
|
45
45
|
|
|
46
46
|
## Remove
|
|
@@ -52,5 +52,5 @@ npx cpyany remove --client claude-desktop # just one
|
|
|
52
52
|
|
|
53
53
|
## License
|
|
54
54
|
|
|
55
|
-
UNLICENSED — the CLI is published for ease of installation, but the
|
|
56
|
-
|
|
55
|
+
UNLICENSED — the CLI is published for ease of installation, but the service
|
|
56
|
+
source is not open-licensed.
|
package/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
2
|
+
// cpyany — install the cpyany MCP server in your AI client.
|
|
3
3
|
// One command: shows a one-time code, you approve it in the browser
|
|
4
4
|
// (RFC 8628 device flow), your client configs get patched, you're done.
|
|
5
5
|
|
|
@@ -11,7 +11,7 @@ import { execFile } from "node:child_process";
|
|
|
11
11
|
import { promisify } from "node:util";
|
|
12
12
|
import open from "open";
|
|
13
13
|
|
|
14
|
-
const VERSION = "0.2.
|
|
14
|
+
const VERSION = "0.2.6";
|
|
15
15
|
const execFileP = promisify(execFile);
|
|
16
16
|
const APP_URL = process.env.PINGHUMANS_APP_URL ?? "https://pinghumans.com";
|
|
17
17
|
// Hoisted with the other top-of-module consts — the entry try-block runs
|
|
@@ -87,82 +87,67 @@ const RULES_MARKER_START =
|
|
|
87
87
|
const RULES_MARKER_END = "<!-- pinghumans-mcp:end -->";
|
|
88
88
|
|
|
89
89
|
// Two-tier guidance (Context7-style):
|
|
90
|
-
// RULE_BODY — short, always-loaded: WHEN to reach for
|
|
90
|
+
// RULE_BODY — short, always-loaded: WHEN to reach for cpyany.
|
|
91
91
|
// SKILL_BODY — full workflow, loaded on demand when the agent does QA.
|
|
92
|
-
const RULE_BODY = `Use
|
|
92
|
+
const RULE_BODY = `Use cpyany when the user asks you to copy, clone, recreate, or match a website, page, section, component, or visual element from a reference URL, screenshot, or existing site.
|
|
93
93
|
|
|
94
|
-
Do not use for:
|
|
94
|
+
Do not use for: unrelated implementation work, backend-only tasks, or generic UI work with no reference to copy from.
|
|
95
95
|
|
|
96
96
|
## Steps
|
|
97
97
|
|
|
98
|
-
1.
|
|
99
|
-
2.
|
|
100
|
-
3.
|
|
101
|
-
4.
|
|
102
|
-
5. For the full
|
|
98
|
+
1. Identify the reference URL, screenshot, or specific element/section the user wants copied.
|
|
99
|
+
2. Preserve the target app's framework and style conventions; copy the look and behavior, not the reference site's codebase wholesale.
|
|
100
|
+
3. Match layout, spacing, typography, color, imagery, responsive behavior, and interaction states as closely as practical.
|
|
101
|
+
4. Verify against the reference with browser inspection or screenshots before reporting completion.
|
|
102
|
+
5. For the full copy-from-reference workflow, use the cpyany skill.
|
|
103
103
|
`;
|
|
104
104
|
|
|
105
105
|
const SKILL_BODY = `---
|
|
106
|
-
name:
|
|
107
|
-
description:
|
|
106
|
+
name: cpyany
|
|
107
|
+
description: Use this skill when the user asks to copy, clone, recreate, or match a website, landing page, section, component, or visual element from a reference site, URL, screenshot, or design inspiration.
|
|
108
108
|
---
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
cpyany helps coding agents recreate a reference site's visual design in the current codebase. The goal is to copy the user's requested look, layout, and interaction in one pass while still fitting the target app's framework and component conventions.
|
|
111
111
|
|
|
112
112
|
## When to Use This Skill
|
|
113
113
|
|
|
114
|
-
-
|
|
115
|
-
-
|
|
116
|
-
-
|
|
117
|
-
-
|
|
118
|
-
-
|
|
114
|
+
- "Copy this homepage/hero/pricing card/nav from <url>"
|
|
115
|
+
- "Make this section look like the reference"
|
|
116
|
+
- "Clone this element into my app"
|
|
117
|
+
- "Use this screenshot/site as visual inspiration and build it here"
|
|
118
|
+
- "Match the spacing, typography, and styling of this page"
|
|
119
119
|
|
|
120
|
-
##
|
|
120
|
+
## Workflow
|
|
121
121
|
|
|
122
|
-
### Step 1:
|
|
122
|
+
### Step 1: Understand the copy target
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
Clarify whether the user wants a whole page, a section, a single component, or only a style direction. If they give a URL, inspect the exact target. If they give a screenshot, use it as the visual source of truth.
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
<script src="https://pinghumans.com/qa-toolbar.js"></script>
|
|
128
|
-
\`\`\`
|
|
126
|
+
### Step 2: Recreate in the target codebase
|
|
129
127
|
|
|
130
|
-
|
|
128
|
+
Use existing project components, styling systems, tokens, and routing patterns. Prefer copying the visual outcome rather than importing unrelated abstractions from the reference. Preserve accessibility and responsive behavior.
|
|
131
129
|
|
|
132
|
-
### Step
|
|
130
|
+
### Step 3: Match the visible details
|
|
133
131
|
|
|
134
|
-
|
|
132
|
+
Pay close attention to:
|
|
135
133
|
|
|
136
|
-
-
|
|
137
|
-
-
|
|
138
|
-
-
|
|
139
|
-
-
|
|
134
|
+
- spacing and alignment
|
|
135
|
+
- typography scale and weight
|
|
136
|
+
- color, gradients, borders, shadows, and blur
|
|
137
|
+
- imagery, icon treatment, and decorative elements
|
|
138
|
+
- hover/focus states and animation feel
|
|
139
|
+
- desktop and mobile layout
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
### Step 4: Verify
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
### Step 3: Wait the right way
|
|
146
|
-
|
|
147
|
-
The call does NOT block (QA takes minutes) — it returns \`status='pending'\` immediately. In order of preference:
|
|
148
|
-
|
|
149
|
-
1. **File early, keep working.** The human tests in parallel — their time is free to you.
|
|
150
|
-
2. **Interactive session with background shell tasks:** launch \`npx cpyany wait <ping_id>\` in the background — it exits the moment results land, waking you with the results.
|
|
151
|
-
3. **One-shot run (subagent / final answer due this run):** do NOT end your run while results are pending — nothing can wake you afterwards, and a backgrounded waiter dies with you. Run \`npx cpyany wait <ping_id>\` in the FOREGROUND, or call \`wait_for_results(ping_id)\` in a loop until complete.
|
|
152
|
-
4. **Checkpoint checks:** \`get_test_results(ping_id)\` at natural pauses.
|
|
153
|
-
5. **Genuinely blocked:** \`wait_for_results(ping_id)\` blocks server-side (~45s per call, free) and returns early on news. Never hand-roll sleep loops.
|
|
154
|
-
|
|
155
|
-
### Step 4: Act on the results
|
|
156
|
-
|
|
157
|
-
Each result has a verdict, notes, pinned comments, per-step truth, and a screenshot URL — open and look at it. Comments carry CSS selectors that point at the exact component to change. Treat the human's verdict/comments as acceptance criteria, not suggestions. Browser, Chrome, screenshots, source checks, and getComputedStyle are implementation aids: use them to satisfy and confirm the worker's requested changes, never to decide the worker was wrong.
|
|
158
|
-
|
|
159
|
-
**Clone-compare tasks** (filed with both \`url\` = the original and \`draft_url\` = your clone): before you touch the clone, call \`check_og_source(ping_id)\`. It reads the ORIGINAL's authored HTML source for exactly the elements a human flagged and diffs each against your clone. The worker's alignment feedback is the acceptance criteria: modify HTML/CSS so every requested move/match/delete is met with reasonable visual accuracy. Allow small manual error in hand-placed move/align markers, but preserve the worker's intended direction and target. Use the returned authored classes to implement the feedback; only fall back to measuring/live inspection for pairs marked \`needs_live_inspection\`, and only to satisfy the human request, not to replace it with your own visual judgment.
|
|
143
|
+
Run the app, inspect the result in a browser, and compare it to the reference. Iterate until the copied result is recognizably faithful.
|
|
160
144
|
|
|
161
145
|
## Guidelines
|
|
162
146
|
|
|
163
|
-
-
|
|
164
|
-
-
|
|
165
|
-
-
|
|
147
|
+
- Do not claim pixel perfection unless you verified it.
|
|
148
|
+
- Do not copy private assets or secrets from the reference.
|
|
149
|
+
- Do not add heavy dependencies just to mimic a small visual effect.
|
|
150
|
+
- When the reference and target app conflict, preserve the target app's architecture and recreate the look locally.
|
|
166
151
|
`;
|
|
167
152
|
|
|
168
153
|
// ─── Entry ────────────────────────────────────────────────────────────────
|
|
@@ -280,7 +265,7 @@ async function setup(client) {
|
|
|
280
265
|
track("setup_complete", { client_label: t });
|
|
281
266
|
}
|
|
282
267
|
|
|
283
|
-
console.log(`\n${pc.green("✔")}
|
|
268
|
+
console.log(`\n${pc.green("✔")} cpyany setup complete\n`);
|
|
284
269
|
for (const { client: c, entries } of summary) {
|
|
285
270
|
console.log(` ${pc.bold(prettyClient(c))}`);
|
|
286
271
|
for (const e of entries) {
|
|
@@ -301,7 +286,7 @@ async function setup(client) {
|
|
|
301
286
|
if (restartList) {
|
|
302
287
|
console.log(`\nRestart ${restartList} to load the MCP server.`);
|
|
303
288
|
}
|
|
304
|
-
console.log(`Try it: ask your agent to "use
|
|
289
|
+
console.log(`Try it: ask your agent to "use cpyany to copy the hero section from a reference site."`);
|
|
305
290
|
}
|
|
306
291
|
|
|
307
292
|
async function remove(client) {
|
|
@@ -319,7 +304,7 @@ async function remove(client) {
|
|
|
319
304
|
}
|
|
320
305
|
for (const t of targets) {
|
|
321
306
|
await unpatchConfig(t);
|
|
322
|
-
console.log(`✓ Removed
|
|
307
|
+
console.log(`✓ Removed cpyany from ${prettyClient(t)} config.`);
|
|
323
308
|
await unpatchRules(t);
|
|
324
309
|
track("remove", { client_label: t });
|
|
325
310
|
}
|
|
@@ -494,7 +479,7 @@ async function performDeviceLogin() {
|
|
|
494
479
|
const linkLine = `${pc.dim("Open this link to approve:")}\n${pc.cyan(authorization.verification_uri_complete)}\n\n${pc.dim("Or visit")} ${pc.cyan(authorization.verification_uri)} ${pc.dim("and enter the code above.")}`;
|
|
495
480
|
console.log(
|
|
496
481
|
boxen(`${codeLine}\n\n${linkLine}`, {
|
|
497
|
-
title: "Sign in to
|
|
482
|
+
title: "Sign in to cpyany",
|
|
498
483
|
titleAlignment: "left",
|
|
499
484
|
padding: 1,
|
|
500
485
|
margin: { top: 1, bottom: 1, left: 2, right: 2 },
|
|
@@ -607,7 +592,8 @@ async function patchConfig(client, token) {
|
|
|
607
592
|
// Claude Desktop doesn't natively support Streamable HTTP MCP servers
|
|
608
593
|
// (only stdio). Bridge via the community mcp-remote package, which spawns
|
|
609
594
|
// a stdio server that proxies to our hosted HTTP MCP.
|
|
610
|
-
config.mcpServers.pinghumans
|
|
595
|
+
delete config.mcpServers.pinghumans;
|
|
596
|
+
config.mcpServers.cpyany = {
|
|
611
597
|
command: "npx",
|
|
612
598
|
args: [
|
|
613
599
|
"-y",
|
|
@@ -619,7 +605,8 @@ async function patchConfig(client, token) {
|
|
|
619
605
|
};
|
|
620
606
|
} else {
|
|
621
607
|
// Cursor + others: native streamable-HTTP works.
|
|
622
|
-
config.mcpServers.pinghumans
|
|
608
|
+
delete config.mcpServers.pinghumans;
|
|
609
|
+
config.mcpServers.cpyany = {
|
|
623
610
|
url: `${APP_URL}/api/mcp`,
|
|
624
611
|
headers: { Authorization: `Bearer ${token}` },
|
|
625
612
|
};
|
|
@@ -630,18 +617,16 @@ async function patchConfig(client, token) {
|
|
|
630
617
|
|
|
631
618
|
async function unpatchConfig(client) {
|
|
632
619
|
if (client === "claude-code") {
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
} catch (err) {
|
|
636
|
-
console.error(`Couldn't run \`claude mcp remove\`: ${err.message}`);
|
|
637
|
-
}
|
|
620
|
+
await execFileP("claude", ["mcp", "remove", "cpyany", "--scope", "user"]).catch(() => {});
|
|
621
|
+
await execFileP("claude", ["mcp", "remove", "pinghumans", "--scope", "user"]).catch(() => {});
|
|
638
622
|
return;
|
|
639
623
|
}
|
|
640
624
|
const path = configPath(client);
|
|
641
625
|
try {
|
|
642
626
|
const text = await readFile(path, "utf8");
|
|
643
627
|
const config = JSON.parse(text);
|
|
644
|
-
if (config?.mcpServers?.pinghumans) {
|
|
628
|
+
if (config?.mcpServers?.cpyany || config?.mcpServers?.pinghumans) {
|
|
629
|
+
delete config.mcpServers.cpyany;
|
|
645
630
|
delete config.mcpServers.pinghumans;
|
|
646
631
|
await writeFile(path, JSON.stringify(config, null, 2) + "\n");
|
|
647
632
|
}
|
|
@@ -653,7 +638,7 @@ async function unpatchConfig(client) {
|
|
|
653
638
|
// ─── Agent rules ──────────────────────────────────────────────────────────
|
|
654
639
|
//
|
|
655
640
|
// In addition to MCP tool descriptions, we install a short prose "rule"
|
|
656
|
-
// telling the agent WHEN to reach for
|
|
641
|
+
// telling the agent WHEN to reach for cpyany. Tool descriptions only
|
|
657
642
|
// fire during tool selection; rules sit in the agent's persistent
|
|
658
643
|
// instructions and bias it toward the tool earlier in reasoning.
|
|
659
644
|
//
|
|
@@ -661,7 +646,7 @@ async function unpatchConfig(client) {
|
|
|
661
646
|
// - Claude Code → ~/.claude/CLAUDE.md (the canonical user-memory file
|
|
662
647
|
// Claude Code auto-loads at session start). Marker tags so re-install
|
|
663
648
|
// and remove are clean even if the user hand-edits the file.
|
|
664
|
-
// - Cursor → ~/.cursor/rules/
|
|
649
|
+
// - Cursor → ~/.cursor/rules/cpyany.mdc (Cursor's auto-loaded
|
|
665
650
|
// rules dir, with `alwaysApply: true` frontmatter).
|
|
666
651
|
// - Claude Desktop → no equivalent; skipped.
|
|
667
652
|
//
|
|
@@ -686,17 +671,17 @@ function stripMarkerBlock(text) {
|
|
|
686
671
|
// mechanism, so it gets MCP config only.
|
|
687
672
|
function rulePath(client) {
|
|
688
673
|
if (client === "claude-code")
|
|
689
|
-
return join(homedir(), ".claude", "rules", "
|
|
674
|
+
return join(homedir(), ".claude", "rules", "cpyany.md");
|
|
690
675
|
if (client === "cursor")
|
|
691
|
-
return join(homedir(), ".cursor", "rules", "
|
|
676
|
+
return join(homedir(), ".cursor", "rules", "cpyany.mdc");
|
|
692
677
|
return null;
|
|
693
678
|
}
|
|
694
679
|
|
|
695
680
|
function skillPath(client) {
|
|
696
681
|
if (client === "claude-code")
|
|
697
|
-
return join(homedir(), ".claude", "skills", "
|
|
682
|
+
return join(homedir(), ".claude", "skills", "cpyany", "SKILL.md");
|
|
698
683
|
if (client === "cursor")
|
|
699
|
-
return join(homedir(), ".cursor", "skills", "
|
|
684
|
+
return join(homedir(), ".cursor", "skills", "cpyany", "SKILL.md");
|
|
700
685
|
return null;
|
|
701
686
|
}
|
|
702
687
|
|
|
@@ -779,7 +764,7 @@ async function refreshStaleRules({ verbose = false } = {}) {
|
|
|
779
764
|
}
|
|
780
765
|
|
|
781
766
|
if (updated.length > 0) {
|
|
782
|
-
console.log(`↻ Refreshed
|
|
767
|
+
console.log(`↻ Refreshed cpyany agent rules (v${VERSION}):`);
|
|
783
768
|
for (const p of updated) console.log(` ${p}`);
|
|
784
769
|
track("rules_refresh");
|
|
785
770
|
} else if (verbose) {
|
|
@@ -812,6 +797,13 @@ async function patchClaudeCodeViaCli(token) {
|
|
|
812
797
|
// It writes to ~/.claude.json (or similar) for us.
|
|
813
798
|
try {
|
|
814
799
|
// Remove any existing config first, otherwise `mcp add` errors on dupes.
|
|
800
|
+
await execFileP("claude", [
|
|
801
|
+
"mcp",
|
|
802
|
+
"remove",
|
|
803
|
+
"cpyany",
|
|
804
|
+
"--scope",
|
|
805
|
+
"user",
|
|
806
|
+
]).catch(() => {});
|
|
815
807
|
await execFileP("claude", [
|
|
816
808
|
"mcp",
|
|
817
809
|
"remove",
|
|
@@ -829,7 +821,7 @@ async function patchClaudeCodeViaCli(token) {
|
|
|
829
821
|
"user",
|
|
830
822
|
"--transport",
|
|
831
823
|
"http",
|
|
832
|
-
"
|
|
824
|
+
"cpyany",
|
|
833
825
|
`${APP_URL}/api/mcp`,
|
|
834
826
|
"--header",
|
|
835
827
|
`Authorization: Bearer ${token}`,
|
|
@@ -872,7 +864,7 @@ function prettyClient(client) {
|
|
|
872
864
|
}
|
|
873
865
|
|
|
874
866
|
function printHelp() {
|
|
875
|
-
console.log(`cpyany — install the
|
|
867
|
+
console.log(`cpyany — install the cpyany MCP server in your AI client.
|
|
876
868
|
|
|
877
869
|
Usage:
|
|
878
870
|
cpyany setup [--client claude-code|claude-desktop|cursor]
|
|
@@ -881,7 +873,7 @@ Usage:
|
|
|
881
873
|
# block until a human test has results (exit 0 = news,
|
|
882
874
|
# 2 = timed out). Run it in the background after filing
|
|
883
875
|
# a test so your agent gets woken when results land.
|
|
884
|
-
cpyany whoami # show which
|
|
876
|
+
cpyany whoami # show which account this machine's token belongs to
|
|
885
877
|
cpyany rules # refresh the installed agent rules to this version
|
|
886
878
|
cpyany version
|
|
887
879
|
|
|
@@ -893,7 +885,6 @@ Setup opens your browser to ${APP_URL}/cli-auth, generates a fresh bearer
|
|
|
893
885
|
token after you sign in, and writes it into each client's MCP config.
|
|
894
886
|
|
|
895
887
|
Aliases: --claude (= --claude-desktop), --code (= --claude-code).
|
|
896
|
-
Legacy binary alias: pinghumans.
|
|
897
888
|
`);
|
|
898
889
|
}
|
|
899
890
|
|
|
@@ -911,7 +902,7 @@ Legacy binary alias: pinghumans.
|
|
|
911
902
|
// Hoisted as a function — the entry dispatch runs before bottom-of-module
|
|
912
903
|
// consts initialize (the 0.0.4 TDZ lesson, again).
|
|
913
904
|
function credsPath() {
|
|
914
|
-
return join(homedir(), ".config", "
|
|
905
|
+
return join(homedir(), ".config", "cpyany", "credentials.json");
|
|
915
906
|
}
|
|
916
907
|
|
|
917
908
|
async function readCreds() {
|
|
@@ -983,7 +974,7 @@ async function validateStoredToken() {
|
|
|
983
974
|
const warn = (s) => (pc ? pc.yellow(s) : s);
|
|
984
975
|
console.error(
|
|
985
976
|
"\n" +
|
|
986
|
-
warn("⚠ Your
|
|
977
|
+
warn("⚠ Your cpyany token is no longer valid") +
|
|
987
978
|
" (revoked, or the account was deleted).\n" +
|
|
988
979
|
" Re-link this machine: " +
|
|
989
980
|
(pc ? pc.cyan("npx cpyany setup") : "npx cpyany setup") +
|
|
@@ -1007,7 +998,7 @@ async function resolveLocalToken() {
|
|
|
1007
998
|
for (const p of candidates) {
|
|
1008
999
|
try {
|
|
1009
1000
|
const cfg = JSON.parse(await readFile(p, "utf8"));
|
|
1010
|
-
const entry = cfg?.mcpServers?.pinghumans;
|
|
1001
|
+
const entry = cfg?.mcpServers?.cpyany ?? cfg?.mcpServers?.pinghumans;
|
|
1011
1002
|
const header = entry?.headers?.Authorization ?? entry?.headers?.authorization;
|
|
1012
1003
|
const m = /Bearer\s+(\S+)/.exec(header ?? "");
|
|
1013
1004
|
if (m) return m[1];
|
|
@@ -1027,7 +1018,7 @@ async function waitForResultsCli(rest) {
|
|
|
1027
1018
|
const token = await resolveLocalToken();
|
|
1028
1019
|
if (!token) {
|
|
1029
1020
|
throw new Error(
|
|
1030
|
-
"No
|
|
1021
|
+
"No cpyany token found. Run `npx cpyany setup` first."
|
|
1031
1022
|
);
|
|
1032
1023
|
}
|
|
1033
1024
|
|
|
@@ -1061,7 +1052,7 @@ async function waitForResultsCli(rest) {
|
|
|
1061
1052
|
|
|
1062
1053
|
if (status === "not_found") {
|
|
1063
1054
|
throw new Error(
|
|
1064
|
-
"Ping not found for this account. Results are asker-scoped — `wait` only works on tests filed with the same
|
|
1055
|
+
"Ping not found for this account. Results are asker-scoped — `wait` only works on tests filed with the same cpyany account."
|
|
1065
1056
|
);
|
|
1066
1057
|
}
|
|
1067
1058
|
if (status !== "pending" || received > baseline) {
|
package/package.json
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cpyany",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Install
|
|
3
|
+
"version": "0.2.6",
|
|
4
|
+
"description": "Install cpyany so coding agents can copy websites, pages, and elements from references.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.mjs",
|
|
7
7
|
"bin": {
|
|
8
|
-
"cpyany": "index.mjs"
|
|
9
|
-
"copyanything": "index.mjs",
|
|
10
|
-
"pinghumans": "index.mjs"
|
|
8
|
+
"cpyany": "index.mjs"
|
|
11
9
|
},
|
|
12
10
|
"files": [
|
|
13
11
|
"index.mjs",
|
|
@@ -26,12 +24,6 @@
|
|
|
26
24
|
"ora": "^9.4.0",
|
|
27
25
|
"picocolors": "^1.1.1"
|
|
28
26
|
},
|
|
29
|
-
"homepage": "https://pinghumans.com",
|
|
30
|
-
"repository": {
|
|
31
|
-
"type": "git",
|
|
32
|
-
"url": "git+https://github.com/alex-durango/pinghumans.git",
|
|
33
|
-
"directory": "cli"
|
|
34
|
-
},
|
|
35
27
|
"license": "UNLICENSED",
|
|
36
28
|
"private": false,
|
|
37
29
|
"publishConfig": {
|
|
@@ -43,9 +35,11 @@
|
|
|
43
35
|
"claude",
|
|
44
36
|
"cursor",
|
|
45
37
|
"ai",
|
|
46
|
-
"humans",
|
|
47
38
|
"cpyany",
|
|
48
|
-
"
|
|
49
|
-
"
|
|
39
|
+
"copy",
|
|
40
|
+
"website",
|
|
41
|
+
"design",
|
|
42
|
+
"reference",
|
|
43
|
+
"clone"
|
|
50
44
|
]
|
|
51
45
|
}
|