@zenalexa/unicli 0.220.0 → 0.220.1

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.
Files changed (182) hide show
  1. package/AGENTS.md +10 -19
  2. package/README.md +17 -11
  3. package/README.zh-CN.md +17 -11
  4. package/dist/adapters/anilist/web.d.ts +11 -0
  5. package/dist/adapters/anilist/web.d.ts.map +1 -0
  6. package/dist/adapters/anilist/web.js +284 -0
  7. package/dist/adapters/anilist/web.js.map +1 -0
  8. package/dist/adapters/bangumi/web.d.ts +14 -0
  9. package/dist/adapters/bangumi/web.d.ts.map +1 -0
  10. package/dist/adapters/bangumi/web.js +257 -0
  11. package/dist/adapters/bangumi/web.js.map +1 -0
  12. package/dist/adapters/dlsite/web.d.ts +31 -0
  13. package/dist/adapters/dlsite/web.d.ts.map +1 -0
  14. package/dist/adapters/dlsite/web.js +455 -0
  15. package/dist/adapters/dlsite/web.js.map +1 -0
  16. package/dist/adapters/ehentai/web.d.ts +66 -0
  17. package/dist/adapters/ehentai/web.d.ts.map +1 -0
  18. package/dist/adapters/ehentai/web.js +608 -0
  19. package/dist/adapters/ehentai/web.js.map +1 -0
  20. package/dist/adapters/jikan/web.d.ts +9 -0
  21. package/dist/adapters/jikan/web.d.ts.map +1 -0
  22. package/dist/adapters/jikan/web.js +154 -0
  23. package/dist/adapters/jikan/web.js.map +1 -0
  24. package/dist/adapters/kitsu/web.d.ts +9 -0
  25. package/dist/adapters/kitsu/web.d.ts.map +1 -0
  26. package/dist/adapters/kitsu/web.js +97 -0
  27. package/dist/adapters/kitsu/web.js.map +1 -0
  28. package/dist/adapters/mangadex/web.d.ts +10 -0
  29. package/dist/adapters/mangadex/web.d.ts.map +1 -0
  30. package/dist/adapters/mangadex/web.js +188 -0
  31. package/dist/adapters/mangadex/web.js.map +1 -0
  32. package/dist/adapters/moegirl/web.d.ts +23 -0
  33. package/dist/adapters/moegirl/web.d.ts.map +1 -0
  34. package/dist/adapters/moegirl/web.js +269 -0
  35. package/dist/adapters/moegirl/web.js.map +1 -0
  36. package/dist/adapters/safebooru/web.d.ts +10 -0
  37. package/dist/adapters/safebooru/web.d.ts.map +1 -0
  38. package/dist/adapters/safebooru/web.js +120 -0
  39. package/dist/adapters/safebooru/web.js.map +1 -0
  40. package/dist/adapters/vndb/web.d.ts +10 -0
  41. package/dist/adapters/vndb/web.d.ts.map +1 -0
  42. package/dist/adapters/vndb/web.js +321 -0
  43. package/dist/adapters/vndb/web.js.map +1 -0
  44. package/dist/agents/codex-pack.d.ts +62 -0
  45. package/dist/agents/codex-pack.d.ts.map +1 -0
  46. package/dist/agents/codex-pack.js +163 -0
  47. package/dist/agents/codex-pack.js.map +1 -0
  48. package/dist/commands/agents.d.ts.map +1 -1
  49. package/dist/commands/agents.js +6 -43
  50. package/dist/commands/agents.js.map +1 -1
  51. package/dist/commands/browser/adapter.d.ts.map +1 -1
  52. package/dist/commands/browser/adapter.js +17 -3
  53. package/dist/commands/browser/adapter.js.map +1 -1
  54. package/dist/commands/describe.d.ts.map +1 -1
  55. package/dist/commands/describe.js +6 -7
  56. package/dist/commands/describe.js.map +1 -1
  57. package/dist/commands/dispatch.d.ts +1 -1
  58. package/dist/commands/dispatch.d.ts.map +1 -1
  59. package/dist/commands/dispatch.js +4 -2
  60. package/dist/commands/dispatch.js.map +1 -1
  61. package/dist/commands/mcp.d.ts +1 -1
  62. package/dist/commands/mcp.d.ts.map +1 -1
  63. package/dist/commands/mcp.js +10 -5
  64. package/dist/commands/mcp.js.map +1 -1
  65. package/dist/core/command-contract-lint.d.ts +10 -0
  66. package/dist/core/command-contract-lint.d.ts.map +1 -0
  67. package/dist/core/command-contract-lint.js +41 -0
  68. package/dist/core/command-contract-lint.js.map +1 -0
  69. package/dist/core/command-contract.d.ts +100 -0
  70. package/dist/core/command-contract.d.ts.map +1 -0
  71. package/dist/core/command-contract.js +174 -0
  72. package/dist/core/command-contract.js.map +1 -0
  73. package/dist/core/index.d.ts +2 -0
  74. package/dist/core/index.d.ts.map +1 -1
  75. package/dist/core/index.js +2 -0
  76. package/dist/core/index.js.map +1 -1
  77. package/dist/discovery/aliases.d.ts +2 -2
  78. package/dist/discovery/aliases.d.ts.map +1 -1
  79. package/dist/discovery/aliases.js +464 -6
  80. package/dist/discovery/aliases.js.map +1 -1
  81. package/dist/discovery/search.d.ts.map +1 -1
  82. package/dist/discovery/search.js +147 -2
  83. package/dist/discovery/search.js.map +1 -1
  84. package/dist/engine/args.d.ts.map +1 -1
  85. package/dist/engine/args.js +18 -1
  86. package/dist/engine/args.js.map +1 -1
  87. package/dist/engine/artifact-validation.d.ts +29 -0
  88. package/dist/engine/artifact-validation.d.ts.map +1 -0
  89. package/dist/engine/artifact-validation.js +211 -0
  90. package/dist/engine/artifact-validation.js.map +1 -0
  91. package/dist/engine/browser/diagnostics.d.ts +38 -0
  92. package/dist/engine/browser/diagnostics.d.ts.map +1 -0
  93. package/dist/engine/browser/diagnostics.js +40 -0
  94. package/dist/engine/browser/diagnostics.js.map +1 -0
  95. package/dist/engine/invoke.d.ts +1 -0
  96. package/dist/engine/invoke.d.ts.map +1 -1
  97. package/dist/engine/invoke.js +1 -0
  98. package/dist/engine/invoke.js.map +1 -1
  99. package/dist/engine/kernel/errors.d.ts +11 -0
  100. package/dist/engine/kernel/errors.d.ts.map +1 -0
  101. package/dist/engine/kernel/errors.js +15 -0
  102. package/dist/engine/kernel/errors.js.map +1 -0
  103. package/dist/engine/kernel/execute.d.ts +7 -18
  104. package/dist/engine/kernel/execute.d.ts.map +1 -1
  105. package/dist/engine/kernel/execute.js +25 -410
  106. package/dist/engine/kernel/execute.js.map +1 -1
  107. package/dist/engine/kernel/stages.d.ts +44 -0
  108. package/dist/engine/kernel/stages.d.ts.map +1 -0
  109. package/dist/engine/kernel/stages.js +428 -0
  110. package/dist/engine/kernel/stages.js.map +1 -0
  111. package/dist/engine/kernel/types.d.ts +21 -1
  112. package/dist/engine/kernel/types.d.ts.map +1 -1
  113. package/dist/engine/steps/download.d.ts +1 -0
  114. package/dist/engine/steps/download.d.ts.map +1 -1
  115. package/dist/engine/steps/download.js +10 -6
  116. package/dist/engine/steps/download.js.map +1 -1
  117. package/dist/fast-path/render.js +1 -1
  118. package/dist/fast-path/render.js.map +1 -1
  119. package/dist/manifest-compact.txt +3 -3
  120. package/dist/manifest-search.json +1 -1
  121. package/dist/manifest.json +3074 -3
  122. package/dist/mcp/handler.d.ts.map +1 -1
  123. package/dist/mcp/handler.js +11 -1
  124. package/dist/mcp/handler.js.map +1 -1
  125. package/dist/mcp/server.d.ts +1 -1
  126. package/dist/mcp/server.js +1 -1
  127. package/dist/mcp/tools.d.ts.map +1 -1
  128. package/dist/mcp/tools.js +18 -10
  129. package/dist/mcp/tools.js.map +1 -1
  130. package/dist/output/error-map.d.ts.map +1 -1
  131. package/dist/output/error-map.js +1 -1
  132. package/dist/output/error-map.js.map +1 -1
  133. package/dist/registry.d.ts.map +1 -1
  134. package/dist/registry.js +2 -1
  135. package/dist/registry.js.map +1 -1
  136. package/package.json +2 -2
  137. package/server.json +3 -3
  138. package/skills/unicli/SKILL.md +1 -1
  139. package/skills/unicli-claude-code/SKILL.md +1 -1
  140. package/skills/unicli-hermes/SKILL.md +1 -1
  141. package/src/adapters/anilist/web.test.ts +93 -0
  142. package/src/adapters/anilist/web.ts +341 -0
  143. package/src/adapters/arxiv/download.yaml +53 -0
  144. package/src/adapters/bangumi/web.test.ts +109 -0
  145. package/src/adapters/bangumi/web.ts +295 -0
  146. package/src/adapters/danbooru/artists.yaml +44 -0
  147. package/src/adapters/danbooru/comments.yaml +45 -0
  148. package/src/adapters/danbooru/detail.yaml +78 -0
  149. package/src/adapters/danbooru/download.yaml +51 -0
  150. package/src/adapters/danbooru/pools.yaml +56 -0
  151. package/src/adapters/danbooru/search.yaml +69 -0
  152. package/src/adapters/danbooru/tags.yaml +42 -0
  153. package/src/adapters/danbooru/wiki.yaml +44 -0
  154. package/src/adapters/dlsite/web.test.ts +132 -0
  155. package/src/adapters/dlsite/web.ts +557 -0
  156. package/src/adapters/ehentai/web.test.ts +157 -0
  157. package/src/adapters/ehentai/web.ts +750 -0
  158. package/src/adapters/jikan/web.test.ts +50 -0
  159. package/src/adapters/jikan/web.ts +177 -0
  160. package/src/adapters/kitsu/web.test.ts +29 -0
  161. package/src/adapters/kitsu/web.ts +109 -0
  162. package/src/adapters/konachan/detail.yaml +62 -0
  163. package/src/adapters/konachan/download.yaml +55 -0
  164. package/src/adapters/konachan/search.yaml +65 -0
  165. package/src/adapters/konachan/tags.yaml +40 -0
  166. package/src/adapters/mangadex/web.test.ts +46 -0
  167. package/src/adapters/mangadex/web.ts +210 -0
  168. package/src/adapters/moegirl/web.test.ts +87 -0
  169. package/src/adapters/moegirl/web.ts +343 -0
  170. package/src/adapters/pdf/read.yaml +49 -0
  171. package/src/adapters/pixiv/download.yaml +15 -2
  172. package/src/adapters/safebooru/detail.yaml +63 -0
  173. package/src/adapters/safebooru/download.yaml +58 -0
  174. package/src/adapters/safebooru/search.yaml +69 -0
  175. package/src/adapters/safebooru/web.test.ts +60 -0
  176. package/src/adapters/safebooru/web.ts +130 -0
  177. package/src/adapters/vndb/web.test.ts +86 -0
  178. package/src/adapters/vndb/web.ts +393 -0
  179. package/src/adapters/yandere/detail.yaml +61 -0
  180. package/src/adapters/yandere/download.yaml +56 -0
  181. package/src/adapters/yandere/search.yaml +67 -0
  182. package/src/adapters/yandere/tags.yaml +41 -0
