@yottagraph-app/aether-instructions 1.1.17 → 1.1.19

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.
@@ -1,310 +0,0 @@
1
- # Relationships
2
-
3
- Relationships (also called "links") represent connections between entities in the Knowledge Graph. These connections are derived from articles.
4
-
5
- ## When to Use
6
-
7
- - You want to find entities connected to a given entity
8
- - You need to understand how two entities are related
9
- - You're exploring a network of connections (e.g., "Who is connected to this person?")
10
- - You need link counts or relationship strength between entities
11
-
12
- ## Key Concepts
13
-
14
- - **Linked Entities**: Other entities that have a relationship with a given entity
15
- - **Link Strength**: Represents the significance of the relationship between two entities
16
- - **Link Counts**: Number of connections between entity pairs, useful for measuring relationship strength
17
- - **Relationship Types**: Common types include:
18
- - `competes_with`
19
- - `compares_to`
20
- - `customer_of`
21
- - `partnered_with`
22
- - `trades_with`
23
- - `invests_in`
24
- - `is_related_to`
25
- - `sues`
26
- - `provides_support_to`
27
-
28
- ## Tips
29
-
30
- - Relationships are bidirectional—if A links to B, B links to A
31
- - High link counts indicate stronger or more frequent connections in the news
32
- - Use with graph endpoints to visualize relationship networks
33
- - **`entity_type` filter limitation**: see the Guidance note on "Get linked entities" below — only three entity types are supported
34
-
35
- <!-- BEGIN GENERATED CONTENT -->
36
-
37
- ## Endpoints
38
-
39
- ### Get linked entities
40
-
41
- `GET /entities/{source_neid}/linked`
42
-
43
- Get list of entities linked to a source entity, optionally filtered by entity type and link type
44
-
45
- #### Parameters
46
-
47
- | Name | Type | Required | Description |
48
- |------|------|----------|-------------|
49
- | source_neid | string | yes | Source entity NEID |
50
- | entity_type | string[] | no | Filter by entity type(s) |
51
- | link_type | string[] | no | Filter by link type(s) |
52
-
53
- #### Responses
54
-
55
- | Status | Description |
56
- |--------|-------------|
57
- | 200 | List of linked entity NEIDs (`GetLinkedEntitiesResponse`) |
58
- | 400 | Invalid parameters (`Error`) |
59
- | 404 | Source entity not found (`Error`) |
60
- | 500 | Internal server error (`Error`) |
61
-
62
- #### Example
63
-
64
- **Request:**
65
-
66
- ```
67
- GET /entities/00416400910670863867/linked
68
- ```
69
-
70
- **Response:**
71
-
72
- ```json
73
- {"entities": ["00416400910670863867"]}
74
- ```
75
-
76
- ---
77
-
78
- ### Get links between entities
79
-
80
- `GET /entities/{source_neid}/links/{target_neid}`
81
-
82
- Get list of links between source and target entities, optionally filtered by link type
83
-
84
- #### Parameters
85
-
86
- | Name | Type | Required | Description |
87
- |------|------|----------|-------------|
88
- | source_neid | string | yes | Source entity NEID |
89
- | target_neid | string | yes | Target entity NEID |
90
- | link_type | string[] | no | Filter by link type(s) |
91
- | include_mentions | boolean | no | Include mention details in response |
92
- | include_articles | boolean | no | Include article details in response |
93
-
94
- #### Responses
95
-
96
- | Status | Description |
97
- |--------|-------------|
98
- | 200 | List of entity links (`GetLinksResponse`) |
99
- | 400 | Invalid parameters (`Error`) |
100
- | 404 | Source or target entity not found (`Error`) |
101
- | 500 | Internal server error (`Error`) |
102
-
103
- #### Example
104
-
105
- **Request:**
106
-
107
- ```
108
- GET /entities/00416400910670863867/links/04358848009837283240
109
- ```
110
-
111
- **Response:**
112
-
113
- ```json
114
- {"links": []}
115
- ```
116
-
117
- ---
118
-
119
- ### Get graph layout
120
-
121
- `GET /graph/{center_neid}/layout`
122
-
123
- Get nodes and edges layout data for visualizing a relationship graph. Response is cached for 5 minutes.
124
-
125
- #### Guidance
126
-
127
- Only use this endpoint if you care about graph-specific properties. Otherwise it is faster to use /entities/{source_neid}/links/{target_neid}. This should always be called with a non-empty list of neids. A typical pattern is to call /graph/{center_neid}/neighborhood and then use the returned NEIDs to call this endpoint.
128
-
129
- #### Parameters
130
-
131
- | Name | Type | Required | Description |
132
- |------|------|----------|-------------|
133
- | center_neid | string | yes | Center entity NEID |
134
- | neid | string[] | no | Additional entity NEIDs to include in layout |
135
- | borderMinX | number | no | Minimum X border for layout |
136
- | borderMinY | number | no | Minimum Y border for layout |
137
- | borderMaxX | number | no | Maximum X border for layout |
138
- | borderMaxY | number | no | Maximum Y border for layout |
139
-
140
- #### Responses
141
-
142
- | Status | Description |
143
- |--------|-------------|
144
- | 200 | Graph layout with nodes and edges (`GraphLayoutResponse`) |
145
- | 400 | Invalid parameters (`Error`) |
146
- | 500 | Internal server error (`Error`) |
147
-
148
- #### Example
149
-
150
- **Request:**
151
-
152
- ```
153
- GET /graph/00416400910670863867/layout?neid=04358848009837283240
154
- ```
155
-
156
- **Response:**
157
-
158
- ```json
159
- {"nodes": [{"neid": "00416400910670863867", "label": "organization|Apple|nationality: us...", "isCentralNode": true, "x": -333.33, "y": -200, "width": 666.67, "height": 266.67}], "edges": [{"source": "00416400910670863867", "target": "04358848009837283240", "label": "competes_with", "path": [{"X": 0, "Y": 0}, {"X": 0, "Y": 66.67}], "article_ids": ["02861951941133789623"], "snippets": ["Apple and Google are mentioned as companies..."], "weight": 0.0267}]}
160
- ```
161
-
162
- ---
163
-
164
- ### Get graph neighborhood
165
-
166
- `GET /graph/{center_neid}/neighborhood`
167
-
168
- Get list of neighboring entities in the relationship graph, optionally filtered by entity type. Response is cached for 5 minutes.
169
-
170
- #### Guidance
171
-
172
- Only use this endpoint if you care about graph-specific properties. Otherwise it is faster to use /entities/{source_neid}/linked. Response includes the center entity itself in the results with a weight of 1.0. The 'neighbors' and 'weights' arrays are parallel (same indices correspond).
173
-
174
- #### Parameters
175
-
176
- | Name | Type | Required | Description |
177
- |------|------|----------|-------------|
178
- | center_neid | string | yes | Center entity NEID |
179
- | size | integer | no | Maximum number of neighbors to return |
180
- | type | string[] | no | Filter by entity type(s) |
181
-
182
- #### Responses
183
-
184
- | Status | Description |
185
- |--------|-------------|
186
- | 200 | Neighbors and their weights (`GraphNeighborhoodResponse`) |
187
- | 400 | Invalid parameters (`Error`) |
188
- | 404 | Center entity not found (`Error`) |
189
- | 500 | Internal server error (`Error`) |
190
-
191
- #### Example
192
-
193
- **Request:**
194
-
195
- ```
196
- GET /graph/00416400910670863867/neighborhood?size=5
197
- ```
198
-
199
- **Response:**
200
-
201
- ```json
202
- {"neighbors": ["00416400910670863867", "04358848009837283240", "00315863961550087877"], "weights": [1, 0.0267, 0.0167]}
203
- ```
204
-
205
- ---
206
-
207
- ### Get link counts between entities
208
-
209
- `GET /graph/{source_neid}/links/{target_neid}/counts`
210
-
211
- Get counts of links between source and target entities over time
212
-
213
- #### Guidance
214
-
215
- Only use this endpoint if you care about graph-specific properties. Otherwise it is faster to use /entities/{source_neid}/links/{target_neid}. Returns link_counts object with relationship types as keys. Values are arrays of timestamps in Excel/OLE serial date format (days since 1899-12-30). Example: 46019.64453125 = 2025-12-28 15:28:07 UTC. The count for each relationship type is the array length (number of observations).
216
-
217
- #### Parameters
218
-
219
- | Name | Type | Required | Description |
220
- |------|------|----------|-------------|
221
- | source_neid | string | yes | Source entity NEID |
222
- | target_neid | string | yes | Target entity NEID |
223
-
224
- #### Responses
225
-
226
- | Status | Description |
227
- |--------|-------------|
228
- | 200 | Link counts by type (`GetLinkCountsResponse`) |
229
- | 400 | Invalid parameters (`Error`) |
230
- | 500 | Internal server error (`Error`) |
231
-
232
- #### Example
233
-
234
- **Request:**
235
-
236
- ```
237
- GET /graph/00416400910670863867/links/04358848009837283240/counts
238
- ```
239
-
240
- **Response:**
241
-
242
- ```json
243
- {"link_counts": {"compares_to": [46019.64453125, 45999.3515625], "competes_with": [45847.5625, 45850.60546875], "customer_of": [45836.9296875], "partnered_with": [], "trades_with": []}}
244
- ```
245
-
246
- ## Types
247
-
248
- ### EntityLink
249
-
250
- A link between two entities
251
-
252
- | Field | Type | Description |
253
- |-------|------|-------------|
254
- | article_ids | string[] | Article IDs where this link was found |
255
- | recorded_at | string | Time when the link was recorded @Format date-time |
256
- | source | string | Source entity NEID |
257
- | target | string | Target entity NEID |
258
- | type | string | Type of link/relationship |
259
-
260
- ### GetLinkCountsResponse
261
-
262
- Response containing link counts between entities
263
-
264
- | Field | Type | Description |
265
- |-------|------|-------------|
266
- | link_counts | object | Map of link type to count values |
267
-
268
- ### GetLinkedEntitiesResponse
269
-
270
- Response containing linked entities
271
-
272
- | Field | Type | Description |
273
- |-------|------|-------------|
274
- | entities | string[] | List of linked entity NEIDs |
275
-
276
- ### GetLinksResponse
277
-
278
- Response containing links between entities
279
-
280
- | Field | Type | Description |
281
- |-------|------|-------------|
282
- | articles | `ArticleDetail`[] | Articles tied to the returned links (only included when requested) |
283
- | links | `EntityLink`[] | List of entity links |
284
- | mentions | `MentionDetail`[] | Mentions tied to the returned links (only included when requested) |
285
-
286
- ### GraphNeighborhoodResponse
287
-
288
- Response containing neighbors of an entity in the relationship graph
289
-
290
- | Field | Type | Description |
291
- |-------|------|-------------|
292
- | neighbors | string[] | List of neighbor NEIDs |
293
- | weights | number[] | Weights corresponding to each neighbor (same order as neighbors) |
294
-
295
- ### LinkedExpression
296
-
297
- | Field | Type | Description |
298
- |-------|------|-------------|
299
- | **linked** | `Linked` | |
300
-
301
- ### Linked
302
-
303
- | Field | Type | Description |
304
- |-------|------|-------------|
305
- | **distance** | integer | Maximum relationship distance to traverse |
306
- | pids | integer[] | Property identifiers defining the relationship types to follow |
307
- | to_entity | string | Target entity ID for relationship traversal |
308
- | direction | string | Direction of relationship traversal. 'outgoing' (default) follows subject->value edges, 'incoming' follows value->subject (reverse) edges, 'both' unions outgoing and incoming results. |
309
-
310
- <!-- END GENERATED CONTENT -->
@@ -1,93 +0,0 @@
1
- # Sentiment
2
-
3
- Sentiment analysis measures how positively or negatively an entity is portrayed in news coverage. The API provides both daily aggregate scores and scores for individual articles.
4
-
5
- ## When to Use
6
-
7
- - You want to know if news coverage about an entity is positive or negative
8
- - You need sentiment trends over time (e.g., "How has sentiment changed this month?")
9
- - You're comparing sentiment between entities or time periods
10
-
11
- ## Key Concepts
12
-
13
- - **Sentiment Score**: Ranges from -1 (very negative) to +1 (very positive), with 0 being neutral.
14
- - Individual article/mention scores are discrete values: `-1`, `-0.75`, `-0.5`, `0`, `0.5`, `0.75`, `1`
15
- - Daily aggregate scores can be any value between -1 and 1 (continuous)
16
- - **Entity-level vs. Mention-level**: Aggregate sentiment for an entity vs. sentiment of a specific mention
17
-
18
- ## Tips
19
-
20
- - Sentiment without a time range returns recent data; always specify dates for historical analysis
21
- - High mention volume with neutral sentiment may indicate routine coverage
22
- - Sudden sentiment shifts often correlate with specific events
23
-
24
- <!-- BEGIN GENERATED CONTENT -->
25
-
26
- ## Endpoints
27
-
28
- ### Get entity sentiment data
29
-
30
- `GET /graph/{neid}/sentiment`
31
-
32
- Get sentiment analysis data for a named entity including scores, timestamps, and daily averages. Response is cached for 5 minutes.
33
-
34
- #### Guidance
35
-
36
- Response is wrapped in a 'sentiment' container object. The sentiment_scores field is a flat array of numbers (not timestamped objects). The time interval for the scores is provided separately in time_interval.start and time_interval.end.
37
-
38
- #### Parameters
39
-
40
- | Name | Type | Required | Description |
41
- |------|------|----------|-------------|
42
- | neid | string | yes | Named Entity ID |
43
-
44
- #### Responses
45
-
46
- | Status | Description |
47
- |--------|-------------|
48
- | 200 | Sentiment data for the entity (`GetNamedEntitySentimentResponse`) |
49
- | 400 | Invalid NEID (`Error`) |
50
- | 404 | Entity not found (`Error`) |
51
- | 500 | Internal server error (`Error`) |
52
-
53
- #### Example
54
-
55
- **Request:**
56
-
57
- ```
58
- GET /graph/00416400910670863867/sentiment
59
- ```
60
-
61
- **Response:**
62
-
63
- ```json
64
- {"sentiment": {"neid": "00416400910670863867", "time_interval": {"start": "2026-01-04T03:02:25.384Z", "end": "2026-02-03T03:02:25.384Z"}, "num_mentions": 28662, "sentiment_scores": [0.5, 0.5, 0.5, 0, 0.5, -0.5, 1]}}
65
- ```
66
-
67
- ## Types
68
-
69
- ### GetNamedEntitySentimentResponse
70
-
71
- Response containing sentiment data for a named entity
72
-
73
- | Field | Type | Description |
74
- |-------|------|-------------|
75
- | sentiment | `NamedEntitySentiment` | |
76
-
77
- ### NamedEntitySentiment
78
-
79
- The sentiment data
80
-
81
- | Field | Type | Description |
82
- |-------|------|-------------|
83
- | daily_timestamps | string[] | Timestamps associated with each daily sentiment trend |
84
- | direct_sentiment_daily_averages | number[] | The average direct sentiment score for the entity for each day in the time interval |
85
- | neid | string | Named Entity ID |
86
- | num_mentions | integer | Number of times the entity has been directly mentioned in an article |
87
- | propagated_sentiment_daily_averages | number[] | The average propagated sentiment score for the entity for each day in the time interval |
88
- | sentiment_article_nindexes | string[] | Nindexes of the articles that correspond to each direct sentiment score |
89
- | sentiment_scores | number[] | All direct sentiment scores for the entity |
90
- | sentiment_timestamps | string[] | Timestamps associated with each direct sentiment score |
91
- | time_interval | `TimeInterval` | |
92
-
93
- <!-- END GENERATED CONTENT -->
@@ -1,63 +0,0 @@
1
- ---
2
- name: test-api-queries
3
- description: Test Elemental API queries before integrating into app code. Use when writing code that calls the query server.
4
- ---
5
-
6
- # Test API Queries
7
-
8
- **When writing code that calls the Query Server, test queries with this CLI tool before integrating them into app code.**
9
-
10
- This prevents wasted iteration cycles from incorrect assumptions about API responses.
11
-
12
- ## Workflow
13
-
14
- 1. **Test the query** using the CLI tool below
15
- 2. **Verify the response** matches what you expect
16
- 3. **Then write the app code** with confidence
17
-
18
- ## CLI Tool
19
-
20
- Location: `~/.cursor/skills/test-api-queries/query-api.js`
21
-
22
- ### Requirements
23
-
24
- The CLI reads these from the project's `.env` file or shell environment:
25
-
26
- - `AUTH0_M2M_DEV_TOKEN` - Auth0 M2M dev token for API access
27
- - `NUXT_PUBLIC_QUERY_SERVER_ADDRESS` - Query server URL
28
-
29
- **Before using this tool**, check that both variables are set in the project's `.env` file. If either is missing or empty, add them to `.env` (see `.env.example` for the format). Tell the user to ask the engineering team for the token value if they don't have it.
30
-
31
- ### Usage
32
-
33
- ```bash
34
- node ~/.cursor/skills/test-api-queries/query-api.js <METHOD> <ENDPOINT> [JSON_PARAMS]
35
- ```
36
-
37
- ### Examples
38
-
39
- ```bash
40
- # Search for entities by name
41
- node ~/.cursor/skills/test-api-queries/query-api.js POST /entities/search \
42
- '{"queries":[{"queryId":1,"query":"Apple"}],"maxResults":3}'
43
-
44
- # Get entity details by ID
45
- node ~/.cursor/skills/test-api-queries/query-api.js GET /entities/00416400910670863867
46
-
47
- # Find entities by type
48
- node ~/.cursor/skills/test-api-queries/query-api.js POST /elemental/find \
49
- '{"expression":{"type":"is_type","is_type":{"fid":10}},"limit":5}'
50
-
51
- # Find entities with a specific property
52
- node ~/.cursor/skills/test-api-queries/query-api.js POST /elemental/find \
53
- '{"expression":{"type":"comparison","comparison":{"operator":"has_value","pid":313}},"limit":10}'
54
-
55
- # Get entity properties
56
- node ~/.cursor/skills/test-api-queries/query-api.js POST /elemental/entities/properties \
57
- '{"eids":["00416400910670863867"],"pids":[8,313]}'
58
- ```
59
-
60
- ### Notes
61
-
62
- - Run from the project directory so the `.env` file is found
63
- - `/elemental/find` and `/elemental/entities/properties` require form-encoded bodies—the tool handles this automatically
@@ -1,172 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Simple CLI tool to query the Elemental API.
4
- *
5
- * Auth: set AUTH0_M2M_DEV_TOKEN as an env var or in your .env file.
6
- * Server: set NUXT_PUBLIC_QUERY_SERVER_ADDRESS in your .env file.
7
- */
8
-
9
- const fs = require('fs');
10
- const path = require('path');
11
-
12
- const FORM_ENCODED_ENDPOINTS = ['/elemental/find', '/elemental/entities/properties'];
13
-
14
- function loadEnvFile() {
15
- // Try current working directory first (project .env), then script directory
16
- const envPaths = [path.join(process.cwd(), '.env'), path.join(__dirname, '.env')];
17
-
18
- for (const envPath of envPaths) {
19
- try {
20
- const contents = fs.readFileSync(envPath, 'utf8');
21
- for (const line of contents.split('\n')) {
22
- const trimmed = line.trim();
23
- if (!trimmed || trimmed.startsWith('#')) continue;
24
- const eqIndex = trimmed.indexOf('=');
25
- if (eqIndex === -1) continue;
26
- const key = trimmed.slice(0, eqIndex).trim();
27
- const value = trimmed.slice(eqIndex + 1).trim();
28
- if (!process.env[key]) {
29
- process.env[key] = value;
30
- }
31
- }
32
- return; // Stop after first successful load
33
- } catch {
34
- // Try next path
35
- }
36
- }
37
- }
38
-
39
- function requiresFormEncoding(endpoint) {
40
- return FORM_ENCODED_ENDPOINTS.some((e) => endpoint.startsWith(e));
41
- }
42
-
43
- function buildFormBody(endpoint, params) {
44
- const formParams = new URLSearchParams();
45
- for (const [key, value] of Object.entries(params)) {
46
- if (typeof value === 'object') {
47
- formParams.append(key, JSON.stringify(value));
48
- } else {
49
- formParams.append(key, String(value));
50
- }
51
- }
52
- return formParams.toString();
53
- }
54
-
55
- async function main() {
56
- loadEnvFile();
57
-
58
- const args = process.argv.slice(2);
59
-
60
- if (args.length < 2 || args[0] === '--help' || args[0] === '-h') {
61
- console.log(`
62
- Elemental API Query Tool
63
-
64
- Usage:
65
- node query-api.js <METHOD> <ENDPOINT> [JSON_PARAMS]
66
-
67
- Examples:
68
- # Search for entities (JSON body)
69
- node query-api.js POST /entities/search '{"queries":[{"queryId":1,"query":"Apple"}],"maxResults":3}'
70
-
71
- # Get entity details
72
- node query-api.js GET /entities/00416400910670863867
73
-
74
- # Find entities by expression (auto form-encoded)
75
- node query-api.js POST /elemental/find '{"expression":{"type":"is_type","is_type":{"fid":10}},"limit":5}'
76
-
77
- # Find entities with a property value
78
- node query-api.js POST /elemental/find '{"expression":{"type":"comparison","comparison":{"operator":"has_value","pid":313}},"limit":10}'
79
-
80
- # Get entity properties (auto form-encoded)
81
- node query-api.js POST /elemental/entities/properties '{"eids":["00416400910670863867"],"pids":[8,313]}'
82
-
83
- Environment variables (required):
84
- AUTH0_M2M_DEV_TOKEN Bearer token for API auth
85
- NUXT_PUBLIC_QUERY_SERVER_ADDRESS Query server URL (e.g. https://query.news.prod.g.lovelace.ai)
86
-
87
- Setup:
88
- 1. Get a dev token from your team
89
- 2. Add AUTH0_M2M_DEV_TOKEN to your .env file or export in your shell
90
- (NUXT_PUBLIC_QUERY_SERVER_ADDRESS should already be in your .env from project init)
91
-
92
- Note: /elemental/find and /elemental/entities/properties require form-encoded bodies.
93
- This tool automatically handles the encoding for these endpoints.
94
- `);
95
- process.exit(0);
96
- }
97
-
98
- const [method, endpoint, jsonParams] = args;
99
-
100
- const token = process.env.AUTH0_M2M_DEV_TOKEN;
101
- if (!token) {
102
- console.error('Error: AUTH0_M2M_DEV_TOKEN is not set.');
103
- console.error('Add it to your .env file or export it in your shell.');
104
- console.error('Run: node query-api.js --help');
105
- process.exit(1);
106
- }
107
-
108
- let params = {};
109
- if (jsonParams) {
110
- try {
111
- params = JSON.parse(jsonParams);
112
- } catch (err) {
113
- console.error('Error: Invalid JSON parameters');
114
- console.error(err.message);
115
- process.exit(1);
116
- }
117
- }
118
-
119
- const server = process.env.NUXT_PUBLIC_QUERY_SERVER_ADDRESS;
120
- if (!server) {
121
- console.error('Error: NUXT_PUBLIC_QUERY_SERVER_ADDRESS is not set.');
122
- console.error('Add it to your .env file or export it in your shell.');
123
- console.error('Run: node query-api.js --help');
124
- process.exit(1);
125
- }
126
-
127
- const normalizedEndpoint = endpoint.startsWith('/') ? endpoint : '/' + endpoint;
128
- let url = `${server}${normalizedEndpoint}`;
129
-
130
- const useFormEncoding = requiresFormEncoding(normalizedEndpoint);
131
- const fetchOptions = {
132
- method: method.toUpperCase(),
133
- headers: {
134
- Authorization: `Bearer ${token}`,
135
- 'Content-Type': useFormEncoding
136
- ? 'application/x-www-form-urlencoded'
137
- : 'application/json',
138
- Accept: 'application/json',
139
- },
140
- };
141
-
142
- if (method.toUpperCase() === 'GET' && Object.keys(params).length > 0) {
143
- const queryString = new URLSearchParams(params).toString();
144
- url = `${url}?${queryString}`;
145
- } else if (method.toUpperCase() !== 'GET' && Object.keys(params).length > 0) {
146
- if (useFormEncoding) {
147
- fetchOptions.body = buildFormBody(normalizedEndpoint, params);
148
- } else {
149
- fetchOptions.body = JSON.stringify(params);
150
- }
151
- }
152
-
153
- console.error(`\n📡 ${method.toUpperCase()} ${url}\n`);
154
-
155
- try {
156
- const response = await fetch(url, fetchOptions);
157
- const data = await response.json();
158
-
159
- if (!response.ok) {
160
- console.error(`❌ Error ${response.status}: ${response.statusText}`);
161
- } else {
162
- console.error(`✅ Success (${response.status})\n`);
163
- }
164
-
165
- console.log(JSON.stringify(data, null, 2));
166
- } catch (err) {
167
- console.error('❌ Request failed:', err.message);
168
- process.exit(1);
169
- }
170
- }
171
-
172
- main();