@zenalexa/unicli 0.221.0 → 0.222.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.
Files changed (247) hide show
  1. package/AGENTS.md +12 -12
  2. package/README.md +40 -31
  3. package/README.zh-CN.md +37 -31
  4. package/crates/unicli-atspi/src/errors.rs +2 -2
  5. package/crates/unicli-uia/src/errors.rs +1 -1
  6. package/dist/adapters/acl-anthology/papers.d.ts +16 -0
  7. package/dist/adapters/acl-anthology/papers.d.ts.map +1 -0
  8. package/dist/adapters/acl-anthology/papers.js +135 -0
  9. package/dist/adapters/acl-anthology/papers.js.map +1 -0
  10. package/dist/adapters/arxiv/papers.js +2 -0
  11. package/dist/adapters/arxiv/papers.js.map +1 -1
  12. package/dist/adapters/baidu-scholar/search.js +5 -0
  13. package/dist/adapters/baidu-scholar/search.js.map +1 -1
  14. package/dist/adapters/crossref/works.d.ts +42 -0
  15. package/dist/adapters/crossref/works.d.ts.map +1 -0
  16. package/dist/adapters/crossref/works.js +157 -0
  17. package/dist/adapters/crossref/works.js.map +1 -0
  18. package/dist/adapters/cvf/papers.d.ts +17 -0
  19. package/dist/adapters/cvf/papers.d.ts.map +1 -0
  20. package/dist/adapters/cvf/papers.js +124 -0
  21. package/dist/adapters/cvf/papers.js.map +1 -0
  22. package/dist/adapters/dblp/publications.js +4 -0
  23. package/dist/adapters/dblp/publications.js.map +1 -1
  24. package/dist/adapters/google-scholar/cite.js +1 -0
  25. package/dist/adapters/google-scholar/cite.js.map +1 -1
  26. package/dist/adapters/google-scholar/profile.js +5 -0
  27. package/dist/adapters/google-scholar/profile.js.map +1 -1
  28. package/dist/adapters/google-scholar/search.js +5 -0
  29. package/dist/adapters/google-scholar/search.js.map +1 -1
  30. package/dist/adapters/hf/paper.js +1 -0
  31. package/dist/adapters/hf/paper.js.map +1 -1
  32. package/dist/adapters/neurips/proceedings.d.ts +17 -0
  33. package/dist/adapters/neurips/proceedings.d.ts.map +1 -0
  34. package/dist/adapters/neurips/proceedings.js +112 -0
  35. package/dist/adapters/neurips/proceedings.js.map +1 -0
  36. package/dist/adapters/openalex/works.d.ts.map +1 -1
  37. package/dist/adapters/openalex/works.js +32 -0
  38. package/dist/adapters/openalex/works.js.map +1 -1
  39. package/dist/adapters/openreview/papers.js +5 -0
  40. package/dist/adapters/openreview/papers.js.map +1 -1
  41. package/dist/adapters/pmlr/proceedings.d.ts +35 -0
  42. package/dist/adapters/pmlr/proceedings.d.ts.map +1 -0
  43. package/dist/adapters/pmlr/proceedings.js +139 -0
  44. package/dist/adapters/pmlr/proceedings.js.map +1 -0
  45. package/dist/adapters/pubmed/articles.js +5 -0
  46. package/dist/adapters/pubmed/articles.js.map +1 -1
  47. package/dist/adapters/semantic-scholar/papers.d.ts +36 -0
  48. package/dist/adapters/semantic-scholar/papers.d.ts.map +1 -0
  49. package/dist/adapters/semantic-scholar/papers.js +214 -0
  50. package/dist/adapters/semantic-scholar/papers.js.map +1 -0
  51. package/dist/adapters/unpaywall/works.d.ts +33 -0
  52. package/dist/adapters/unpaywall/works.d.ts.map +1 -0
  53. package/dist/adapters/unpaywall/works.js +101 -0
  54. package/dist/adapters/unpaywall/works.js.map +1 -0
  55. package/dist/cli.d.ts.map +1 -1
  56. package/dist/cli.js +15 -1
  57. package/dist/cli.js.map +1 -1
  58. package/dist/commands/compute.d.ts.map +1 -1
  59. package/dist/commands/compute.js +1 -1
  60. package/dist/commands/compute.js.map +1 -1
  61. package/dist/commands/do.d.ts +30 -0
  62. package/dist/commands/do.d.ts.map +1 -0
  63. package/dist/commands/do.js +248 -0
  64. package/dist/commands/do.js.map +1 -0
  65. package/dist/commands/doctor-compute.js +11 -7
  66. package/dist/commands/doctor-compute.js.map +1 -1
  67. package/dist/commands/extract.d.ts +34 -0
  68. package/dist/commands/extract.d.ts.map +1 -0
  69. package/dist/commands/extract.js +316 -0
  70. package/dist/commands/extract.js.map +1 -0
  71. package/dist/commands/lint.js +3 -3
  72. package/dist/commands/lint.js.map +1 -1
  73. package/dist/commands/migrate-schema.d.ts.map +1 -1
  74. package/dist/commands/migrate-schema.js +26 -22
  75. package/dist/commands/migrate-schema.js.map +1 -1
  76. package/dist/commands/scholar.d.ts +33 -0
  77. package/dist/commands/scholar.d.ts.map +1 -0
  78. package/dist/commands/scholar.js +494 -0
  79. package/dist/commands/scholar.js.map +1 -0
  80. package/dist/commands/search.d.ts.map +1 -1
  81. package/dist/commands/search.js +2 -5
  82. package/dist/commands/search.js.map +1 -1
  83. package/dist/discovery/aliases.d.ts +2 -2
  84. package/dist/discovery/aliases.d.ts.map +1 -1
  85. package/dist/discovery/aliases.js +182 -11
  86. package/dist/discovery/aliases.js.map +1 -1
  87. package/dist/discovery/intents.d.ts +10 -0
  88. package/dist/discovery/intents.d.ts.map +1 -0
  89. package/dist/discovery/intents.js +255 -0
  90. package/dist/discovery/intents.js.map +1 -0
  91. package/dist/discovery/loader.d.ts.map +1 -1
  92. package/dist/discovery/loader.js +47 -2
  93. package/dist/discovery/loader.js.map +1 -1
  94. package/dist/discovery/search.d.ts +4 -1
  95. package/dist/discovery/search.d.ts.map +1 -1
  96. package/dist/discovery/search.js +28 -140
  97. package/dist/discovery/search.js.map +1 -1
  98. package/dist/electron-apps.d.ts +1 -1
  99. package/dist/electron-apps.d.ts.map +1 -1
  100. package/dist/electron-apps.js +2 -2
  101. package/dist/electron-apps.js.map +1 -1
  102. package/dist/engine/executor.d.ts +1 -1
  103. package/dist/engine/executor.js +7 -7
  104. package/dist/engine/executor.js.map +1 -1
  105. package/dist/engine/repair/remedies.js +3 -3
  106. package/dist/engine/repair/remedies.js.map +1 -1
  107. package/dist/engine/steps/desktop-ax.d.ts +4 -0
  108. package/dist/engine/steps/desktop-ax.d.ts.map +1 -1
  109. package/dist/engine/steps/desktop-ax.js +8 -0
  110. package/dist/engine/steps/desktop-ax.js.map +1 -1
  111. package/dist/engine/steps/desktop-sidecar.d.ts +1 -1
  112. package/dist/engine/steps/desktop-sidecar.js +1 -1
  113. package/dist/engine/steps/index.d.ts +1 -1
  114. package/dist/engine/steps/index.d.ts.map +1 -1
  115. package/dist/engine/steps/index.js +1 -1
  116. package/dist/engine/steps/index.js.map +1 -1
  117. package/dist/engine/steps/visual.d.ts +47 -0
  118. package/dist/engine/steps/visual.d.ts.map +1 -0
  119. package/dist/engine/steps/visual.js +66 -0
  120. package/dist/engine/steps/visual.js.map +1 -0
  121. package/dist/engine/transport/mcp-browser.d.ts +1 -1
  122. package/dist/engine/transport/mcp-browser.js +1 -1
  123. package/dist/fast-path/handlers/discovery.d.ts.map +1 -1
  124. package/dist/fast-path/handlers/discovery.js +17 -3
  125. package/dist/fast-path/handlers/discovery.js.map +1 -1
  126. package/dist/manifest-compact.txt +13 -11
  127. package/dist/manifest-search.json +1 -1
  128. package/dist/manifest.json +529 -121
  129. package/dist/mcp/handler.d.ts.map +1 -1
  130. package/dist/mcp/handler.js +14 -2
  131. package/dist/mcp/handler.js.map +1 -1
  132. package/dist/mcp/tools.d.ts.map +1 -1
  133. package/dist/mcp/tools.js +11 -3
  134. package/dist/mcp/tools.js.map +1 -1
  135. package/dist/registry.d.ts +1 -0
  136. package/dist/registry.d.ts.map +1 -1
  137. package/dist/registry.js +5 -0
  138. package/dist/registry.js.map +1 -1
  139. package/dist/transport/adapters/desktop-atspi.js +1 -1
  140. package/dist/transport/adapters/desktop-atspi.js.map +1 -1
  141. package/dist/transport/adapters/desktop-ax-background-activation-swift.d.ts +9 -0
  142. package/dist/transport/adapters/desktop-ax-background-activation-swift.d.ts.map +1 -0
  143. package/dist/transport/adapters/desktop-ax-background-activation-swift.js +99 -0
  144. package/dist/transport/adapters/desktop-ax-background-activation-swift.js.map +1 -0
  145. package/dist/transport/adapters/desktop-ax-background-click-swift.d.ts.map +1 -1
  146. package/dist/transport/adapters/desktop-ax-background-click-swift.js +10 -133
  147. package/dist/transport/adapters/desktop-ax-background-click-swift.js.map +1 -1
  148. package/dist/transport/adapters/desktop-ax-background-click.d.ts +1 -3
  149. package/dist/transport/adapters/desktop-ax-background-click.d.ts.map +1 -1
  150. package/dist/transport/adapters/desktop-ax-background-click.js +1 -69
  151. package/dist/transport/adapters/desktop-ax-background-click.js.map +1 -1
  152. package/dist/transport/adapters/desktop-ax-background-dispatch-swift.d.ts +9 -0
  153. package/dist/transport/adapters/desktop-ax-background-dispatch-swift.d.ts.map +1 -0
  154. package/dist/transport/adapters/desktop-ax-background-dispatch-swift.js +169 -0
  155. package/dist/transport/adapters/desktop-ax-background-dispatch-swift.js.map +1 -0
  156. package/dist/transport/adapters/desktop-ax-background-input-swift.d.ts +23 -0
  157. package/dist/transport/adapters/desktop-ax-background-input-swift.d.ts.map +1 -0
  158. package/dist/transport/adapters/desktop-ax-background-input-swift.js +157 -0
  159. package/dist/transport/adapters/desktop-ax-background-input-swift.js.map +1 -0
  160. package/dist/transport/adapters/desktop-ax-background-input.d.ts +13 -0
  161. package/dist/transport/adapters/desktop-ax-background-input.d.ts.map +1 -0
  162. package/dist/transport/adapters/desktop-ax-background-input.js +124 -0
  163. package/dist/transport/adapters/desktop-ax-background-input.js.map +1 -0
  164. package/dist/transport/adapters/desktop-ax-background-window-swift.d.ts +9 -0
  165. package/dist/transport/adapters/desktop-ax-background-window-swift.d.ts.map +1 -0
  166. package/dist/transport/adapters/desktop-ax-background-window-swift.js +110 -0
  167. package/dist/transport/adapters/desktop-ax-background-window-swift.js.map +1 -0
  168. package/dist/transport/adapters/desktop-ax-swift.d.ts +1 -0
  169. package/dist/transport/adapters/desktop-ax-swift.d.ts.map +1 -1
  170. package/dist/transport/adapters/desktop-ax-swift.js +1 -0
  171. package/dist/transport/adapters/desktop-ax-swift.js.map +1 -1
  172. package/dist/transport/adapters/desktop-ax.d.ts +4 -1
  173. package/dist/transport/adapters/desktop-ax.d.ts.map +1 -1
  174. package/dist/transport/adapters/desktop-ax.js +57 -6
  175. package/dist/transport/adapters/desktop-ax.js.map +1 -1
  176. package/dist/transport/adapters/desktop-uia.js +1 -1
  177. package/dist/transport/adapters/desktop-uia.js.map +1 -1
  178. package/dist/transport/adapters/visual.d.ts +114 -0
  179. package/dist/transport/adapters/visual.d.ts.map +1 -0
  180. package/dist/transport/adapters/visual.js +473 -0
  181. package/dist/transport/adapters/visual.js.map +1 -0
  182. package/dist/transport/bus.d.ts +1 -1
  183. package/dist/transport/bus.js +3 -3
  184. package/dist/transport/bus.js.map +1 -1
  185. package/dist/transport/capability.d.ts.map +1 -1
  186. package/dist/transport/capability.js +32 -30
  187. package/dist/transport/capability.js.map +1 -1
  188. package/dist/transport/cascade.js +27 -27
  189. package/dist/transport/cascade.js.map +1 -1
  190. package/dist/transport/types.d.ts +5 -5
  191. package/dist/transport/types.d.ts.map +1 -1
  192. package/dist/transport/types.js +1 -1
  193. package/dist/types/scholarly.d.ts +49 -0
  194. package/dist/types/scholarly.d.ts.map +1 -0
  195. package/dist/types/scholarly.js +16 -0
  196. package/dist/types/scholarly.js.map +1 -0
  197. package/docs/operate/compute.md +20 -7
  198. package/docs/operate/focus-behavior.md +21 -12
  199. package/docs/operate/troubleshooting.md +17 -12
  200. package/package.json +5 -11
  201. package/server.json +2 -2
  202. package/skills/unicli/SKILL.md +1 -1
  203. package/skills/unicli-claude-code/SKILL.md +1 -1
  204. package/skills/unicli-hermes/SKILL.md +1 -1
  205. package/skills/unicli-repair/references/error-codes.md +7 -7
  206. package/src/adapters/_archived/README.md +6 -6
  207. package/src/adapters/_archived/apple-music/rate-album.yaml +15 -15
  208. package/src/adapters/_archived/archive.json +2 -2
  209. package/src/adapters/acl-anthology/papers.ts +157 -0
  210. package/src/adapters/arxiv/download.yaml +1 -1
  211. package/src/adapters/arxiv/paper.yaml +1 -1
  212. package/src/adapters/arxiv/papers.ts +2 -0
  213. package/src/adapters/arxiv/search.yaml +1 -1
  214. package/src/adapters/arxiv/trending.yaml +1 -1
  215. package/src/adapters/baidu-scholar/search.ts +5 -0
  216. package/src/adapters/crossref/works.ts +209 -0
  217. package/src/adapters/cvf/papers.ts +136 -0
  218. package/src/adapters/dblp/publications.ts +4 -0
  219. package/src/adapters/figma/export-selected.yaml +16 -16
  220. package/src/adapters/google-scholar/cite.ts +1 -0
  221. package/src/adapters/google-scholar/profile.ts +5 -0
  222. package/src/adapters/google-scholar/search.ts +5 -0
  223. package/src/adapters/hf/paper.test.ts +10 -0
  224. package/src/adapters/hf/paper.ts +1 -0
  225. package/src/adapters/hf/top.yaml +1 -1
  226. package/src/adapters/huggingface-papers/daily.yaml +1 -1
  227. package/src/adapters/huggingface-papers/search.yaml +1 -1
  228. package/src/adapters/neurips/proceedings.ts +126 -0
  229. package/src/adapters/openalex/works.test.ts +40 -32
  230. package/src/adapters/openalex/works.ts +33 -0
  231. package/src/adapters/openreview/papers.ts +5 -0
  232. package/src/adapters/pmlr/proceedings.ts +167 -0
  233. package/src/adapters/pubmed/articles.ts +5 -0
  234. package/src/adapters/semantic-scholar/papers.ts +268 -0
  235. package/src/adapters/unpaywall/works.ts +138 -0
  236. package/src/adapters/zoom/toggle-mute.yaml +1 -1
  237. package/src/adapters/zotero/search.yaml +1 -1
  238. package/dist/engine/steps/cua.d.ts +0 -41
  239. package/dist/engine/steps/cua.d.ts.map +0 -1
  240. package/dist/engine/steps/cua.js +0 -59
  241. package/dist/engine/steps/cua.js.map +0 -1
  242. package/dist/transport/adapters/cua.d.ts +0 -239
  243. package/dist/transport/adapters/cua.d.ts.map +0 -1
  244. package/dist/transport/adapters/cua.js +0 -661
  245. package/dist/transport/adapters/cua.js.map +0 -1
  246. package/src/adapters/cua/bench-list.yaml +0 -28
  247. package/src/adapters/cua/bench-run.yaml +0 -40