@@ -0,0 +1,393 @@
1
+ /**
2
+ * @owner src/adapters/vndb/web.ts
3
+ * @does Register VNDB public visual novel, release, tag, staff, producer, and character lookup commands.
4
+ * @needs VNDB Kana API JSON contract and public unauthenticated read endpoints.
5
+ * @feeds ACG research workflows, visual novel discovery, game tag/type search.
6
+ * @breaks VNDB field names or filter semantics drifting will hide games or metadata.
7
+ */
8
+
9
+ import { USER_AGENT } from "../../constants.js";
10
+ import { cli, Strategy } from "../../registry.js";
11
+
12
+ const VNDB_API = "https://api.vndb.org/kana";
13
+
14
+ interface VndbResponse {
15
+ more?: boolean;
16
+ results?: Record<string, unknown>[];
17
+ }
18
+
19
+ function str(value: unknown): string {
20
+ return value === undefined || value === null ? "" : String(value);
21
+ }
22
+
23
+ function requireQuery(value: unknown): string {
24
+ const query = str(value).trim();
25
+ if (!query) throw new Error("VNDB query cannot be empty.");
26
+ return query;
27
+ }
28
+
29
+ function requireLimit(value: unknown, fallback = 20): number {
30
+ if (value === undefined || value === null || value === "") return fallback;
31
+ const n = Number(value);
32
+ if (!Number.isInteger(n) || n < 1 || n > 100) {
33
+ throw new Error("VNDB limit must be an integer in [1, 100].");
34
+ }
35
+ return n;
36
+ }
37
+
38
+ function normalizeId(value: unknown, prefix: string): string {
39
+ const raw = str(value).trim().toLowerCase();
40
+ if (!raw) throw new Error("VNDB id cannot be empty.");
41
+ return raw.startsWith(prefix) ? raw : `${prefix}${raw}`;
42
+ }
43
+
44
+ function joinNames(value: unknown, key = "name"): string {
45
+ return Array.isArray(value)
46
+ ? value
47
+ .map((item) => str((item as Record<string, unknown>)[key]))
48
+ .join(", ")
49
+ : "";
50
+ }
51
+
52
+ function joinStrings(value: unknown): string {
53
+ return Array.isArray(value) ? value.map(String).join(", ") : str(value);
54
+ }
55
+
56
+ function firstUrl(value: unknown): string {
57
+ if (!value || typeof value !== "object") return "";
58
+ return str((value as Record<string, unknown>).url);
59
+ }
60
+
61
+ function tagSummary(value: unknown): string {
62
+ if (!Array.isArray(value)) return "";
63
+ return value
64
+ .slice()
65
+ .sort(
66
+ (a, b) =>
67
+ Number((b as Record<string, unknown>).rating ?? 0) -
68
+ Number((a as Record<string, unknown>).rating ?? 0),
69
+ )
70
+ .slice(0, 12)
71
+ .map((item) => str((item as Record<string, unknown>).name))
72
+ .filter(Boolean)
73
+ .join(", ");
74
+ }
75
+
76
+ function sortValue(value: unknown, allowed: Record<string, string>): string {
77
+ const key = str(value || "relevance")
78
+ .toLowerCase()
79
+ .replace(/[\s_-]+/g, "");
80
+ const mapped = allowed[key];
81
+ if (!mapped) {
82
+ throw new Error(
83
+ `Unsupported VNDB sort: ${value}. Supported: ${Object.keys(allowed).join(", ")}.`,
84
+ );
85
+ }
86
+ return mapped;
87
+ }
88
+
89
+ async function vndbPost(
90
+ endpoint: string,
91
+ body: Record<string, unknown>,
92
+ ): Promise<Record<string, unknown>[]> {
93
+ const response = await fetch(`${VNDB_API}/${endpoint}`, {
94
+ method: "POST",
95
+ headers: {
96
+ Accept: "application/json",
97
+ "Content-Type": "application/json",
98
+ "User-Agent": USER_AGENT,
99
+ },
100
+ body: JSON.stringify(body),
101
+ });
102
+ if (!response.ok) {
103
+ throw new Error(
104
+ `VNDB ${endpoint} request failed with HTTP ${response.status}.`,
105
+ );
106
+ }
107
+ const json = (await response.json()) as VndbResponse;
108
+ return json.results ?? [];
109
+ }
110
+
111
+ function rankRows(rows: Record<string, unknown>[]): Record<string, unknown>[] {
112
+ return rows.map((row, index) => ({ rank: index + 1, ...row }));
113
+ }
114
+
115
+ export function mapVndbVisualNovels(
116
+ rows: Record<string, unknown>[],
117
+ ): Record<string, unknown>[] {
118
+ return rankRows(
119
+ rows.map((item) => ({
120
+ id: str(item.id),
121
+ title: str(item.title),
122
+ alttitle: str(item.alttitle),
123
+ released: str(item.released),
124
+ languages: joinStrings(item.languages),
125
+ platforms: joinStrings(item.platforms),
126
+ rating: item.rating ?? "",
127
+ votecount: item.votecount ?? "",
128
+ developers: joinNames(item.developers),
129
+ tags: tagSummary(item.tags),
130
+ image: firstUrl(item.image),
131
+ url: `https://vndb.org/${str(item.id)}`,
132
+ })),
133
+ );
134
+ }
135
+
136
+ export function mapVndbReleases(
137
+ rows: Record<string, unknown>[],
138
+ ): Record<string, unknown>[] {
139
+ return rankRows(
140
+ rows.map((item) => ({
141
+ id: str(item.id),
142
+ title: str(item.title),
143
+ released: str(item.released),
144
+ platforms: joinStrings(item.platforms),
145
+ producers: joinNames(item.producers),
146
+ vns: joinNames(item.vns, "title"),
147
+ url: `https://vndb.org/${str(item.id)}`,
148
+ })),
149
+ );
150
+ }
151
+
152
+ function mapSimple(
153
+ rows: Record<string, unknown>[],
154
+ urlPrefix: string,
155
+ ): Record<string, unknown>[] {
156
+ return rankRows(
157
+ rows.map((item) => ({
158
+ id: str(item.id),
159
+ name: str(item.name),
160
+ original: str(item.original),
161
+ category: str(item.category),
162
+ type: str(item.type),
163
+ lang: str(item.lang),
164
+ aliases: joinStrings(item.aliases),
165
+ description: str(item.description).replace(/\s+/g, " ").slice(0, 500),
166
+ url: `https://vndb.org/${urlPrefix}${str(item.id).replace(/^[a-z]+/, "")}`,
167
+ })),
168
+ );
169
+ }
170
+
171
+ async function searchVn(kwargs: Record<string, unknown>) {
172
+ const sort = sortValue(kwargs.sort, {
173
+ relevance: "searchrank",
174
+ rating: "rating",
175
+ votes: "votecount",
176
+ votecount: "votecount",
177
+ released: "released",
178
+ time: "released",
179
+ title: "title",
180
+ });
181
+ const rows = await vndbPost("vn", {
182
+ filters: ["search", "=", requireQuery(kwargs.query)],
183
+ fields:
184
+ "id,title,alttitle,released,languages,platforms,rating,votecount,image.url,tags.rating,tags.name,tags.category,developers.name",
185
+ sort,
186
+ reverse: kwargs.reverse === true,
187
+ results: requireLimit(kwargs.limit),
188
+ });
189
+ return mapVndbVisualNovels(rows);
190
+ }
191
+
192
+ const VN_COLUMNS = [
193
+ "rank",
194
+ "id",
195
+ "title",
196
+ "alttitle",
197
+ "released",
198
+ "languages",
199
+ "platforms",
200
+ "rating",
201
+ "votecount",
202
+ "developers",
203
+ "tags",
204
+ "url",
205
+ ];
206
+
207
+ cli({
208
+ site: "vndb",
209
+ name: "search",
210
+ description: "Search VNDB visual novels by title, alias, tag, or keyword",
211
+ domain: "api.vndb.org",
212
+ strategy: Strategy.PUBLIC,
213
+ browser: false,
214
+ args: [
215
+ { name: "query", type: "str", required: true, positional: true },
216
+ { name: "limit", type: "int", default: 20 },
217
+ {
218
+ name: "sort",
219
+ type: "str",
220
+ default: "relevance",
221
+ description: "relevance, rating, votes, released, time, title",
222
+ },
223
+ { name: "reverse", type: "bool", default: false },
224
+ ],
225
+ columns: VN_COLUMNS,
226
+ func: async (_page, kwargs) => searchVn(kwargs),
227
+ });
228
+
229
+ cli({
230
+ site: "vndb",
231
+ name: "vn",
232
+ description: "Get VNDB visual novel details by v-id",
233
+ domain: "api.vndb.org",
234
+ strategy: Strategy.PUBLIC,
235
+ browser: false,
236
+ args: [{ name: "id", type: "str", required: true, positional: true }],
237
+ columns: VN_COLUMNS,
238
+ func: async (_page, kwargs) =>
239
+ mapVndbVisualNovels(
240
+ await vndbPost("vn", {
241
+ filters: ["id", "=", normalizeId(kwargs.id, "v")],
242
+ fields:
243
+ "id,title,alttitle,released,languages,platforms,rating,votecount,image.url,tags.rating,tags.name,tags.category,developers.name",
244
+ results: 1,
245
+ }),
246
+ ),
247
+ });
248
+
249
+ cli({
250
+ site: "vndb",
251
+ name: "releases",
252
+ description: "Search VNDB releases by title or visual novel keyword",
253
+ domain: "api.vndb.org",
254
+ strategy: Strategy.PUBLIC,
255
+ browser: false,
256
+ args: [
257
+ { name: "query", type: "str", required: true, positional: true },
258
+ { name: "limit", type: "int", default: 20 },
259
+ {
260
+ name: "sort",
261
+ type: "str",
262
+ default: "released",
263
+ description: "released, time, title",
264
+ },
265
+ { name: "reverse", type: "bool", default: true },
266
+ ],
267
+ columns: [
268
+ "rank",
269
+ "id",
270
+ "title",
271
+ "released",
272
+ "platforms",
273
+ "producers",
274
+ "vns",
275
+ "url",
276
+ ],
277
+ func: async (_page, kwargs) =>
278
+ mapVndbReleases(
279
+ await vndbPost("release", {
280
+ filters: ["search", "=", requireQuery(kwargs.query)],
281
+ fields: "id,title,released,platforms,producers.name,vns.title",
282
+ sort: sortValue(kwargs.sort, {
283
+ released: "released",
284
+ time: "released",
285
+ title: "title",
286
+ }),
287
+ reverse: kwargs.reverse !== false,
288
+ results: requireLimit(kwargs.limit),
289
+ }),
290
+ ),
291
+ });
292
+
293
+ cli({
294
+ site: "vndb",
295
+ name: "tags",
296
+ description: "Search VNDB game tags and genres",
297
+ domain: "api.vndb.org",
298
+ strategy: Strategy.PUBLIC,
299
+ browser: false,
300
+ args: [
301
+ { name: "query", type: "str", required: true, positional: true },
302
+ { name: "limit", type: "int", default: 20 },
303
+ ],
304
+ columns: ["rank", "id", "name", "category", "description", "url"],
305
+ func: async (_page, kwargs) =>
306
+ mapSimple(
307
+ await vndbPost("tag", {
308
+ filters: ["search", "=", requireQuery(kwargs.query)],
309
+ fields: "id,name,category,description",
310
+ results: requireLimit(kwargs.limit),
311
+ }),
312
+ "g",
313
+ ),
314
+ });
315
+
316
+ cli({
317
+ site: "vndb",
318
+ name: "staff",
319
+ description: "Search VNDB creators and staff",
320
+ domain: "api.vndb.org",
321
+ strategy: Strategy.PUBLIC,
322
+ browser: false,
323
+ args: [
324
+ { name: "query", type: "str", required: true, positional: true },
325
+ { name: "limit", type: "int", default: 20 },
326
+ ],
327
+ columns: ["rank", "id", "name", "original", "lang", "description", "url"],
328
+ func: async (_page, kwargs) =>
329
+ mapSimple(
330
+ await vndbPost("staff", {
331
+ filters: ["search", "=", requireQuery(kwargs.query)],
332
+ fields: "id,name,original,lang,gender,description",
333
+ results: requireLimit(kwargs.limit),
334
+ }),
335
+ "s",
336
+ ),
337
+ });
338
+
339
+ cli({
340
+ site: "vndb",
341
+ name: "producers",
342
+ description:
343
+ "Search VNDB producers, studios, brands, makers, circles, companies, and visual novel publishers such as Yuzusoft",
344
+ domain: "api.vndb.org",
345
+ strategy: Strategy.PUBLIC,
346
+ browser: false,
347
+ args: [
348
+ { name: "query", type: "str", required: true, positional: true },
349
+ { name: "limit", type: "int", default: 20 },
350
+ ],
351
+ columns: [
352
+ "rank",
353
+ "id",
354
+ "name",
355
+ "aliases",
356
+ "lang",
357
+ "type",
358
+ "description",
359
+ "url",
360
+ ],
361
+ func: async (_page, kwargs) =>
362
+ mapSimple(
363
+ await vndbPost("producer", {
364
+ filters: ["search", "=", requireQuery(kwargs.query)],
365
+ fields: "id,name,aliases,lang,type,description",
366
+ results: requireLimit(kwargs.limit),
367
+ }),
368
+ "p",
369
+ ),
370
+ });
371
+
372
+ cli({
373
+ site: "vndb",
374
+ name: "characters",
375
+ description: "Search VNDB characters and traits",
376
+ domain: "api.vndb.org",
377
+ strategy: Strategy.PUBLIC,
378
+ browser: false,
379
+ args: [
380
+ { name: "query", type: "str", required: true, positional: true },
381
+ { name: "limit", type: "int", default: 20 },
382
+ ],
383
+ columns: ["rank", "id", "name", "original", "description", "url"],
384
+ func: async (_page, kwargs) =>
385
+ mapSimple(
386
+ await vndbPost("character", {
387
+ filters: ["search", "=", requireQuery(kwargs.query)],
388
+ fields: "id,name,original,sex,description,vns.title,traits.name",
389
+ results: requireLimit(kwargs.limit),
390
+ }),
391
+ "c",
392
+ ),
393
+ });
@@ -0,0 +1,61 @@
1
+ site: yandere
2
+ name: detail
3
+ description: Get yande.re post detail by post id
4
+ domain: yande.re
5
+ type: web-api
6
+ strategy: public
7
+
8
+ args:
9
+ id:
10
+ type: str
11
+ required: true
12
+ positional: true
13
+
14
+ pipeline:
15
+ - fetch:
16
+ url: "https://yande.re/post.json"
17
+ params:
18
+ tags: "${{ 'id:' + args.id }}"
19
+ limit: 1
20
+
21
+ - map:
22
+ id: "${{ item.id }}"
23
+ rating: "${{ item.rating }}"
24
+ score: "${{ item.score }}"
25
+ author: "${{ item.author }}"
26
+ created_at: "${{ item.created_at }}"
27
+ width: "${{ item.width }}"
28
+ height: "${{ item.height }}"
29
+ file_ext: "${{ item.file_ext }}"
30
+ file_size: "${{ item.file_size }}"
31
+ tags: "${{ item.tags }}"
32
+ source: "${{ item.source }}"
33
+ preview_url: "${{ item.preview_url }}"
34
+ sample_url: "${{ item.sample_url }}"
35
+ file_url: "${{ item.file_url }}"
36
+ url: "${{ 'https://yande.re/post/show/' + item.id }}"
37
+
38
+ columns:
39
+ [
40
+ id,
41
+ rating,
42
+ score,
43
+ author,
44
+ width,
45
+ height,
46
+ file_ext,
47
+ file_size,
48
+ tags,
49
+ source,
50
+ sample_url,
51
+ file_url,
52
+ url,
53
+ ]
54
+
55
+ # schema-v2 metadata — injected by `unicli migrate schema-v2`
56
+ capabilities: ["http.fetch"]
57
+ minimum_capability: http.fetch
58
+ trust: public
59
+ confidentiality: public
60
+ quarantine: false
61
+ schema_version: v2
@@ -0,0 +1,56 @@
1
+ site: yandere
2
+ name: download
3
+ description: Download yande.re post image by post id
4
+ domain: yande.re
5
+ type: web-api
6
+ strategy: public
7
+
8
+ args:
9
+ id:
10
+ type: str
11
+ required: true
12
+ positional: true
13
+ output:
14
+ type: str
15
+ default: "./downloads/yandere"
16
+ description: Output directory
17
+ x-unicli-kind: path
18
+ quality:
19
+ type: str
20
+ default: sample
21
+ description: sample or original
22
+
23
+ columns: [id, rating, tags, url, _download]
24
+
25
+ pipeline:
26
+ - fetch:
27
+ url: "https://yande.re/post.json"
28
+ params:
29
+ tags: "${{ 'id:' + args.id }}"
30
+ limit: 1
31
+
32
+ - assert:
33
+ condition: "data.length > 0"
34
+ message: "No yande.re post found for this id."
35
+
36
+ - download:
37
+ url: "${{ args.quality === 'original' ? item.file_url : item.sample_url }}"
38
+ dir: "${{ args.output }}"
39
+ filename: "${{ item.id }}.${{ (args.quality === 'original' ? item.file_url : item.sample_url) | ext | default('jpg') }}"
40
+ type: image
41
+ concurrency: 1
42
+
43
+ - map:
44
+ id: "${{ item.id }}"
45
+ rating: "${{ item.rating }}"
46
+ tags: "${{ item.tags }}"
47
+ url: "${{ 'https://yande.re/post/show/' + item.id }}"
48
+ _download: "${{ item._download }}"
49
+
50
+ # schema-v2 metadata — injected by `unicli migrate schema-v2`
51
+ capabilities: ["http.fetch", "http.download"]
52
+ minimum_capability: http.fetch
53
+ trust: public
54
+ confidentiality: public
55
+ quarantine: false
56
+ schema_version: v2
@@ -0,0 +1,67 @@
1
+ site: yandere
2
+ name: search
3
+ description: Search yande.re illustration posts by Moebooru tag query
4
+ domain: yande.re
5
+ type: web-api
6
+ strategy: public
7
+
8
+ args:
9
+ tags:
10
+ type: str
11
+ required: true
12
+ positional: true
13
+ description: Tag query, for example "hanabi rating:safe" or "order:score"
14
+ limit:
15
+ type: int
16
+ default: 20
17
+ page:
18
+ type: int
19
+ default: 1
20
+
21
+ pipeline:
22
+ - fetch:
23
+ url: "https://yande.re/post.json"
24
+ params:
25
+ tags: "${{ args.tags }}"
26
+ limit: "${{ args.limit }}"
27
+ page: "${{ args.page }}"
28
+
29
+ - map:
30
+ rank: "${{ index + 1 }}"
31
+ id: "${{ item.id }}"
32
+ rating: "${{ item.rating }}"
33
+ score: "${{ item.score }}"
34
+ author: "${{ item.author }}"
35
+ width: "${{ item.width }}"
36
+ height: "${{ item.height }}"
37
+ file_ext: "${{ item.file_ext }}"
38
+ tags: "${{ item.tags }}"
39
+ source: "${{ item.source }}"
40
+ preview_url: "${{ item.preview_url }}"
41
+ sample_url: "${{ item.sample_url }}"
42
+ file_url: "${{ item.file_url }}"
43
+ url: "${{ 'https://yande.re/post/show/' + item.id }}"
44
+
45
+ columns:
46
+ [
47
+ rank,
48
+ id,
49
+ rating,
50
+ score,
51
+ author,
52
+ width,
53
+ height,
54
+ tags,
55
+ source,
56
+ sample_url,
57
+ file_url,
58
+ url,
59
+ ]
60
+
61
+ # schema-v2 metadata — injected by `unicli migrate schema-v2`
62
+ capabilities: ["http.fetch"]
63
+ minimum_capability: http.fetch
64
+ trust: public
65
+ confidentiality: public
66
+ quarantine: false
67
+ schema_version: v2
@@ -0,0 +1,41 @@
1
+ site: yandere
2
+ name: tags
3
+ description: Search yande.re tags by name prefix
4
+ domain: yande.re
5
+ type: web-api
6
+ strategy: public
7
+
8
+ args:
9
+ query:
10
+ type: str
11
+ required: true
12
+ positional: true
13
+ limit:
14
+ type: int
15
+ default: 20
16
+
17
+ pipeline:
18
+ - fetch:
19
+ url: "https://yande.re/tag.json"
20
+ params:
21
+ name: "${{ args.query }}"
22
+ limit: "${{ args.limit }}"
23
+
24
+ - map:
25
+ rank: "${{ index + 1 }}"
26
+ id: "${{ item.id }}"
27
+ name: "${{ item.name }}"
28
+ count: "${{ item.count }}"
29
+ type: "${{ item.type }}"
30
+ ambiguous: "${{ item.ambiguous }}"
31
+ url: "${{ 'https://yande.re/post?tags=' + item.name }}"
32
+
33
+ columns: [rank, id, name, count, type, ambiguous, url]
34
+
35
+ # schema-v2 metadata — injected by `unicli migrate schema-v2`
36
+ capabilities: ["http.fetch"]
37
+ minimum_capability: http.fetch
38
+ trust: public
39
+ confidentiality: public
40
+ quarantine: false
41
+ schema_version: v2