@relvehq/agent-ready 0.3.0 → 0.4.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/README.md +24 -3
- package/SETUP.md +354 -354
- package/dist/core/report.d.ts +9 -3
- package/dist/core/report.js +30 -10
- package/dist/core/version.d.ts +1 -0
- package/dist/core/version.js +8 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +5 -1
- package/dist/refine/check.d.ts +3 -1
- package/dist/refine/check.js +43 -0
- package/dist/refine/remote.d.ts +3 -2
- package/dist/refine/remote.js +3 -2
- package/dist/wp/relve-agent-ready/includes/class-relve-ar-admin.php +560 -550
- package/dist/wp/relve-agent-ready/includes/class-relve-ar-converter.php +762 -762
- package/dist/wp/relve-agent-ready/includes/class-relve-ar-llms.php +192 -192
- package/dist/wp/relve-agent-ready/includes/class-relve-ar-markdown-builder.php +455 -455
- package/dist/wp/relve-agent-ready/includes/class-relve-ar-negotiation.php +334 -334
- package/dist/wp/relve-agent-ready/includes/class-relve-ar-refiner.php +894 -894
- package/dist/wp/relve-agent-ready/includes/class-relve-ar-reporter.php +147 -0
- package/dist/wp/relve-agent-ready/includes/class-relve-ar-response.php +226 -226
- package/dist/wp/relve-agent-ready/includes/class-relve-ar-router.php +422 -421
- package/dist/wp/relve-agent-ready/includes/class-relve-ar-settings.php +210 -201
- package/dist/wp/relve-agent-ready/includes/class-relve-ar-store.php +583 -583
- package/dist/wp/relve-agent-ready/includes/index.php +2 -2
- package/dist/wp/relve-agent-ready/index.php +2 -2
- package/dist/wp/relve-agent-ready/readme.txt +119 -108
- package/dist/wp/relve-agent-ready/relve-agent-ready.php +82 -81
- package/dist/wp/relve-agent-ready/uninstall.php +35 -35
- package/package.json +1 -1
|
@@ -1,192 +1,192 @@
|
|
|
1
|
-
<?php
|
|
2
|
-
/**
|
|
3
|
-
* llms.txt (https://llmstxt.org): "# Site name", "> tagline", then sections
|
|
4
|
-
* of links to the `.md` URLs of recent posts and pages (up to 200).
|
|
5
|
-
*
|
|
6
|
-
* Port of @relvehq/agent-ready core/llms.ts (buildLlmsTxt) plus the WordPress
|
|
7
|
-
* side: which posts to list and whether this plugin should answer at all
|
|
8
|
-
* (mode auto|on|off; auto stays off when Yoast serves its own llms.txt or a
|
|
9
|
-
* physical llms.txt file exists).
|
|
10
|
-
*
|
|
11
|
-
* @package Relve_Agent_Ready
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
if ( ! defined( 'ABSPATH' ) ) {
|
|
15
|
-
exit;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* llms.txt builder and mode decision.
|
|
20
|
-
*/
|
|
21
|
-
class Relve_AR_Llms {
|
|
22
|
-
|
|
23
|
-
/** Max links listed. */
|
|
24
|
-
const MAX_ENTRIES = 200;
|
|
25
|
-
|
|
26
|
-
// ── pure helpers (unit-tested) ────────────────────────────────────────
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Should the plugin answer llms.txt?
|
|
30
|
-
*
|
|
31
|
-
* @param string $mode auto|on|off.
|
|
32
|
-
* @param bool $yoast_serves Yoast's llms.txt feature is on.
|
|
33
|
-
* @param bool $physical_file A real llms.txt file sits in the web root.
|
|
34
|
-
* @return bool
|
|
35
|
-
*/
|
|
36
|
-
public static function should_serve( $mode, $yoast_serves, $physical_file = false ) {
|
|
37
|
-
if ( 'on' === $mode ) {
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
if ( 'off' === $mode ) {
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
return ! $yoast_serves && ! $physical_file;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Build the file.
|
|
48
|
-
*
|
|
49
|
-
* @param string $title Site name.
|
|
50
|
-
* @param string $summary Tagline.
|
|
51
|
-
* @param array $sections Section name => list of { title, url, description }.
|
|
52
|
-
* @return string
|
|
53
|
-
*/
|
|
54
|
-
public static function build( $title, $summary, $sections ) {
|
|
55
|
-
$title = self::one_line( $title );
|
|
56
|
-
$out = array( '# ' . ( '' === $title ? 'Site' : $title ) );
|
|
57
|
-
$sum = self::one_line( $summary );
|
|
58
|
-
if ( '' !== $sum ) {
|
|
59
|
-
$out[] = '> ' . $sum;
|
|
60
|
-
}
|
|
61
|
-
foreach ( $sections as $name => $entries ) {
|
|
62
|
-
$lines = array();
|
|
63
|
-
foreach ( $entries as $e ) {
|
|
64
|
-
if ( empty( $e['title'] ) || empty( $e['url'] ) ) {
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
$t = str_replace( array( '[', ']' ), '', self::one_line( $e['title'] ) );
|
|
68
|
-
$desc = isset( $e['description'] ) ? self::one_line( $e['description'] ) : '';
|
|
69
|
-
$lines[] = '- [' . $t . '](' . Relve_AR_Converter::url_for_markdown( $e['url'] ) . ')' . ( '' !== $desc ? ': ' . $desc : '' );
|
|
70
|
-
}
|
|
71
|
-
if ( $lines ) {
|
|
72
|
-
$out[] = '## ' . self::one_line( $name );
|
|
73
|
-
$out[] = implode( "\n", $lines );
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return implode( "\n\n", $out ) . "\n";
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* The `.md` URL of a permalink: "https://s/a/b/" -> "https://s/a/b.md";
|
|
81
|
-
* the home URL -> ".../index.md"; a permalink with a query string (plain
|
|
82
|
-
* permalinks) gets "format=md" instead.
|
|
83
|
-
*
|
|
84
|
-
* @param string $permalink Permalink.
|
|
85
|
-
* @param string $home_url Home URL.
|
|
86
|
-
* @return string
|
|
87
|
-
*/
|
|
88
|
-
public static function md_url( $permalink, $home_url ) {
|
|
89
|
-
$permalink = explode( '#', (string) $permalink, 2 )[0];
|
|
90
|
-
if ( false !== strpos( $permalink, '?' ) ) {
|
|
91
|
-
return $permalink . '&format=md';
|
|
92
|
-
}
|
|
93
|
-
if ( rtrim( $permalink, '/' ) === rtrim( (string) $home_url, '/' ) ) {
|
|
94
|
-
return rtrim( $permalink, '/' ) . '/index.md';
|
|
95
|
-
}
|
|
96
|
-
return rtrim( $permalink, '/' ) . '.md';
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Collapse whitespace.
|
|
101
|
-
*
|
|
102
|
-
* @param string $s Text.
|
|
103
|
-
* @return string
|
|
104
|
-
*/
|
|
105
|
-
private static function one_line( $s ) {
|
|
106
|
-
return Relve_AR_Markdown_Builder::one_line( (string) $s );
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// ── WordPress-bound ───────────────────────────────────────────────────
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Does Yoast SEO serve llms.txt on this site? Checked defensively: Yoast
|
|
113
|
-
* active and its "enable_llms_txt" option on. Unsure means no.
|
|
114
|
-
*
|
|
115
|
-
* @return bool
|
|
116
|
-
*/
|
|
117
|
-
public static function yoast_serves() {
|
|
118
|
-
$serves = false;
|
|
119
|
-
if ( defined( 'WPSEO_VERSION' ) || class_exists( 'WPSEO_Options' ) ) {
|
|
120
|
-
$opt = get_option( 'wpseo' );
|
|
121
|
-
$serves = is_array( $opt ) && ! empty( $opt['enable_llms_txt'] );
|
|
122
|
-
}
|
|
123
|
-
return (bool) apply_filters( 'relve_agent_ready_yoast_serves_llms', $serves );
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Is there a physical llms.txt in the WordPress root (the web server
|
|
128
|
-
* would answer it before WordPress anyway)?
|
|
129
|
-
*
|
|
130
|
-
* @return bool
|
|
131
|
-
*/
|
|
132
|
-
public static function physical_file_exists() {
|
|
133
|
-
return defined( 'ABSPATH' ) && file_exists( ABSPATH . 'llms.txt' );
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Build this site's llms.txt.
|
|
138
|
-
*
|
|
139
|
-
* @param array $settings Plugin settings.
|
|
140
|
-
* @return string
|
|
141
|
-
*/
|
|
142
|
-
public static function generate( $settings ) {
|
|
143
|
-
$home = home_url( '/' );
|
|
144
|
-
$types = array_values( array_intersect( (array) $settings['post_types'], get_post_types( array( 'public' => true ) ) ) );
|
|
145
|
-
$sections = array();
|
|
146
|
-
$left = self::MAX_ENTRIES;
|
|
147
|
-
$home_path = Relve_AR_Router::home_path();
|
|
148
|
-
foreach ( $types as $type ) {
|
|
149
|
-
if ( $left <= 0 ) {
|
|
150
|
-
break;
|
|
151
|
-
}
|
|
152
|
-
$obj = get_post_type_object( $type );
|
|
153
|
-
$name = $obj && isset( $obj->labels->name ) ? $obj->labels->name : $type;
|
|
154
|
-
$query = new WP_Query(
|
|
155
|
-
array(
|
|
156
|
-
'post_type' => $type,
|
|
157
|
-
'post_status' => 'publish',
|
|
158
|
-
'has_password' => false,
|
|
159
|
-
'posts_per_page' => $left,
|
|
160
|
-
'orderby' => 'post' === $type ? 'date' : 'modified',
|
|
161
|
-
'order' => 'DESC',
|
|
162
|
-
'ignore_sticky_posts' => true,
|
|
163
|
-
'no_found_rows' => true,
|
|
164
|
-
)
|
|
165
|
-
);
|
|
166
|
-
$entries = array();
|
|
167
|
-
foreach ( $query->posts as $p ) {
|
|
168
|
-
$permalink = get_permalink( $p );
|
|
169
|
-
if ( ! $permalink ) {
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
172
|
-
$path = Relve_AR_Negotiation::normalize_path( (string) wp_parse_url( $permalink, PHP_URL_PATH ) );
|
|
173
|
-
if ( null !== $path && Relve_AR_Negotiation::is_excluded( $path, $settings['excluded'], $home_path ) ) {
|
|
174
|
-
continue;
|
|
175
|
-
}
|
|
176
|
-
$entries[] = array(
|
|
177
|
-
'title' => html_entity_decode( wp_strip_all_tags( get_the_title( $p ) ), ENT_QUOTES, 'UTF-8' ),
|
|
178
|
-
'url' => self::md_url( $permalink, $home ),
|
|
179
|
-
'description' => Relve_AR_Markdown_Builder::shorten( Relve_AR_Markdown_Builder::description_for( $p ), 200 ),
|
|
180
|
-
);
|
|
181
|
-
}
|
|
182
|
-
$left -= count( $entries );
|
|
183
|
-
$sections[ $name ] = $entries;
|
|
184
|
-
}
|
|
185
|
-
$sections = apply_filters( 'relve_agent_ready_llms_sections', $sections );
|
|
186
|
-
return self::build(
|
|
187
|
-
html_entity_decode( get_bloginfo( 'name' ), ENT_QUOTES, 'UTF-8' ),
|
|
188
|
-
html_entity_decode( get_bloginfo( 'description' ), ENT_QUOTES, 'UTF-8' ),
|
|
189
|
-
$sections
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
1
|
+
<?php
|
|
2
|
+
/**
|
|
3
|
+
* llms.txt (https://llmstxt.org): "# Site name", "> tagline", then sections
|
|
4
|
+
* of links to the `.md` URLs of recent posts and pages (up to 200).
|
|
5
|
+
*
|
|
6
|
+
* Port of @relvehq/agent-ready core/llms.ts (buildLlmsTxt) plus the WordPress
|
|
7
|
+
* side: which posts to list and whether this plugin should answer at all
|
|
8
|
+
* (mode auto|on|off; auto stays off when Yoast serves its own llms.txt or a
|
|
9
|
+
* physical llms.txt file exists).
|
|
10
|
+
*
|
|
11
|
+
* @package Relve_Agent_Ready
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
if ( ! defined( 'ABSPATH' ) ) {
|
|
15
|
+
exit;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* llms.txt builder and mode decision.
|
|
20
|
+
*/
|
|
21
|
+
class Relve_AR_Llms {
|
|
22
|
+
|
|
23
|
+
/** Max links listed. */
|
|
24
|
+
const MAX_ENTRIES = 200;
|
|
25
|
+
|
|
26
|
+
// ── pure helpers (unit-tested) ────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Should the plugin answer llms.txt?
|
|
30
|
+
*
|
|
31
|
+
* @param string $mode auto|on|off.
|
|
32
|
+
* @param bool $yoast_serves Yoast's llms.txt feature is on.
|
|
33
|
+
* @param bool $physical_file A real llms.txt file sits in the web root.
|
|
34
|
+
* @return bool
|
|
35
|
+
*/
|
|
36
|
+
public static function should_serve( $mode, $yoast_serves, $physical_file = false ) {
|
|
37
|
+
if ( 'on' === $mode ) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
if ( 'off' === $mode ) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return ! $yoast_serves && ! $physical_file;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Build the file.
|
|
48
|
+
*
|
|
49
|
+
* @param string $title Site name.
|
|
50
|
+
* @param string $summary Tagline.
|
|
51
|
+
* @param array $sections Section name => list of { title, url, description }.
|
|
52
|
+
* @return string
|
|
53
|
+
*/
|
|
54
|
+
public static function build( $title, $summary, $sections ) {
|
|
55
|
+
$title = self::one_line( $title );
|
|
56
|
+
$out = array( '# ' . ( '' === $title ? 'Site' : $title ) );
|
|
57
|
+
$sum = self::one_line( $summary );
|
|
58
|
+
if ( '' !== $sum ) {
|
|
59
|
+
$out[] = '> ' . $sum;
|
|
60
|
+
}
|
|
61
|
+
foreach ( $sections as $name => $entries ) {
|
|
62
|
+
$lines = array();
|
|
63
|
+
foreach ( $entries as $e ) {
|
|
64
|
+
if ( empty( $e['title'] ) || empty( $e['url'] ) ) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
$t = str_replace( array( '[', ']' ), '', self::one_line( $e['title'] ) );
|
|
68
|
+
$desc = isset( $e['description'] ) ? self::one_line( $e['description'] ) : '';
|
|
69
|
+
$lines[] = '- [' . $t . '](' . Relve_AR_Converter::url_for_markdown( $e['url'] ) . ')' . ( '' !== $desc ? ': ' . $desc : '' );
|
|
70
|
+
}
|
|
71
|
+
if ( $lines ) {
|
|
72
|
+
$out[] = '## ' . self::one_line( $name );
|
|
73
|
+
$out[] = implode( "\n", $lines );
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return implode( "\n\n", $out ) . "\n";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The `.md` URL of a permalink: "https://s/a/b/" -> "https://s/a/b.md";
|
|
81
|
+
* the home URL -> ".../index.md"; a permalink with a query string (plain
|
|
82
|
+
* permalinks) gets "format=md" instead.
|
|
83
|
+
*
|
|
84
|
+
* @param string $permalink Permalink.
|
|
85
|
+
* @param string $home_url Home URL.
|
|
86
|
+
* @return string
|
|
87
|
+
*/
|
|
88
|
+
public static function md_url( $permalink, $home_url ) {
|
|
89
|
+
$permalink = explode( '#', (string) $permalink, 2 )[0];
|
|
90
|
+
if ( false !== strpos( $permalink, '?' ) ) {
|
|
91
|
+
return $permalink . '&format=md';
|
|
92
|
+
}
|
|
93
|
+
if ( rtrim( $permalink, '/' ) === rtrim( (string) $home_url, '/' ) ) {
|
|
94
|
+
return rtrim( $permalink, '/' ) . '/index.md';
|
|
95
|
+
}
|
|
96
|
+
return rtrim( $permalink, '/' ) . '.md';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Collapse whitespace.
|
|
101
|
+
*
|
|
102
|
+
* @param string $s Text.
|
|
103
|
+
* @return string
|
|
104
|
+
*/
|
|
105
|
+
private static function one_line( $s ) {
|
|
106
|
+
return Relve_AR_Markdown_Builder::one_line( (string) $s );
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ── WordPress-bound ───────────────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Does Yoast SEO serve llms.txt on this site? Checked defensively: Yoast
|
|
113
|
+
* active and its "enable_llms_txt" option on. Unsure means no.
|
|
114
|
+
*
|
|
115
|
+
* @return bool
|
|
116
|
+
*/
|
|
117
|
+
public static function yoast_serves() {
|
|
118
|
+
$serves = false;
|
|
119
|
+
if ( defined( 'WPSEO_VERSION' ) || class_exists( 'WPSEO_Options' ) ) {
|
|
120
|
+
$opt = get_option( 'wpseo' );
|
|
121
|
+
$serves = is_array( $opt ) && ! empty( $opt['enable_llms_txt'] );
|
|
122
|
+
}
|
|
123
|
+
return (bool) apply_filters( 'relve_agent_ready_yoast_serves_llms', $serves );
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Is there a physical llms.txt in the WordPress root (the web server
|
|
128
|
+
* would answer it before WordPress anyway)?
|
|
129
|
+
*
|
|
130
|
+
* @return bool
|
|
131
|
+
*/
|
|
132
|
+
public static function physical_file_exists() {
|
|
133
|
+
return defined( 'ABSPATH' ) && file_exists( ABSPATH . 'llms.txt' );
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Build this site's llms.txt.
|
|
138
|
+
*
|
|
139
|
+
* @param array $settings Plugin settings.
|
|
140
|
+
* @return string
|
|
141
|
+
*/
|
|
142
|
+
public static function generate( $settings ) {
|
|
143
|
+
$home = home_url( '/' );
|
|
144
|
+
$types = array_values( array_intersect( (array) $settings['post_types'], get_post_types( array( 'public' => true ) ) ) );
|
|
145
|
+
$sections = array();
|
|
146
|
+
$left = self::MAX_ENTRIES;
|
|
147
|
+
$home_path = Relve_AR_Router::home_path();
|
|
148
|
+
foreach ( $types as $type ) {
|
|
149
|
+
if ( $left <= 0 ) {
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
$obj = get_post_type_object( $type );
|
|
153
|
+
$name = $obj && isset( $obj->labels->name ) ? $obj->labels->name : $type;
|
|
154
|
+
$query = new WP_Query(
|
|
155
|
+
array(
|
|
156
|
+
'post_type' => $type,
|
|
157
|
+
'post_status' => 'publish',
|
|
158
|
+
'has_password' => false,
|
|
159
|
+
'posts_per_page' => $left,
|
|
160
|
+
'orderby' => 'post' === $type ? 'date' : 'modified',
|
|
161
|
+
'order' => 'DESC',
|
|
162
|
+
'ignore_sticky_posts' => true,
|
|
163
|
+
'no_found_rows' => true,
|
|
164
|
+
)
|
|
165
|
+
);
|
|
166
|
+
$entries = array();
|
|
167
|
+
foreach ( $query->posts as $p ) {
|
|
168
|
+
$permalink = get_permalink( $p );
|
|
169
|
+
if ( ! $permalink ) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
$path = Relve_AR_Negotiation::normalize_path( (string) wp_parse_url( $permalink, PHP_URL_PATH ) );
|
|
173
|
+
if ( null !== $path && Relve_AR_Negotiation::is_excluded( $path, $settings['excluded'], $home_path ) ) {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
$entries[] = array(
|
|
177
|
+
'title' => html_entity_decode( wp_strip_all_tags( get_the_title( $p ) ), ENT_QUOTES, 'UTF-8' ),
|
|
178
|
+
'url' => self::md_url( $permalink, $home ),
|
|
179
|
+
'description' => Relve_AR_Markdown_Builder::shorten( Relve_AR_Markdown_Builder::description_for( $p ), 200 ),
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
$left -= count( $entries );
|
|
183
|
+
$sections[ $name ] = $entries;
|
|
184
|
+
}
|
|
185
|
+
$sections = apply_filters( 'relve_agent_ready_llms_sections', $sections );
|
|
186
|
+
return self::build(
|
|
187
|
+
html_entity_decode( get_bloginfo( 'name' ), ENT_QUOTES, 'UTF-8' ),
|
|
188
|
+
html_entity_decode( get_bloginfo( 'description' ), ENT_QUOTES, 'UTF-8' ),
|
|
189
|
+
$sections
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
}
|