@@ -0,0 +1,268 @@
1
+ /**
2
+ * @owner src::adapters::semantic-scholar::papers
3
+ * @does Registers Semantic Scholar Graph API paper search, detail, citations, references, and PDF discovery commands.
4
+ * @needs api.semanticscholar.org Graph v1, optional SEMANTIC_SCHOLAR_API_KEY, src/registry.ts
5
+ * @feeds src/commands/scholar.ts via scholar.* capability tags
6
+ * @breaks Graph API rate limits or response-shape drift surface as explicit adapter errors; no cached fallback is used.
7
+ * @invariants Paper references are normalized to Semantic Scholar's accepted DOI:/ARXIV:/paperId formats; output maps to ScholarlyWorkRecord.
8
+ * @side-effects HTTPS egress to api.semanticscholar.org only
9
+ * @perf O(limit) JSON mapping per command
10
+ * @concurrency safe
11
+ * @test tests/unit/adapters/scholar-sources.test.ts
12
+ * @stability experimental
13
+ * @since 2026-05-19
14
+ */
15
+
16
+ import { cli, Strategy } from "../../registry.js";
17
+ import type { ScholarlyWorkRecord } from "../../types/scholarly.js";
18
+
19
+ const API = "https://api.semanticscholar.org/graph/v1";
20
+ const FIELDS = [
21
+ "paperId",
22
+ "title",
23
+ "abstract",
24
+ "year",
25
+ "authors",
26
+ "citationCount",
27
+ "referenceCount",
28
+ "venue",
29
+ "publicationVenue",
30
+ "url",
31
+ "openAccessPdf",
32
+ "externalIds",
33
+ ].join(",");
34
+
35
+ interface S2Paper {
36
+ paperId?: unknown;
37
+ title?: unknown;
38
+ abstract?: unknown;
39
+ year?: unknown;
40
+ authors?: Array<{ name?: unknown }>;
41
+ citationCount?: unknown;
42
+ referenceCount?: unknown;
43
+ venue?: unknown;
44
+ url?: unknown;
45
+ openAccessPdf?: { url?: unknown };
46
+ externalIds?: Record<string, unknown>;
47
+ }
48
+
49
+ function str(value: unknown): string {
50
+ return typeof value === "string" ? value.trim() : "";
51
+ }
52
+
53
+ function num(value: unknown): number | undefined {
54
+ return typeof value === "number" && Number.isFinite(value)
55
+ ? value
56
+ : undefined;
57
+ }
58
+
59
+ function bareDoi(value: unknown): string {
60
+ return str(value)
61
+ .replace(/^doi:/i, "")
62
+ .replace(/^https?:\/\/(?:dx\.)?doi\.org\//i, "");
63
+ }
64
+
65
+ function bareArxiv(value: unknown): string {
66
+ return str(value)
67
+ .replace(/^arxiv:/i, "")
68
+ .replace(/^https?:\/\/arxiv\.org\/(?:abs|pdf)\//i, "")
69
+ .replace(/\.pdf$/i, "")
70
+ .replace(/v\d+$/i, "");
71
+ }
72
+
73
+ export function requireSemanticScholarPaperRef(value: unknown): string {
74
+ const raw = String(value ?? "").trim();
75
+ if (!raw) throw new Error("semantic-scholar paper reference is required.");
76
+ const doi = bareDoi(raw);
77
+ if (/^10\.\S+\/\S+/.test(doi)) return `DOI:${doi}`;
78
+ if (
79
+ /^(?:arxiv:|https?:\/\/arxiv\.org\/(?:abs|pdf)\/|\d{4}\.\d{4,5})/i.test(raw)
80
+ ) {
81
+ return `ARXIV:${bareArxiv(raw)}`;
82
+ }
83
+ if (/^[a-f0-9]{40}$/i.test(raw)) return raw;
84
+ throw new Error(
85
+ `semantic-scholar paper reference "${raw}" is not recognised.`,
86
+ );
87
+ }
88
+
89
+ function headers(): Record<string, string> {
90
+ const out: Record<string, string> = {
91
+ Accept: "application/json",
92
+ "User-Agent":
93
+ "unicli-semantic-scholar/1.0 (https://github.com/olo-dot-io/Uni-CLI)",
94
+ };
95
+ const key = process.env.SEMANTIC_SCHOLAR_API_KEY?.trim();
96
+ if (key) out["x-api-key"] = key;
97
+ return out;
98
+ }
99
+
100
+ async function fetchS2(path: string, label: string): Promise<unknown> {
101
+ const response = await fetch(`${API}${path}`, { headers: headers() });
102
+ if (response.status === 404) throw new Error(`${label} returned no result.`);
103
+ if (response.status === 429) {
104
+ throw new Error(
105
+ `${label} returned HTTP 429; set SEMANTIC_SCHOLAR_API_KEY or retry later.`,
106
+ );
107
+ }
108
+ if (!response.ok)
109
+ throw new Error(`${label} returned HTTP ${response.status}.`);
110
+ const json = (await response.json()) as {
111
+ error?: unknown;
112
+ message?: unknown;
113
+ };
114
+ if (json.error || json.message) {
115
+ throw new Error(
116
+ `${label} returned API error: ${String(json.error ?? json.message)}.`,
117
+ );
118
+ }
119
+ return json;
120
+ }
121
+
122
+ export function mapSemanticScholarPaper(
123
+ paper: S2Paper,
124
+ source: string,
125
+ ): ScholarlyWorkRecord {
126
+ const id = str(paper.paperId);
127
+ if (!id)
128
+ throw new Error("Semantic Scholar returned a paper without paperId.");
129
+ const doi = bareDoi(paper.externalIds?.DOI);
130
+ const arxiv = bareArxiv(paper.externalIds?.ArXiv);
131
+ return {
132
+ id,
133
+ title: str(paper.title),
134
+ abstract: str(paper.abstract) || undefined,
135
+ authors: Array.isArray(paper.authors)
136
+ ? paper.authors.map((author) => str(author.name)).filter(Boolean)
137
+ : undefined,
138
+ year: num(paper.year),
139
+ venue: str(paper.venue) || undefined,
140
+ doi: doi || undefined,
141
+ arxiv_id: arxiv || undefined,
142
+ semantic_scholar_id: id,
143
+ cited_by_count: num(paper.citationCount),
144
+ references_count: num(paper.referenceCount),
145
+ pdf_url: str(paper.openAccessPdf?.url) || undefined,
146
+ source_adapter: source,
147
+ source_url: str(paper.url) || `https://www.semanticscholar.org/paper/${id}`,
148
+ retrieved_at: new Date().toISOString(),
149
+ };
150
+ }
151
+
152
+ function rows(
153
+ papers: unknown,
154
+ source = "semantic-scholar",
155
+ ): ScholarlyWorkRecord[] {
156
+ const list = Array.isArray(papers) ? papers : [];
157
+ return list.map((paper) => mapSemanticScholarPaper(paper as S2Paper, source));
158
+ }
159
+
160
+ cli({
161
+ site: "semantic-scholar",
162
+ name: "search",
163
+ description: "Search Semantic Scholar papers",
164
+ domain: "api.semanticscholar.org",
165
+ strategy: Strategy.PUBLIC,
166
+ args: [
167
+ { name: "query", type: "str", required: true, positional: true },
168
+ { name: "limit", type: "int", default: 20 },
169
+ ],
170
+ columns: [
171
+ "id",
172
+ "title",
173
+ "authors",
174
+ "year",
175
+ "venue",
176
+ "doi",
177
+ "pdf_url",
178
+ "source_url",
179
+ ],
180
+ capabilities: ["http.fetch", "scholar.search"],
181
+ func: async (_page, kwargs) => {
182
+ const query = String(kwargs.query ?? "").trim();
183
+ if (!query)
184
+ throw new Error("semantic-scholar search query cannot be empty.");
185
+ const limit = Math.min(Math.max(Number(kwargs.limit ?? 20), 1), 100);
186
+ const body = (await fetchS2(
187
+ `/paper/search?query=${encodeURIComponent(query)}&limit=${limit}&fields=${encodeURIComponent(FIELDS)}`,
188
+ "semantic-scholar search",
189
+ )) as { data?: S2Paper[] };
190
+ const out = rows(body.data);
191
+ if (out.length === 0)
192
+ throw new Error(`No Semantic Scholar papers matched "${query}".`);
193
+ return out;
194
+ },
195
+ });
196
+
197
+ cli({
198
+ site: "semantic-scholar",
199
+ name: "paper",
200
+ description: "Fetch one Semantic Scholar paper by paperId, DOI, or arXiv id",
201
+ domain: "api.semanticscholar.org",
202
+ strategy: Strategy.PUBLIC,
203
+ args: [{ name: "id", type: "str", required: true, positional: true }],
204
+ columns: [
205
+ "id",
206
+ "title",
207
+ "authors",
208
+ "year",
209
+ "venue",
210
+ "doi",
211
+ "pdf_url",
212
+ "source_url",
213
+ ],
214
+ capabilities: ["http.fetch", "scholar.get", "scholar.pdf"],
215
+ func: async (_page, kwargs) => {
216
+ const ref = requireSemanticScholarPaperRef(
217
+ kwargs.id ?? kwargs.ref ?? kwargs.doi ?? kwargs.arxiv_id,
218
+ );
219
+ const paper = (await fetchS2(
220
+ `/paper/${encodeURIComponent(ref)}?fields=${encodeURIComponent(FIELDS)}`,
221
+ `semantic-scholar paper ${ref}`,
222
+ )) as S2Paper;
223
+ return [mapSemanticScholarPaper(paper, "semantic-scholar")];
224
+ },
225
+ });
226
+
227
+ for (const [name, path, cap] of [
228
+ ["citations", "citations", "scholar.citations"],
229
+ ["references", "references", "scholar.references"],
230
+ ] as const) {
231
+ cli({
232
+ site: "semantic-scholar",
233
+ name,
234
+ description: `List Semantic Scholar paper ${name}`,
235
+ domain: "api.semanticscholar.org",
236
+ strategy: Strategy.PUBLIC,
237
+ args: [
238
+ { name: "id", type: "str", required: true, positional: true },
239
+ { name: "limit", type: "int", default: 20 },
240
+ ],
241
+ columns: [
242
+ "id",
243
+ "title",
244
+ "authors",
245
+ "year",
246
+ "venue",
247
+ "doi",
248
+ "pdf_url",
249
+ "source_url",
250
+ ],
251
+ capabilities: ["http.fetch", cap],
252
+ func: async (_page, kwargs) => {
253
+ const ref = requireSemanticScholarPaperRef(kwargs.id ?? kwargs.ref);
254
+ const limit = Math.min(Math.max(Number(kwargs.limit ?? 20), 1), 100);
255
+ const body = (await fetchS2(
256
+ `/paper/${encodeURIComponent(ref)}/${path}?limit=${limit}&fields=${encodeURIComponent(FIELDS)}`,
257
+ `semantic-scholar ${name} ${ref}`,
258
+ )) as { data?: Array<{ citingPaper?: S2Paper; citedPaper?: S2Paper }> };
259
+ const papers = (body.data ?? []).map((item) =>
260
+ name === "citations" ? item.citingPaper : item.citedPaper,
261
+ );
262
+ const out = rows(papers);
263
+ if (out.length === 0)
264
+ throw new Error(`No Semantic Scholar ${name} found for ${ref}.`);
265
+ return out;
266
+ },
267
+ });
268
+ }
@@ -0,0 +1,138 @@
1
+ /**
2
+ * @owner src::adapters::unpaywall::works
3
+ * @does Registers Unpaywall DOI open-access lookup for PDF availability.
4
+ * @needs api.unpaywall.org v2, UNPAYWALL_EMAIL or --email, src/registry.ts
5
+ * @feeds src/commands/scholar.ts via scholar.pdf and scholar.get
6
+ * @breaks Missing email is an explicit invalid-input error; Unpaywall drift surfaces as adapter error, never as a fabricated PDF.
7
+ * @invariants Only DOI-shaped references are accepted; best_oa_location is preferred for PDF and landing URLs.
8
+ * @side-effects HTTPS egress to api.unpaywall.org only
9
+ * @perf O(1) per DOI
10
+ * @concurrency safe
11
+ * @test tests/unit/adapters/scholar-sources.test.ts
12
+ * @stability experimental
13
+ * @since 2026-05-19
14
+ */
15
+
16
+ import { cli, Strategy } from "../../registry.js";
17
+ import type { ScholarlyWorkRecord } from "../../types/scholarly.js";
18
+
19
+ const API = "https://api.unpaywall.org/v2";
20
+
21
+ interface OaLocation {
22
+ url_for_pdf?: unknown;
23
+ url_for_landing_page?: unknown;
24
+ host_type?: unknown;
25
+ version?: unknown;
26
+ license?: unknown;
27
+ }
28
+
29
+ interface UnpaywallWork {
30
+ doi?: unknown;
31
+ title?: unknown;
32
+ is_oa?: unknown;
33
+ oa_status?: unknown;
34
+ best_oa_location?: OaLocation | null;
35
+ }
36
+
37
+ function str(value: unknown): string {
38
+ return typeof value === "string" ? value.trim() : "";
39
+ }
40
+
41
+ function bareDoi(value: unknown): string {
42
+ return str(value)
43
+ .replace(/^doi:/i, "")
44
+ .replace(/^https?:\/\/(?:dx\.)?doi\.org\//i, "");
45
+ }
46
+
47
+ export function requireUnpaywallDoi(value: unknown): string {
48
+ const doi = bareDoi(value);
49
+ if (!/^10\.\S+\/\S+/.test(doi)) {
50
+ throw new Error(
51
+ `unpaywall DOI "${String(value ?? "")}" is not recognised.`,
52
+ );
53
+ }
54
+ return doi;
55
+ }
56
+
57
+ function requireEmail(value: unknown): string {
58
+ const email = str(value) || process.env.UNPAYWALL_EMAIL?.trim() || "";
59
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
60
+ throw new Error("unpaywall lookup requires --email or UNPAYWALL_EMAIL.");
61
+ }
62
+ return email;
63
+ }
64
+
65
+ export function mapUnpaywallWork(
66
+ work: UnpaywallWork,
67
+ source: string,
68
+ ): ScholarlyWorkRecord {
69
+ const doi = requireUnpaywallDoi(work.doi);
70
+ const best = work.best_oa_location ?? {};
71
+ return {
72
+ id: doi,
73
+ title: str(work.title),
74
+ doi,
75
+ is_open_access: work.is_oa === true,
76
+ oa_status: str(work.oa_status) || undefined,
77
+ pdf_url: str(best.url_for_pdf) || undefined,
78
+ landing_url: str(best.url_for_landing_page) || `https://doi.org/${doi}`,
79
+ type:
80
+ [str(best.host_type), str(best.version), str(best.license)]
81
+ .filter(Boolean)
82
+ .join(":") || undefined,
83
+ source_adapter: source,
84
+ source_url: str(best.url_for_landing_page) || `https://doi.org/${doi}`,
85
+ retrieved_at: new Date().toISOString(),
86
+ };
87
+ }
88
+
89
+ async function fetchUnpaywall(
90
+ doi: string,
91
+ email: string,
92
+ ): Promise<UnpaywallWork> {
93
+ const response = await fetch(
94
+ `${API}/${encodeURIComponent(doi)}?email=${encodeURIComponent(email)}`,
95
+ {
96
+ headers: {
97
+ Accept: "application/json",
98
+ "User-Agent":
99
+ "unicli-unpaywall/1.0 (https://github.com/olo-dot-io/Uni-CLI)",
100
+ },
101
+ },
102
+ );
103
+ if (response.status === 404)
104
+ throw new Error(`Unpaywall returned no result for ${doi}.`);
105
+ if (response.status === 422)
106
+ throw new Error("Unpaywall rejected the email parameter.");
107
+ if (response.status === 429) throw new Error("Unpaywall returned HTTP 429.");
108
+ if (!response.ok)
109
+ throw new Error(`Unpaywall returned HTTP ${response.status}.`);
110
+ return response.json() as Promise<UnpaywallWork>;
111
+ }
112
+
113
+ cli({
114
+ site: "unpaywall",
115
+ name: "oa",
116
+ description: "Find open-access PDF availability for a DOI via Unpaywall",
117
+ domain: "api.unpaywall.org",
118
+ strategy: Strategy.PUBLIC,
119
+ args: [
120
+ { name: "doi", type: "str", required: true, positional: true },
121
+ { name: "email", type: "str", description: "Unpaywall requester email" },
122
+ ],
123
+ columns: [
124
+ "id",
125
+ "title",
126
+ "doi",
127
+ "is_open_access",
128
+ "oa_status",
129
+ "pdf_url",
130
+ "source_url",
131
+ ],
132
+ capabilities: ["http.fetch", "scholar.get", "scholar.pdf"],
133
+ func: async (_page, kwargs) => {
134
+ const doi = requireUnpaywallDoi(kwargs.doi ?? kwargs.id ?? kwargs.ref);
135
+ const email = requireEmail(kwargs.email);
136
+ return [mapUnpaywallWork(await fetchUnpaywall(doi, email), "unpaywall")];
137
+ },
138
+ });
@@ -1,6 +1,6 @@
1
1
  # Pure AX demo — toggle microphone mute in an in-progress Zoom meeting.
2
2
  #
3
- # No CUA involved. Zoom's "Meeting > Mute/Unmute Audio" menu item is
3
+ # No Visual involved. Zoom's "Meeting > Mute/Unmute Audio" menu item is
4
4
  # deterministic and driven entirely through the Accessibility API.
5
5
  site: zoom
6
6
  name: toggle-mute
@@ -38,7 +38,7 @@ pipeline:
38
38
  columns: [key, title, type, date, creators]
39
39
 
40
40
  # schema-v2 metadata — injected by `unicli migrate schema-v2`
41
- capabilities: ["http.fetch"]
41
+ capabilities: ["http.fetch", "scholar.search"]
42
42
  minimum_capability: http.fetch
43
43
  trust: public
44
44
  confidentiality: public
@@ -1,41 +0,0 @@
1
- /**
2
- * CUA pipeline step handlers — every cua_* action delegates to the
3
- * transport bus (`bus.require`/`adapter.open`/`adapter.action`). The bus
4
- * routes to whichever transport declares the step; `action()` never
5
- * throws — callers always receive an envelope.
6
- */
7
- import type { Envelope } from "../../core/envelope.js";
8
- import type { TransportBus, TransportContext } from "../../transport/types.js";
9
- /** Shared shape used by every cua_* handler. */
10
- export interface CuaStepContext {
11
- bus: TransportBus;
12
- transportCtx: TransportContext;
13
- platform?: NodeJS.Platform;
14
- }
15
- export declare function handleCuaSnapshot(ctx: CuaStepContext, params?: Record<string, unknown>): Promise<Envelope<unknown>>;
16
- export declare function handleCuaClick(ctx: CuaStepContext, params: Record<string, unknown>): Promise<Envelope<unknown>>;
17
- export declare function handleCuaType(ctx: CuaStepContext, params: Record<string, unknown>): Promise<Envelope<unknown>>;
18
- export declare function handleCuaKey(ctx: CuaStepContext, params: Record<string, unknown>): Promise<Envelope<unknown>>;
19
- export declare function handleCuaScroll(ctx: CuaStepContext, params: Record<string, unknown>): Promise<Envelope<unknown>>;
20
- export declare function handleCuaDrag(ctx: CuaStepContext, params: Record<string, unknown>): Promise<Envelope<unknown>>;
21
- export declare function handleCuaWait(ctx: CuaStepContext, params: Record<string, unknown>): Promise<Envelope<unknown>>;
22
- export declare function handleCuaAssert(ctx: CuaStepContext, params: Record<string, unknown>): Promise<Envelope<unknown>>;
23
- export declare function handleCuaAsk(ctx: CuaStepContext, params: Record<string, unknown>): Promise<Envelope<unknown>>;
24
- export declare function handleCuaBackend(ctx: CuaStepContext, params?: Record<string, unknown>): Promise<Envelope<unknown>>;
25
- export declare function handleCuaLaunch(ctx: CuaStepContext, params: Record<string, unknown>): Promise<Envelope<unknown>>;
26
- /** Kind → handler dispatch table (consumed by executor.dispatchBusStep in src/engine/executor.ts). */
27
- export declare const CUA_STEP_HANDLERS: {
28
- readonly cua_snapshot: typeof handleCuaSnapshot;
29
- readonly cua_click: typeof handleCuaClick;
30
- readonly cua_type: typeof handleCuaType;
31
- readonly cua_key: typeof handleCuaKey;
32
- readonly cua_scroll: typeof handleCuaScroll;
33
- readonly cua_drag: typeof handleCuaDrag;
34
- readonly cua_wait: typeof handleCuaWait;
35
- readonly cua_assert: typeof handleCuaAssert;
36
- readonly cua_ask: typeof handleCuaAsk;
37
- readonly cua_backend: typeof handleCuaBackend;
38
- readonly cua_launch: typeof handleCuaLaunch;
39
- };
40
- export type CuaStepKind = keyof typeof CUA_STEP_HANDLERS;
41
- //# sourceMappingURL=cua.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cua.d.ts","sourceRoot":"","sources":["../../../src/engine/steps/cua.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAGV,YAAY,EACZ,gBAAgB,EACjB,MAAM,0BAA0B,CAAC;AAElC,gDAAgD;AAChD,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,YAAY,CAAC;IAClB,YAAY,EAAE,gBAAgB,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC5B;AAYD,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,cAAc,EACnB,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAE5B;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAE5B;AAED,wBAAsB,aAAa,CACjC,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAE5B;AAED,wBAAsB,YAAY,CAChC,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAE5B;AAED,wBAAsB,eAAe,CACnC,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAE5B;AAED,wBAAsB,aAAa,CACjC,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAE5B;AAED,wBAAsB,aAAa,CACjC,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAE5B;AAED,wBAAsB,eAAe,CACnC,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAE5B;AAED,wBAAsB,YAAY,CAChC,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAE5B;AAED,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,cAAc,EACnB,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAE5B;AAED,wBAAsB,eAAe,CACnC,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAE5B;AAED,sGAAsG;AACtG,eAAO,MAAM,iBAAiB;;;;;;;;;;;;CAYpB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,iBAAiB,CAAC"}
@@ -1,59 +0,0 @@
1
- /**
2
- * CUA pipeline step handlers — every cua_* action delegates to the
3
- * transport bus (`bus.require`/`adapter.open`/`adapter.action`). The bus
4
- * routes to whichever transport declares the step; `action()` never
5
- * throws — callers always receive an envelope.
6
- */
7
- async function dispatch(ctx, kind, params) {
8
- const adapter = ctx.bus.require(kind, ctx.platform);
9
- await adapter.open(ctx.transportCtx);
10
- return (await adapter.action({ kind, params }));
11
- }
12
- export async function handleCuaSnapshot(ctx, params = {}) {
13
- return dispatch(ctx, "cua_snapshot", params);
14
- }
15
- export async function handleCuaClick(ctx, params) {
16
- return dispatch(ctx, "cua_click", params);
17
- }
18
- export async function handleCuaType(ctx, params) {
19
- return dispatch(ctx, "cua_type", params);
20
- }
21
- export async function handleCuaKey(ctx, params) {
22
- return dispatch(ctx, "cua_key", params);
23
- }
24
- export async function handleCuaScroll(ctx, params) {
25
- return dispatch(ctx, "cua_scroll", params);
26
- }
27
- export async function handleCuaDrag(ctx, params) {
28
- return dispatch(ctx, "cua_drag", params);
29
- }
30
- export async function handleCuaWait(ctx, params) {
31
- return dispatch(ctx, "cua_wait", params);
32
- }
33
- export async function handleCuaAssert(ctx, params) {
34
- return dispatch(ctx, "cua_assert", params);
35
- }
36
- export async function handleCuaAsk(ctx, params) {
37
- return dispatch(ctx, "cua_ask", params);
38
- }
39
- export async function handleCuaBackend(ctx, params = {}) {
40
- return dispatch(ctx, "cua_backend", params);
41
- }
42
- export async function handleCuaLaunch(ctx, params) {
43
- return dispatch(ctx, "cua_launch", params);
44
- }
45
- /** Kind → handler dispatch table (consumed by executor.dispatchBusStep in src/engine/executor.ts). */
46
- export const CUA_STEP_HANDLERS = {
47
- cua_snapshot: handleCuaSnapshot,
48
- cua_click: handleCuaClick,
49
- cua_type: handleCuaType,
50
- cua_key: handleCuaKey,
51
- cua_scroll: handleCuaScroll,
52
- cua_drag: handleCuaDrag,
53
- cua_wait: handleCuaWait,
54
- cua_assert: handleCuaAssert,
55
- cua_ask: handleCuaAsk,
56
- cua_backend: handleCuaBackend,
57
- cua_launch: handleCuaLaunch,
58
- };
59
- //# sourceMappingURL=cua.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cua.js","sourceRoot":"","sources":["../../../src/engine/steps/cua.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAiBH,KAAK,UAAU,QAAQ,CACrB,GAAmB,EACnB,IAAY,EACZ,MAA+B;IAE/B,MAAM,OAAO,GAAqB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;IACtE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACrC,OAAO,CAAC,MAAM,OAAO,CAAC,MAAM,CAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAoB,CAAC;AACxE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,GAAmB,EACnB,SAAkC,EAAE;IAEpC,OAAO,QAAQ,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,GAAmB,EACnB,MAA+B;IAE/B,OAAO,QAAQ,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,GAAmB,EACnB,MAA+B;IAE/B,OAAO,QAAQ,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAAmB,EACnB,MAA+B;IAE/B,OAAO,QAAQ,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,GAAmB,EACnB,MAA+B;IAE/B,OAAO,QAAQ,CAAC,GAAG,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,GAAmB,EACnB,MAA+B;IAE/B,OAAO,QAAQ,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,GAAmB,EACnB,MAA+B;IAE/B,OAAO,QAAQ,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,GAAmB,EACnB,MAA+B;IAE/B,OAAO,QAAQ,CAAC,GAAG,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAAmB,EACnB,MAA+B;IAE/B,OAAO,QAAQ,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAAmB,EACnB,SAAkC,EAAE;IAEpC,OAAO,QAAQ,CAAC,GAAG,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,GAAmB,EACnB,MAA+B;IAE/B,OAAO,QAAQ,CAAC,GAAG,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;AAC7C,CAAC;AAED,sGAAsG;AACtG,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,YAAY,EAAE,iBAAiB;IAC/B,SAAS,EAAE,cAAc;IACzB,QAAQ,EAAE,aAAa;IACvB,OAAO,EAAE,YAAY;IACrB,UAAU,EAAE,eAAe;IAC3B,QAAQ,EAAE,aAAa;IACvB,QAAQ,EAAE,aAAa;IACvB,UAAU,EAAE,eAAe;IAC3B,OAAO,EAAE,YAAY;IACrB,WAAW,EAAE,gBAAgB;IAC7B,UAAU,EAAE,eAAe;CACnB,CAAC"}