ebay-mcp 1.13.2 → 1.14.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.de.md +3 -3
- package/README.es.md +3 -3
- package/README.fr.md +3 -3
- package/README.ja.md +3 -3
- package/README.ko.md +3 -3
- package/README.md +163 -41
- package/README.pt-BR.md +3 -3
- package/README.ru.md +3 -3
- package/README.zh-CN.md +3 -3
- package/build/api/account-management/account.js +633 -297
- package/build/api/analytics-and-report/analytics.js +111 -65
- package/build/api/client.js +199 -140
- package/build/api/clientRequestError.js +23 -0
- package/build/api/clientTrading.js +213 -0
- package/build/api/clientTradingError.js +4 -0
- package/build/api/communication/feedback.js +147 -59
- package/build/api/communication/message.js +134 -81
- package/build/api/communication/negotiation.js +65 -53
- package/build/api/communication/notification.js +458 -164
- package/build/api/developer/developer.js +99 -45
- package/build/api/index.js +23 -8
- package/build/api/listing-management/inventory.js +884 -338
- package/build/api/listing-metadata/metadata.js +416 -170
- package/build/api/listing-metadata/taxonomy.js +187 -42
- package/build/api/marketing-and-promotions/marketing.js +1392 -598
- package/build/api/marketing-and-promotions/recommendation.js +49 -24
- package/build/api/order-management/dispute.js +226 -45
- package/build/api/order-management/fulfillment.js +144 -71
- package/build/api/order-management/orderIssueHelpers.js +61 -0
- package/build/api/other/compliance.js +50 -38
- package/build/api/other/edelivery.js +603 -159
- package/build/api/other/finding.js +280 -0
- package/build/api/other/identity.js +30 -15
- package/build/api/other/translation.js +29 -14
- package/build/api/other/vero.js +113 -27
- package/build/api/rateLimitTracker.js +55 -0
- package/build/api/shared/request.js +189 -98
- package/build/api/trading/trading.js +154 -87
- package/build/auth/credentialSession.js +250 -0
- package/build/auth/oauth.js +298 -171
- package/build/auth/{oauth-metadata.js → oauthMetadata.js} +22 -6
- package/build/auth/oauthMiddleware.js +104 -0
- package/build/auth/{scope-utils.js → scopeUtils.js} +101 -28
- package/build/auth/tokenVerifier.js +243 -0
- package/build/auth/tokenVerifierError.js +21 -0
- package/build/config/environment.js +148 -113
- package/build/config/{tool-families.js → toolFamilies.js} +30 -14
- package/build/index.js +25 -19
- package/build/mcp/{http-transport.js → httpTransport.js} +102 -52
- package/build/mcp/readOnlyFilter.js +67 -0
- package/build/mcp/runtime.js +31 -13
- package/build/mcp/{tool-gating.js → toolGating.js} +77 -49
- package/build/mcp/{ui-bridge.js → uiBridge.js} +80 -23
- package/build/schemas/account-management/account.js +144 -6
- package/build/schemas/analytics/analytics.js +33 -19
- package/build/schemas/communication/messages.js +12 -42
- package/build/schemas/developer/developer.js +135 -0
- package/build/schemas/fulfillment/orders.js +183 -21
- package/build/schemas/index.js +27 -19
- package/build/schemas/inventory-management/inventory.js +17 -9
- package/build/schemas/marketing/marketing.js +480 -25
- package/build/schemas/metadata/metadata.js +11 -6
- package/build/schemas/other/finding.js +27 -0
- package/build/schemas/other/otherApis.js +424 -0
- package/build/schemas/taxonomy/taxonomy.js +11 -5
- package/build/scripts/{auto-setup.js → autoSetup.js} +87 -74
- package/build/scripts/{build-ui.js → buildUi.js} +6 -1
- package/build/scripts/{dev-sync.js → devSync.js} +169 -110
- package/build/scripts/diagnostics.js +52 -44
- package/build/scripts/{download-specs.js → downloadSpecs.js} +29 -21
- package/build/scripts/{interactive-setup.js → interactiveSetup.js} +84 -78
- package/build/scripts/setup.js +241 -178
- package/build/scripts/{setup-shared.js → setupShared.js} +29 -7
- package/build/scripts/skills.js +35 -27
- package/build/scripts/updateApiStatusDoc.js +71 -0
- package/build/serverHttp.js +90 -0
- package/build/skills/content.js +124 -118
- package/build/skills/index.js +22 -10
- package/build/skills/install.js +21 -10
- package/build/skills/markers.js +50 -15
- package/build/skills/{registry-snapshot.js → registrySnapshot.js} +8 -2
- package/build/skills/render.js +41 -14
- package/build/skills/targets.js +49 -17
- package/build/tools/categories/account.js +166 -264
- package/build/tools/categories/analytics.js +11 -22
- package/build/tools/categories/browse.js +17 -0
- package/build/tools/categories/communication.js +124 -399
- package/build/tools/categories/connector.js +23 -20
- package/build/tools/categories/developer.js +46 -159
- package/build/tools/categories/fulfillment.js +84 -215
- package/build/tools/categories/index.js +6 -14
- package/build/tools/categories/inventory.js +238 -293
- package/build/tools/categories/marketing.js +677 -39
- package/build/tools/categories/metadata.js +190 -117
- package/build/tools/categories/other.js +81 -208
- package/build/tools/categories/taxonomy.js +29 -20
- package/build/tools/categories/{token-management.js → tokenManagement.js} +187 -192
- package/build/tools/categories/trading.js +16 -47
- package/build/tools/contracts.js +34 -24
- package/build/tools/{define-tool.js → defineTool.js} +44 -29
- package/build/tools/registry.js +87 -29
- package/build/tools/schemas.js +3 -57
- package/build/tools/ui/archetypes.js +1 -0
- package/build/tools/ui/{map-helpers.js → mapHelpers.js} +69 -19
- package/build/tools/ui/maps.js +294 -80
- package/build/types/index.js +1 -1
- package/build/ui/card.html +14 -14
- package/build/ui/chart.html +14 -14
- package/build/ui/stat.html +87 -0
- package/build/ui/table.html +14 -14
- package/build/utils/apiStatusFeed.js +95 -0
- package/build/utils/cliOutput.js +15 -0
- package/build/utils/cliUi.js +108 -0
- package/build/utils/communication/feedback.js +57 -77
- package/build/utils/communication/message.js +69 -98
- package/build/utils/communication/negotiation.js +30 -57
- package/build/utils/communication/notification.js +112 -252
- package/build/utils/{date-converter.js → dateConverter.js} +62 -30
- package/build/utils/effectSchema.js +203 -0
- package/build/utils/effectSchemaAdapter.js +121 -0
- package/build/utils/effectSchemaChainMethods.js +146 -0
- package/build/utils/effectSchemaRefinements.js +104 -0
- package/build/utils/effectSchemaStringMethods.js +63 -0
- package/build/utils/effectSchemaTypes.js +47 -0
- package/build/utils/{env-parser.js → envParser.js} +10 -2
- package/build/utils/errors.js +7 -3
- package/build/utils/http.js +148 -89
- package/build/utils/{llm-client-detector.js → llmClientDetector.js} +295 -274
- package/build/utils/logger.js +127 -51
- package/build/utils/{oauth-helper.js → oauthHelper.js} +127 -90
- package/build/utils/other/compliance.js +18 -38
- package/build/utils/other/identity.js +2 -2
- package/build/utils/other/translation.js +4 -4
- package/build/utils/other/vero.js +36 -20
- package/build/utils/schemaHelpers.js +22 -0
- package/build/utils/scopeHelper.js +270 -0
- package/build/utils/securityChecker.js +316 -0
- package/build/utils/{setup-validator.js → setupValidator.js} +113 -94
- package/build/utils/{setup-wizard.js → setupWizard.js} +57 -46
- package/build/utils/{token-utils.js → tokenUtils.js} +2 -2
- package/build/utils/trading/trading.js +35 -0
- package/build/utils/typeGuards.js +12 -0
- package/build/utils/version.js +65 -29
- package/package.json +10 -8
- package/build/api/client-trading.js +0 -124
- package/build/api/communication/shared.js +0 -59
- package/build/api/shared/query-params.js +0 -16
- package/build/auth/credential-session.js +0 -166
- package/build/auth/oauth-middleware.js +0 -100
- package/build/auth/token-verifier.js +0 -186
- package/build/schemas/other/other-apis.js +0 -390
- package/build/scripts/update-api-status-doc.js +0 -44
- package/build/server-http.js +0 -85
- package/build/tools/definitions/marketing.js +0 -1381
- package/build/tools/tool-handlers/marketing.js +0 -312
- package/build/utils/account-management/account.js +0 -818
- package/build/utils/api-status-feed.js +0 -84
- package/build/utils/cli-ui.js +0 -47
- package/build/utils/order-management/dispute.js +0 -369
- package/build/utils/order-management/fulfillment.js +0 -205
- package/build/utils/other/edelivery.js +0 -235
- package/build/utils/schema-helpers.js +0 -13
- package/build/utils/scope-helper.js +0 -207
- package/build/utils/security-checker.js +0 -248
- package/build/utils/type-guards.js +0 -6
- /package/build/auth/{oauth-types.js → oauthTypes.js} +0 -0
- /package/build/{server-http.d.ts → serverHttp.d.ts} +0 -0
- /package/build/tools/ui/{view-models.js → viewModels.js} +0 -0
- /package/build/types/{ebay-enums.js → ebayEnums.js} +0 -0
package/build/utils/logger.js
CHANGED
|
@@ -2,11 +2,13 @@ import winston from 'winston';
|
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { existsSync, mkdirSync } from 'fs';
|
|
4
4
|
import { homedir } from 'os';
|
|
5
|
+
import { getLoggingConfig } from '../config/environment.js';
|
|
5
6
|
/**
|
|
6
7
|
* Log directory for eBay MCP Server
|
|
7
8
|
* Stored in user's home directory under .ebay-mcp/logs
|
|
8
9
|
*/
|
|
9
10
|
const LOG_DIR = join(homedir(), '.ebay-mcp', 'logs');
|
|
11
|
+
const loggingConfig = getLoggingConfig();
|
|
10
12
|
// Ensure log directory exists
|
|
11
13
|
if (!existsSync(LOG_DIR)) {
|
|
12
14
|
mkdirSync(LOG_DIR, { recursive: true });
|
|
@@ -15,11 +17,11 @@ if (!existsSync(LOG_DIR)) {
|
|
|
15
17
|
* Log level from environment variable or default to 'info'
|
|
16
18
|
* Levels: error, warn, info, http, verbose, debug, silly
|
|
17
19
|
*/
|
|
18
|
-
const LOG_LEVEL =
|
|
20
|
+
const LOG_LEVEL = loggingConfig.logLevel;
|
|
19
21
|
/**
|
|
20
22
|
* Whether to enable file logging (disabled in production MCP mode by default)
|
|
21
23
|
*/
|
|
22
|
-
|
|
24
|
+
let fileLoggingEnabled = loggingConfig.enableFileLogging;
|
|
23
25
|
/**
|
|
24
26
|
* Custom log format with timestamp and colored output
|
|
25
27
|
*/
|
|
@@ -43,7 +45,7 @@ const transports = [
|
|
|
43
45
|
}),
|
|
44
46
|
];
|
|
45
47
|
// Add file transports if enabled
|
|
46
|
-
if (
|
|
48
|
+
if (fileLoggingEnabled) {
|
|
47
49
|
transports.push(
|
|
48
50
|
// Error log - only errors
|
|
49
51
|
new winston.transports.File({
|
|
@@ -82,29 +84,36 @@ const logger = winston.createLogger({
|
|
|
82
84
|
});
|
|
83
85
|
/**
|
|
84
86
|
* Create a child logger for a specific component
|
|
87
|
+
*
|
|
88
|
+
* @param component - Component label prefixed to every emitted message.
|
|
89
|
+
* @returns Component-scoped logger methods.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```ts
|
|
93
|
+
* const logger = createLogger('API');
|
|
94
|
+
* logger.info('Ready');
|
|
95
|
+
* ```
|
|
85
96
|
*/
|
|
86
|
-
export
|
|
87
|
-
|
|
88
|
-
error
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
warn
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
info
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
http
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
debug
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
verbose
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
};
|
|
107
|
-
}
|
|
97
|
+
export const createLogger = (component) => ({
|
|
98
|
+
error: (message, meta) => {
|
|
99
|
+
logger.error(`[${component}] ${message}`, meta);
|
|
100
|
+
},
|
|
101
|
+
warn: (message, meta) => {
|
|
102
|
+
logger.warn(`[${component}] ${message}`, meta);
|
|
103
|
+
},
|
|
104
|
+
info: (message, meta) => {
|
|
105
|
+
logger.info(`[${component}] ${message}`, meta);
|
|
106
|
+
},
|
|
107
|
+
http: (message, meta) => {
|
|
108
|
+
logger.http(`[${component}] ${message}`, meta);
|
|
109
|
+
},
|
|
110
|
+
debug: (message, meta) => {
|
|
111
|
+
logger.debug(`[${component}] ${message}`, meta);
|
|
112
|
+
},
|
|
113
|
+
verbose: (message, meta) => {
|
|
114
|
+
logger.verbose(`[${component}] ${message}`, meta);
|
|
115
|
+
},
|
|
116
|
+
});
|
|
108
117
|
/**
|
|
109
118
|
* Pre-configured loggers for different components
|
|
110
119
|
*/
|
|
@@ -120,17 +129,40 @@ export const toolLogger = createLogger('Tool');
|
|
|
120
129
|
export const setupLogger = createLogger('Setup');
|
|
121
130
|
/**
|
|
122
131
|
* Log HTTP request details
|
|
132
|
+
*
|
|
133
|
+
* @param method - HTTP method being sent.
|
|
134
|
+
* @param url - Request URL.
|
|
135
|
+
* @param params - Optional query parameters.
|
|
136
|
+
* @param body - Optional request body.
|
|
137
|
+
* @returns Nothing; writes to configured logger transports.
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```ts
|
|
141
|
+
* logRequest('GET', url, params);
|
|
142
|
+
* ```
|
|
123
143
|
*/
|
|
124
|
-
export
|
|
144
|
+
export const logRequest = (method, url, params, body) => {
|
|
125
145
|
apiLogger.http(`Request: ${method.toUpperCase()} ${url}`, {
|
|
126
146
|
params: params && Object.keys(params).length > 0 ? params : undefined,
|
|
127
147
|
body: body ? truncateData(body) : undefined,
|
|
128
148
|
});
|
|
129
|
-
}
|
|
149
|
+
};
|
|
130
150
|
/**
|
|
131
151
|
* Log HTTP response details
|
|
152
|
+
*
|
|
153
|
+
* @param status - HTTP response status code.
|
|
154
|
+
* @param statusText - HTTP response status text.
|
|
155
|
+
* @param data - Optional response payload.
|
|
156
|
+
* @param rateLimitRemaining - Optional remaining request quota.
|
|
157
|
+
* @param rateLimitTotal - Optional total request quota.
|
|
158
|
+
* @returns Nothing; writes to configured logger transports.
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```ts
|
|
162
|
+
* logResponse(200, 'OK', data);
|
|
163
|
+
* ```
|
|
132
164
|
*/
|
|
133
|
-
export
|
|
165
|
+
export const logResponse = (status, statusText, data, rateLimitRemaining, rateLimitTotal) => {
|
|
134
166
|
const meta = {};
|
|
135
167
|
if (rateLimitRemaining && rateLimitTotal) {
|
|
136
168
|
meta.rateLimit = `${rateLimitRemaining}/${rateLimitTotal}`;
|
|
@@ -139,42 +171,81 @@ export function logResponse(status, statusText, data, rateLimitRemaining, rateLi
|
|
|
139
171
|
meta.data = truncateData(data);
|
|
140
172
|
}
|
|
141
173
|
apiLogger.http(`Response: ${status} ${statusText}`, meta);
|
|
142
|
-
}
|
|
174
|
+
};
|
|
143
175
|
/**
|
|
144
176
|
* Log HTTP error response
|
|
177
|
+
*
|
|
178
|
+
* @param status - HTTP status code, when a response exists.
|
|
179
|
+
* @param statusText - HTTP status text, when a response exists.
|
|
180
|
+
* @param url - Request URL that failed.
|
|
181
|
+
* @param errorData - Optional parsed error payload.
|
|
182
|
+
* @returns Nothing; writes to configured logger transports.
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```ts
|
|
186
|
+
* logErrorResponse(error.status, error.statusText, url, error.data);
|
|
187
|
+
* ```
|
|
145
188
|
*/
|
|
146
|
-
export
|
|
147
|
-
|
|
189
|
+
export const logErrorResponse = (status, statusText, url, errorData) => {
|
|
190
|
+
const statusLabel = status === undefined ? 'No status' : String(status);
|
|
191
|
+
const statusTextLabel = statusText ?? 'No response';
|
|
192
|
+
apiLogger.error(`Error Response: ${statusLabel} ${statusTextLabel}`, {
|
|
148
193
|
url,
|
|
149
194
|
error: errorData ? truncateData(errorData) : undefined,
|
|
150
195
|
});
|
|
151
|
-
}
|
|
196
|
+
};
|
|
152
197
|
/**
|
|
153
198
|
* Truncate large data objects for logging
|
|
154
199
|
*/
|
|
155
|
-
|
|
200
|
+
const truncateData = (data, maxLength = 1000) => {
|
|
156
201
|
const str = JSON.stringify(data);
|
|
157
202
|
if (str.length <= maxLength) {
|
|
158
203
|
return data;
|
|
159
204
|
}
|
|
160
205
|
return `${str.substring(0, maxLength)}... [truncated]`;
|
|
161
|
-
}
|
|
206
|
+
};
|
|
162
207
|
/**
|
|
163
208
|
* Get log file paths for user reference
|
|
209
|
+
*
|
|
210
|
+
* @returns Paths to the log directory and known log files.
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```ts
|
|
214
|
+
* const paths = getLogPaths();
|
|
215
|
+
* ```
|
|
164
216
|
*/
|
|
165
|
-
export
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
217
|
+
export const getLogPaths = () => ({
|
|
218
|
+
logDir: LOG_DIR,
|
|
219
|
+
errorLog: join(LOG_DIR, 'error.log'),
|
|
220
|
+
combinedLog: join(LOG_DIR, 'combined.log'),
|
|
221
|
+
debugLog: join(LOG_DIR, 'debug.log'),
|
|
222
|
+
});
|
|
223
|
+
/**
|
|
224
|
+
* Reports whether file logging is currently enabled.
|
|
225
|
+
*
|
|
226
|
+
* @returns True when Winston file transports are enabled.
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* ```ts
|
|
230
|
+
* if (isFileLoggingEnabled()) {
|
|
231
|
+
* serverLogger.info('File logging enabled');
|
|
232
|
+
* }
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
235
|
+
export const isFileLoggingEnabled = () => fileLoggingEnabled;
|
|
173
236
|
/**
|
|
174
237
|
* Enable or disable file logging at runtime
|
|
238
|
+
*
|
|
239
|
+
* @param enabled - Whether file logging should be enabled.
|
|
240
|
+
* @returns Nothing; mutates Winston transports when enabling.
|
|
241
|
+
*
|
|
242
|
+
* @example
|
|
243
|
+
* ```ts
|
|
244
|
+
* setFileLogging(true);
|
|
245
|
+
* ```
|
|
175
246
|
*/
|
|
176
|
-
export
|
|
177
|
-
if (enabled && !
|
|
247
|
+
export const setFileLogging = (enabled) => {
|
|
248
|
+
if (enabled && !fileLoggingEnabled) {
|
|
178
249
|
logger.add(new winston.transports.File({
|
|
179
250
|
filename: join(LOG_DIR, 'error.log'),
|
|
180
251
|
level: 'error',
|
|
@@ -190,17 +261,22 @@ export function setFileLogging(enabled) {
|
|
|
190
261
|
maxFiles: 5,
|
|
191
262
|
tailable: true,
|
|
192
263
|
}));
|
|
264
|
+
fileLoggingEnabled = true;
|
|
193
265
|
logger.info('File logging enabled');
|
|
194
266
|
}
|
|
195
|
-
}
|
|
267
|
+
};
|
|
196
268
|
/**
|
|
197
269
|
* Set log level at runtime
|
|
270
|
+
*
|
|
271
|
+
* @param level - Winston log level name.
|
|
272
|
+
* @returns Nothing; mutates the shared Winston logger level.
|
|
273
|
+
*
|
|
274
|
+
* @example
|
|
275
|
+
* ```ts
|
|
276
|
+
* setLogLevel('debug');
|
|
277
|
+
* ```
|
|
198
278
|
*/
|
|
199
|
-
export
|
|
279
|
+
export const setLogLevel = (level) => {
|
|
200
280
|
logger.level = level;
|
|
201
281
|
logger.info(`Log level set to: ${level}`);
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Default Winston logger instance.
|
|
205
|
-
*/
|
|
206
|
-
export default logger;
|
|
282
|
+
};
|
|
@@ -4,18 +4,25 @@
|
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import { createServer } from 'http';
|
|
6
6
|
import { getOAuthAuthorizationUrl } from '../config/environment.js';
|
|
7
|
+
import { writeCliLine } from './cliOutput.js';
|
|
7
8
|
/**
|
|
8
9
|
* Generate terminal hyperlink (if supported)
|
|
9
10
|
*/
|
|
10
|
-
|
|
11
|
-
return `\u001B]8;;${url}\u0007${text}\u001B]8;;\u0007`;
|
|
12
|
-
}
|
|
11
|
+
const hyperlink = (text, url) => `\u001B]8;;${url}\u0007${text}\u001B]8;;\u0007`;
|
|
13
12
|
/**
|
|
14
|
-
*
|
|
13
|
+
* Generates an eBay OAuth authorization URL.
|
|
14
|
+
*
|
|
15
|
+
* @param clientId eBay application client ID.
|
|
16
|
+
* @param redirectUri Redirect URI or RuName configured for the application.
|
|
17
|
+
* @param environment eBay environment that should receive the authorization request.
|
|
18
|
+
* @param scopes OAuth scopes to request; defaults come from the environment helper.
|
|
19
|
+
* @returns Fully qualified eBay OAuth authorization URL.
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* const url = generateAuthUrl(clientId, redirectUri, 'sandbox');
|
|
23
|
+
* ```
|
|
15
24
|
*/
|
|
16
|
-
export
|
|
17
|
-
return getOAuthAuthorizationUrl(clientId, redirectUri, environment, scopes);
|
|
18
|
-
}
|
|
25
|
+
export const generateAuthUrl = (clientId, redirectUri, environment, scopes) => getOAuthAuthorizationUrl(clientId, redirectUri, environment, scopes);
|
|
19
26
|
/**
|
|
20
27
|
* Escape a string for safe interpolation into HTML text/attribute context.
|
|
21
28
|
*
|
|
@@ -25,14 +32,12 @@ export function generateAuthUrl(clientId, redirectUri, environment, scopes) {
|
|
|
25
32
|
* HTML-significant characters neutralizes the markup while keeping the message
|
|
26
33
|
* legible.
|
|
27
34
|
*/
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
.replace(/'/g, ''');
|
|
35
|
-
}
|
|
35
|
+
const escapeHtml = (value) => value
|
|
36
|
+
.replace(/&/g, '&')
|
|
37
|
+
.replace(/</g, '<')
|
|
38
|
+
.replace(/>/g, '>')
|
|
39
|
+
.replace(/"/g, '"')
|
|
40
|
+
.replace(/'/g, ''');
|
|
36
41
|
/**
|
|
37
42
|
* Render the small HTML page the user lands on after eBay redirects back.
|
|
38
43
|
*
|
|
@@ -41,7 +46,7 @@ function escapeHtml(value) {
|
|
|
41
46
|
* potentially attacker-controlled (eBay's `error_description`), so it is
|
|
42
47
|
* HTML-escaped before interpolation.
|
|
43
48
|
*/
|
|
44
|
-
|
|
49
|
+
const renderCallbackPage = (success, detail = '') => {
|
|
45
50
|
const gradient = success
|
|
46
51
|
? 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'
|
|
47
52
|
: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)';
|
|
@@ -78,7 +83,7 @@ function renderCallbackPage(success, detail = '') {
|
|
|
78
83
|
${autoClose}
|
|
79
84
|
</body>
|
|
80
85
|
</html>`;
|
|
81
|
-
}
|
|
86
|
+
};
|
|
82
87
|
/**
|
|
83
88
|
* Start a loopback HTTP server that captures the eBay OAuth redirect.
|
|
84
89
|
*
|
|
@@ -94,8 +99,12 @@ function renderCallbackPage(success, detail = '') {
|
|
|
94
99
|
* @param timeoutMs How long to wait for the redirect before settling with `timeout`.
|
|
95
100
|
* @param options Path and CSRF-`state` tuning — see {@link CallbackServerOptions}.
|
|
96
101
|
* @returns The live server plus a promise resolving to the callback result.
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* const { server, codePromise } = await startCallbackServer(3000);
|
|
105
|
+
* ```
|
|
97
106
|
*/
|
|
98
|
-
export async
|
|
107
|
+
export const startCallbackServer = async (port = 3000, timeoutMs = 300_000, options = {}) => {
|
|
99
108
|
const callbackPath = options.path ?? '/oauth/callback';
|
|
100
109
|
const { expectedState } = options;
|
|
101
110
|
return await new Promise((resolve, reject) => {
|
|
@@ -160,76 +169,99 @@ export async function startCallbackServer(port = 3000, timeoutMs = 300000, optio
|
|
|
160
169
|
resolve({ server, codePromise });
|
|
161
170
|
});
|
|
162
171
|
});
|
|
163
|
-
}
|
|
172
|
+
};
|
|
164
173
|
/**
|
|
165
|
-
*
|
|
174
|
+
* Runs an interactive OAuth flow with a local callback server.
|
|
175
|
+
*
|
|
176
|
+
* @param config eBay application configuration used to build the authorization URL.
|
|
177
|
+
* @param scopes OAuth scopes to request for the user token.
|
|
178
|
+
* @returns The authorization code when the browser callback succeeds, otherwise `null`.
|
|
179
|
+
* @example
|
|
180
|
+
* ```ts
|
|
181
|
+
* const code = await interactiveOAuthFlow(config);
|
|
182
|
+
* ```
|
|
166
183
|
*/
|
|
167
|
-
export async
|
|
168
|
-
|
|
184
|
+
export const interactiveOAuthFlow = async (config, scopes) => {
|
|
185
|
+
writeCliLine(chalk.bold.cyan('\n🔐 Interactive OAuth Flow\n'));
|
|
169
186
|
// Check if redirect URI is localhost
|
|
170
187
|
const redirectUri = config.redirectUri || 'http://localhost:3000/oauth/callback';
|
|
171
188
|
const isLocalhost = redirectUri.includes('localhost') || redirectUri.includes('127.0.0.1');
|
|
172
189
|
if (!isLocalhost) {
|
|
173
|
-
|
|
190
|
+
writeCliLine(chalk.yellow('⚠️ Your redirect URI is not localhost. Interactive flow requires localhost callback.\n'));
|
|
174
191
|
return null;
|
|
175
192
|
}
|
|
176
193
|
// Extract port from redirect URI
|
|
177
194
|
const portMatch = /:(\d+)/.exec(redirectUri);
|
|
178
|
-
const port = portMatch ? parseInt(portMatch[1], 10) : 3000;
|
|
195
|
+
const port = portMatch ? Number.parseInt(portMatch[1], 10) : 3000;
|
|
179
196
|
// Start callback server
|
|
180
|
-
|
|
197
|
+
writeCliLine(chalk.cyan('Starting local OAuth callback server...\n'));
|
|
181
198
|
const { server, codePromise } = await startCallbackServer(port);
|
|
182
199
|
// Generate auth URL
|
|
183
200
|
const authUrl = generateAuthUrl(config.clientId, redirectUri, config.environment, scopes);
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
201
|
+
writeCliLine(chalk.bold.white('📋 Step 1: Authorize the Application\n'));
|
|
202
|
+
writeCliLine(chalk.gray('Open this URL in your browser:\n'));
|
|
203
|
+
writeCliLine(chalk.blue.underline(hyperlink(authUrl.substring(0, 20) + '...', authUrl)));
|
|
204
|
+
writeCliLine('');
|
|
205
|
+
writeCliLine(chalk.gray('Waiting for authorization...'));
|
|
206
|
+
writeCliLine(chalk.gray('(This window will update automatically after you authorize)\n'));
|
|
190
207
|
// Wait for callback
|
|
191
208
|
const result = await codePromise;
|
|
192
209
|
// Close server
|
|
193
210
|
server.close();
|
|
194
211
|
if (result.error) {
|
|
195
|
-
|
|
212
|
+
writeCliLine(chalk.red(`\n✗ Authorization failed: ${result.errorDescription || result.error}\n`));
|
|
196
213
|
return null;
|
|
197
214
|
}
|
|
198
215
|
if (result.code) {
|
|
199
|
-
|
|
216
|
+
writeCliLine(chalk.green('\n✓ Authorization successful!\n'));
|
|
200
217
|
return result.code;
|
|
201
218
|
}
|
|
202
|
-
|
|
219
|
+
writeCliLine(chalk.yellow('\n⚠️ No authorization code received.\n'));
|
|
203
220
|
return null;
|
|
204
|
-
}
|
|
221
|
+
};
|
|
205
222
|
/**
|
|
206
|
-
*
|
|
223
|
+
* Displays manual OAuth instructions in the setup CLI.
|
|
224
|
+
*
|
|
225
|
+
* @param clientId eBay application client ID.
|
|
226
|
+
* @param redirectUri Redirect URI or RuName configured for the application.
|
|
227
|
+
* @param environment eBay environment for the authorization URL.
|
|
228
|
+
* @param scopes OAuth scopes to include in the manual authorization URL.
|
|
229
|
+
* @returns Nothing; instructions are written to stdout.
|
|
230
|
+
* @example
|
|
231
|
+
* ```ts
|
|
232
|
+
* displayManualOAuthInstructions(clientId, redirectUri, 'sandbox');
|
|
233
|
+
* ```
|
|
207
234
|
*/
|
|
208
|
-
export
|
|
235
|
+
export const displayManualOAuthInstructions = (clientId, redirectUri, environment, scopes) => {
|
|
209
236
|
const authUrl = generateAuthUrl(clientId, redirectUri, environment, scopes);
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
237
|
+
writeCliLine(chalk.bold.cyan('\n📖 Manual OAuth Token Acquisition Guide\n'));
|
|
238
|
+
writeCliLine(chalk.white('Step 1: Generate Authorization URL\n'));
|
|
239
|
+
writeCliLine(chalk.gray('Copy this URL and open it in your browser:\n'));
|
|
240
|
+
writeCliLine(chalk.blue.underline(authUrl));
|
|
241
|
+
writeCliLine('');
|
|
242
|
+
writeCliLine(chalk.white('\nStep 2: Authorize the Application\n'));
|
|
243
|
+
writeCliLine(chalk.gray(' • Log in to your eBay account'));
|
|
244
|
+
writeCliLine(chalk.gray(' • Review the permissions requested'));
|
|
245
|
+
writeCliLine(chalk.gray(' • Click "Agree" to authorize\n'));
|
|
246
|
+
writeCliLine(chalk.white('Step 3: Get the Authorization Code\n'));
|
|
247
|
+
writeCliLine(chalk.gray(' • After authorization, you will be redirected to your redirect URI'));
|
|
248
|
+
writeCliLine(chalk.gray(' • The URL will contain a "code" parameter'));
|
|
249
|
+
writeCliLine(chalk.gray(' • Example: https://your-redirect-uri?code=v^1.1#i^1...\n'));
|
|
250
|
+
writeCliLine(chalk.white('Step 4: Exchange Code for Tokens\n'));
|
|
251
|
+
writeCliLine(chalk.gray(' • Use the code to get your refresh token'));
|
|
252
|
+
writeCliLine(chalk.gray(' • This can be done through the MCP tool: ebay_exchange_auth_code'));
|
|
253
|
+
writeCliLine(chalk.gray(' • Or paste the code in the setup wizard when prompted\n'));
|
|
254
|
+
};
|
|
228
255
|
/**
|
|
229
|
-
*
|
|
256
|
+
* Gets help text for RuName (Redirect URI) setup.
|
|
257
|
+
*
|
|
258
|
+
* @returns A formatted help block explaining eBay RuName creation.
|
|
259
|
+
* @example
|
|
260
|
+
* ```ts
|
|
261
|
+
* writeCliLine(getRuNameHelp());
|
|
262
|
+
* ```
|
|
230
263
|
*/
|
|
231
|
-
export
|
|
232
|
-
return `
|
|
264
|
+
export const getRuNameHelp = () => `
|
|
233
265
|
${chalk.bold.cyan('What is a RuName (Redirect URI)?')}
|
|
234
266
|
|
|
235
267
|
A RuName (Redirect URL name) is a unique identifier for your OAuth redirect URI.
|
|
@@ -260,37 +292,42 @@ ${chalk.bold.white('Common RuName formats:')}
|
|
|
260
292
|
${chalk.yellow('Note:')} The RuName is NOT the same as the redirect URI itself.
|
|
261
293
|
It's a reference name that eBay associates with your redirect URI.
|
|
262
294
|
`;
|
|
263
|
-
}
|
|
264
295
|
/**
|
|
265
|
-
*
|
|
296
|
+
* Displays the first-time eBay developer guide in the setup CLI.
|
|
297
|
+
*
|
|
298
|
+
* @returns Nothing; the guide is written to stdout.
|
|
299
|
+
* @example
|
|
300
|
+
* ```ts
|
|
301
|
+
* displayFirstTimeDeveloperGuide();
|
|
302
|
+
* ```
|
|
266
303
|
*/
|
|
267
|
-
export
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
304
|
+
export const displayFirstTimeDeveloperGuide = () => {
|
|
305
|
+
writeCliLine(chalk.bold.cyan('\n🆕 First-Time eBay Developer Guide\n'));
|
|
306
|
+
writeCliLine(chalk.white("Welcome! Here's how to get started:\n"));
|
|
307
|
+
writeCliLine(chalk.bold.yellow('Step 1: Create eBay Developer Account\n'));
|
|
308
|
+
writeCliLine(chalk.gray(' 1. Visit: ') + chalk.blue.underline('https://developer.ebay.com/'));
|
|
309
|
+
writeCliLine(chalk.gray(' 2. Click "Register" or "Join"'));
|
|
310
|
+
writeCliLine(chalk.gray(' 3. Complete the registration form'));
|
|
311
|
+
writeCliLine(chalk.gray(' 4. Verify your email address\n'));
|
|
312
|
+
writeCliLine(chalk.bold.yellow('Step 2: Create an Application\n'));
|
|
313
|
+
writeCliLine(chalk.gray(' 1. Go to: ') + chalk.blue.underline('https://developer.ebay.com/my/keys'));
|
|
314
|
+
writeCliLine(chalk.gray(' 2. Click "Create Application"'));
|
|
315
|
+
writeCliLine(chalk.gray(' 3. Fill in application details (name, description)'));
|
|
316
|
+
writeCliLine(chalk.gray(' 4. Choose Sandbox environment to start\n'));
|
|
317
|
+
writeCliLine(chalk.bold.yellow('Step 3: Get Your Credentials\n'));
|
|
318
|
+
writeCliLine(chalk.gray(' After creating the app, you will see:'));
|
|
319
|
+
writeCliLine(chalk.gray(' • App ID (Client ID) - Copy this'));
|
|
320
|
+
writeCliLine(chalk.gray(' • Cert ID (Client Secret) - Copy this'));
|
|
321
|
+
writeCliLine(chalk.gray(' • These are needed for the setup wizard\n'));
|
|
322
|
+
writeCliLine(chalk.bold.yellow('Step 4: Create RuName (Redirect URI)\n'));
|
|
323
|
+
writeCliLine(chalk.gray(' 1. In your application settings'));
|
|
324
|
+
writeCliLine(chalk.gray(' 2. Navigate to "User Tokens" section'));
|
|
325
|
+
writeCliLine(chalk.gray(' 3. Click "Add RuName"'));
|
|
326
|
+
writeCliLine(chalk.gray(' 4. Enter: http://localhost:3000/oauth/callback'));
|
|
327
|
+
writeCliLine(chalk.gray(' 5. Save and copy the generated RuName\n'));
|
|
328
|
+
writeCliLine(chalk.bold.yellow('Step 5: Get User Token\n'));
|
|
329
|
+
writeCliLine(chalk.gray(' Option A: Use this setup wizard (recommended)'));
|
|
330
|
+
writeCliLine(chalk.gray(' Option B: Manual OAuth flow through eBay Developer Portal\n'));
|
|
331
|
+
writeCliLine(chalk.green.bold("✅ Once you have these, you're ready to continue!\n"));
|
|
332
|
+
writeCliLine(chalk.gray('Press Enter to continue when you have your credentials ready...'));
|
|
333
|
+
};
|
|
@@ -1,51 +1,44 @@
|
|
|
1
|
-
import { z } from '
|
|
1
|
+
import { z } from '../../utils/effectSchema.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Effect-backed schemas for Compliance API input validation
|
|
4
4
|
* Based on: src/api/other/compliance.ts
|
|
5
5
|
* OpenAPI spec: docs/sell-apps/other-apis/sell_compliance_v1_oas3.json
|
|
6
6
|
* Types from: src/types/sell_compliance_v1_oas3.ts
|
|
7
7
|
*/
|
|
8
|
-
// Reusable schema for
|
|
8
|
+
// Reusable schema for complianceType parameter
|
|
9
9
|
const complianceTypeSchema = z
|
|
10
10
|
.string({
|
|
11
|
-
invalid_type_error: '
|
|
11
|
+
invalid_type_error: 'complianceType must be a string',
|
|
12
12
|
description: 'Compliance type(s) to filter violations (e.g., ASPECTS_ADOPTION, HTTPS, OUTSIDE_EBAY_BUYING_AND_SELLING, RETURNS_POLICY)',
|
|
13
13
|
})
|
|
14
14
|
.optional();
|
|
15
|
-
// Reusable schema for offset parameter
|
|
15
|
+
// Reusable schema for offset parameter
|
|
16
16
|
const offsetSchema = z
|
|
17
|
-
.
|
|
18
|
-
invalid_type_error: 'offset must be a
|
|
17
|
+
.number({
|
|
18
|
+
invalid_type_error: 'offset must be a number',
|
|
19
19
|
description: 'Number of items to skip (zero-based index)',
|
|
20
20
|
})
|
|
21
|
+
.int()
|
|
22
|
+
.nonnegative()
|
|
21
23
|
.optional();
|
|
22
|
-
// Reusable schema for limit parameter
|
|
24
|
+
// Reusable schema for limit parameter
|
|
23
25
|
const limitSchema = z
|
|
24
|
-
.
|
|
25
|
-
invalid_type_error: 'limit must be a
|
|
26
|
+
.number({
|
|
27
|
+
invalid_type_error: 'limit must be a number',
|
|
26
28
|
description: 'Maximum number of items to return (default: 100, max: 200)',
|
|
27
29
|
})
|
|
30
|
+
.int()
|
|
31
|
+
.positive()
|
|
28
32
|
.optional();
|
|
29
33
|
/**
|
|
30
34
|
* Schema for getListingViolations method
|
|
31
35
|
* Endpoint: GET /listing_violation
|
|
32
|
-
* Query:
|
|
36
|
+
* Query: complianceType, offset, limit, filter
|
|
33
37
|
*/
|
|
34
38
|
export const getListingViolationsSchema = z.object({
|
|
35
|
-
|
|
36
|
-
message: 'Compliance type is required',
|
|
37
|
-
required_error: 'compliance_type is required',
|
|
38
|
-
invalid_type_error: 'compliance_type must be a string',
|
|
39
|
-
description: 'The compliance type of violations to retrieve (only one type allowed per call)',
|
|
40
|
-
}),
|
|
39
|
+
complianceType: complianceTypeSchema,
|
|
41
40
|
offset: offsetSchema,
|
|
42
41
|
limit: limitSchema,
|
|
43
|
-
listing_id: z
|
|
44
|
-
.string({
|
|
45
|
-
invalid_type_error: 'listing_id must be a string',
|
|
46
|
-
description: 'Filter by specific eBay listing ID (not yet supported)',
|
|
47
|
-
})
|
|
48
|
-
.optional(),
|
|
49
42
|
filter: z
|
|
50
43
|
.string({
|
|
51
44
|
invalid_type_error: 'filter must be a string',
|
|
@@ -56,21 +49,8 @@ export const getListingViolationsSchema = z.object({
|
|
|
56
49
|
/**
|
|
57
50
|
* Schema for getListingViolationsSummary method
|
|
58
51
|
* Endpoint: GET /listing_violation_summary
|
|
59
|
-
* Query:
|
|
52
|
+
* Query: complianceType
|
|
60
53
|
*/
|
|
61
54
|
export const getListingViolationsSummarySchema = z.object({
|
|
62
|
-
|
|
63
|
-
});
|
|
64
|
-
/**
|
|
65
|
-
* Schema for suppressViolation method
|
|
66
|
-
* Endpoint: POST /suppress_violation
|
|
67
|
-
* Body: listing_violation_id (required)
|
|
68
|
-
*/
|
|
69
|
-
export const suppressViolationSchema = z.object({
|
|
70
|
-
listing_violation_id: z.string({
|
|
71
|
-
message: 'Listing violation ID is required',
|
|
72
|
-
required_error: 'listing_violation_id is required',
|
|
73
|
-
invalid_type_error: 'listing_violation_id must be a string',
|
|
74
|
-
description: 'The unique identifier of the listing violation to suppress',
|
|
75
|
-
}),
|
|
55
|
+
complianceType: complianceTypeSchema,
|
|
76
56
|
});
|