@0xinsider/mcp 1.3.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -0
- package/dist/cli-lib.d.ts.map +1 -1
- package/dist/cli-lib.js +9 -5
- package/dist/cli-lib.js.map +1 -1
- package/dist/cli.js +6 -1
- package/dist/cli.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/product-cli/client.d.ts +9 -0
- package/dist/product-cli/client.d.ts.map +1 -0
- package/dist/product-cli/client.js +78 -0
- package/dist/product-cli/client.js.map +1 -0
- package/dist/product-cli/errors.d.ts +7 -0
- package/dist/product-cli/errors.d.ts.map +1 -0
- package/dist/product-cli/errors.js +13 -0
- package/dist/product-cli/errors.js.map +1 -0
- package/dist/product-cli/index.d.ts +4 -0
- package/dist/product-cli/index.d.ts.map +1 -0
- package/dist/product-cli/index.js +354 -0
- package/dist/product-cli/index.js.map +1 -0
- package/dist/product-cli/oauth.d.ts +10 -0
- package/dist/product-cli/oauth.d.ts.map +1 -0
- package/dist/product-cli/oauth.js +246 -0
- package/dist/product-cli/oauth.js.map +1 -0
- package/dist/product-cli/storage.d.ts +20 -0
- package/dist/product-cli/storage.d.ts.map +1 -0
- package/dist/product-cli/storage.js +158 -0
- package/dist/product-cli/storage.js.map +1 -0
- package/dist/sdk/client.d.ts +765 -0
- package/dist/sdk/client.d.ts.map +1 -0
- package/dist/sdk/client.js +974 -0
- package/dist/sdk/client.js.map +1 -0
- package/dist/sdk/errors.d.ts +313 -0
- package/dist/sdk/errors.d.ts.map +1 -0
- package/dist/sdk/errors.js +521 -0
- package/dist/sdk/errors.js.map +1 -0
- package/dist/sdk/index.d.ts +26 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +25 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/sdk/pagination.d.ts +76 -0
- package/dist/sdk/pagination.d.ts.map +1 -0
- package/dist/sdk/pagination.js +106 -0
- package/dist/sdk/pagination.js.map +1 -0
- package/dist/sdk/schema.d.ts +2425 -0
- package/dist/sdk/schema.d.ts.map +1 -0
- package/dist/sdk/schema.js +7 -0
- package/dist/sdk/schema.js.map +1 -0
- package/dist/sdk/stream.d.ts +182 -0
- package/dist/sdk/stream.d.ts.map +1 -0
- package/dist/sdk/stream.js +391 -0
- package/dist/sdk/stream.js.map +1 -0
- package/dist/sdk/webhooks.d.ts +163 -0
- package/dist/sdk/webhooks.d.ts.map +1 -0
- package/dist/sdk/webhooks.js +122 -0
- package/dist/sdk/webhooks.js.map +1 -0
- package/package.json +6 -3
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import { API_CLIENT_OPERATIONS, OxinsiderApiError, RequestTimeoutError, isListEnvelope, paginatePages, paginationResumePoint, } from "#sdk";
|
|
2
|
+
import { VERSION } from "../constants.js";
|
|
3
|
+
import { CliError } from "./errors.js";
|
|
4
|
+
import { login, logout } from "./oauth.js";
|
|
5
|
+
import { CliApiClient } from "./client.js";
|
|
6
|
+
const commands = {
|
|
7
|
+
"markets search": { operation: "searchMarkets", argument: "q", query: true },
|
|
8
|
+
"markets explore": { operation: "exploreMarkets" },
|
|
9
|
+
"market snapshot": { operation: "getMarketSnapshot", argument: "condition_id" },
|
|
10
|
+
"market intel": { operation: "getMarketIntel", argument: "condition_id" },
|
|
11
|
+
"traders get": { operation: "getTrader", argument: "address" },
|
|
12
|
+
"traders pnl": { operation: "getTraderPnl", argument: "address" },
|
|
13
|
+
"leaderboard list": { operation: "listLeaderboard" },
|
|
14
|
+
"whales list": { operation: "listWhaleTrades" },
|
|
15
|
+
"positions list": { operation: "listPositions" },
|
|
16
|
+
"pick today": { operation: "getPickOfTheDay" },
|
|
17
|
+
"reports daily": { operation: "getDailyReportSnapshot" },
|
|
18
|
+
usage: { operation: "getUsage" },
|
|
19
|
+
whoami: { operation: "getAccountIdentity" },
|
|
20
|
+
};
|
|
21
|
+
const commandMap = new Map(Object.entries(commands));
|
|
22
|
+
const roots = new Set([
|
|
23
|
+
...Object.keys(commands).map((command) => command.split(" ")[0]),
|
|
24
|
+
"login",
|
|
25
|
+
"logout",
|
|
26
|
+
"api",
|
|
27
|
+
"completions",
|
|
28
|
+
"--version",
|
|
29
|
+
"-v",
|
|
30
|
+
]);
|
|
31
|
+
export function isProductCommand(value) {
|
|
32
|
+
return value !== undefined && roots.has(value);
|
|
33
|
+
}
|
|
34
|
+
export function productHelp() {
|
|
35
|
+
return `Product commands:
|
|
36
|
+
login Show a verification URL and code; wait for browser approval
|
|
37
|
+
whoami Read the authenticated account and credential identity
|
|
38
|
+
logout Revoke and remove only the stored CLI session
|
|
39
|
+
markets search <query> Search markets
|
|
40
|
+
markets explore Browse markets
|
|
41
|
+
market snapshot|intel <id> Read a market
|
|
42
|
+
traders get|pnl <wallet> Read a wallet
|
|
43
|
+
leaderboard list Read the leaderboard
|
|
44
|
+
whales list Read large trades
|
|
45
|
+
positions list Read positions
|
|
46
|
+
pick today Read today's pick
|
|
47
|
+
reports daily Read the current UTC-day report (--query date=YYYY-MM-DD to select)
|
|
48
|
+
usage Read your API usage (OAuth usage scope)
|
|
49
|
+
api call <operation-id> Call a registered, non-administrative JSON GET operation
|
|
50
|
+
completions bash|zsh|fish Print shell completion setup
|
|
51
|
+
--version Print the package version
|
|
52
|
+
|
|
53
|
+
Data options:
|
|
54
|
+
--json One JSON document (default, including in pipes)
|
|
55
|
+
--table Tab-separated human view; metadata goes to stderr
|
|
56
|
+
--query name=value Add an API query parameter; repeat for array values
|
|
57
|
+
--path name=value Supply a path parameter for api call
|
|
58
|
+
--limit N / --cursor VALUE Set a page size or starting cursor
|
|
59
|
+
--pages N Follow at most N pages (1-100); bound reached exits 8
|
|
60
|
+
--timeout-ms N Deadline per request including retries (1-300000; default 30000)
|
|
61
|
+
--retries N SDK retries (0-3; default 2); respects Retry-After
|
|
62
|
+
--help Show help without credentials or network requests
|
|
63
|
+
|
|
64
|
+
Environment:
|
|
65
|
+
OXINSIDER_API_KEY Overrides stored CLI credentials; never saved or revoked by logout
|
|
66
|
+
OXINSIDER_CONFIG_DIR Private session directory (default: $XDG_CONFIG_HOME/0xinsider/cli)
|
|
67
|
+
OXINSIDER_API_URL Production API or loopback development origin only
|
|
68
|
+
|
|
69
|
+
Exit codes: 0 success, 2 usage, 3 credentials, 4 access/subscription, 5 rate limit,
|
|
70
|
+
6 upstream/storage/contract, 7 network/timeout, 8 bounded incomplete traversal, 130 interrupted.
|
|
71
|
+
No arguments, serve, init, and 0xinsider-mcp retain their MCP behavior.`;
|
|
72
|
+
}
|
|
73
|
+
function integer(value, flag, min, max) {
|
|
74
|
+
if (value === undefined || !/^\d+$/.test(value))
|
|
75
|
+
throw new CliError(2, `${flag} requires an integer from ${min} to ${max}.`);
|
|
76
|
+
const result = Number(value);
|
|
77
|
+
if (!Number.isSafeInteger(result) || result < min || result > max)
|
|
78
|
+
throw new CliError(2, `${flag} must be between ${min} and ${max}.`);
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
function pair(value, target, flag) {
|
|
82
|
+
const index = value?.indexOf("=") ?? -1;
|
|
83
|
+
if (!value || index < 1)
|
|
84
|
+
throw new CliError(2, `${flag} requires name=value.`);
|
|
85
|
+
const key = value.slice(0, index);
|
|
86
|
+
if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(key) || (flag === "--path" && Object.hasOwn(target, key)))
|
|
87
|
+
throw new CliError(2, `${flag} has an invalid or repeated parameter name.`);
|
|
88
|
+
if (["token", "api_key", "access_token", "authorization"].includes(key.toLowerCase()))
|
|
89
|
+
throw new CliError(2, "Credentials belong in the environment or stored session, never query parameters.");
|
|
90
|
+
const next = value.slice(index + 1);
|
|
91
|
+
if (flag === "--query" && key === "limit") {
|
|
92
|
+
if (Object.hasOwn(target, key))
|
|
93
|
+
throw new CliError(2, "Repeated limit parameter.");
|
|
94
|
+
target[key] = integer(next, flag, 1, 1000);
|
|
95
|
+
}
|
|
96
|
+
else if (Object.hasOwn(target, key)) {
|
|
97
|
+
if (key === "cursor")
|
|
98
|
+
throw new CliError(2, "Repeated cursor parameter.");
|
|
99
|
+
const previous = target[key];
|
|
100
|
+
target[key] = Array.isArray(previous) ? [...previous, next] : [String(previous), next];
|
|
101
|
+
}
|
|
102
|
+
else
|
|
103
|
+
target[key] = next;
|
|
104
|
+
}
|
|
105
|
+
function parse(args) {
|
|
106
|
+
const result = {
|
|
107
|
+
positionals: [],
|
|
108
|
+
query: {},
|
|
109
|
+
path: {},
|
|
110
|
+
pages: 1,
|
|
111
|
+
timeoutMs: 30_000,
|
|
112
|
+
retries: 2,
|
|
113
|
+
table: false,
|
|
114
|
+
};
|
|
115
|
+
const seen = new Set();
|
|
116
|
+
let literal = false;
|
|
117
|
+
for (let i = 0; i < args.length; i++) {
|
|
118
|
+
const arg = args[i];
|
|
119
|
+
if (literal || !arg.startsWith("--")) {
|
|
120
|
+
result.positionals.push(arg);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (arg === "--") {
|
|
124
|
+
literal = true;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (!["--query", "--path"].includes(arg) && seen.has(arg))
|
|
128
|
+
throw new CliError(2, `Repeated option ${arg}.`);
|
|
129
|
+
seen.add(arg);
|
|
130
|
+
switch (arg) {
|
|
131
|
+
case "--json":
|
|
132
|
+
break;
|
|
133
|
+
case "--table":
|
|
134
|
+
result.table = true;
|
|
135
|
+
break;
|
|
136
|
+
case "--query":
|
|
137
|
+
pair(args[++i], result.query, arg);
|
|
138
|
+
break;
|
|
139
|
+
case "--path":
|
|
140
|
+
pair(args[++i], result.path, arg);
|
|
141
|
+
break;
|
|
142
|
+
case "--limit":
|
|
143
|
+
if (Object.hasOwn(result.query, "limit"))
|
|
144
|
+
throw new CliError(2, "Repeated limit parameter.");
|
|
145
|
+
result.query.limit = integer(args[++i], arg, 1, 1000);
|
|
146
|
+
break;
|
|
147
|
+
case "--cursor": {
|
|
148
|
+
const value = args[++i];
|
|
149
|
+
if (!value || value.startsWith("--"))
|
|
150
|
+
throw new CliError(2, "--cursor requires a value.");
|
|
151
|
+
if (Object.hasOwn(result.query, "cursor"))
|
|
152
|
+
throw new CliError(2, "Repeated cursor parameter.");
|
|
153
|
+
result.query.cursor = value;
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
case "--pages":
|
|
157
|
+
result.pages = integer(args[++i], arg, 1, 100);
|
|
158
|
+
break;
|
|
159
|
+
case "--timeout-ms":
|
|
160
|
+
result.timeoutMs = integer(args[++i], arg, 1, 300_000);
|
|
161
|
+
break;
|
|
162
|
+
case "--retries":
|
|
163
|
+
result.retries = integer(args[++i], arg, 0, 3);
|
|
164
|
+
break;
|
|
165
|
+
default:
|
|
166
|
+
throw new CliError(2, `Unknown option ${arg}. Use --help.`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (seen.has("--json") && seen.has("--table"))
|
|
170
|
+
throw new CliError(2, "Choose --json or --table, not both.");
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
function table(value) {
|
|
174
|
+
if (isListEnvelope(value)) {
|
|
175
|
+
const rows = value.data.map((row) => typeof row === "object" && row !== null ? row : { value: row });
|
|
176
|
+
const columns = [...new Set(rows.flatMap((row) => Object.keys(row)))];
|
|
177
|
+
console.log(columns.join("\t"));
|
|
178
|
+
for (const row of rows)
|
|
179
|
+
console.log(columns.map((column) => JSON.stringify(row[column]) ?? "").join("\t"));
|
|
180
|
+
const { data: _data, ...metadata } = value;
|
|
181
|
+
console.error(JSON.stringify(metadata));
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
console.log(JSON.stringify(value, null, 2));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function emit(value, human) {
|
|
188
|
+
if (human)
|
|
189
|
+
table(value);
|
|
190
|
+
else
|
|
191
|
+
console.log(JSON.stringify(value));
|
|
192
|
+
}
|
|
193
|
+
function selectOperation(options) {
|
|
194
|
+
const args = [...options.positionals];
|
|
195
|
+
let operation;
|
|
196
|
+
if (args[0] === "api" && args[1] === "call") {
|
|
197
|
+
if (args.length !== 3)
|
|
198
|
+
throw new CliError(2, "Use api call <operation-id> with --path and --query parameters.");
|
|
199
|
+
const found = API_CLIENT_OPERATIONS.find((item) => item.operationId === args[2]);
|
|
200
|
+
if (!found ||
|
|
201
|
+
found.method !== "GET" ||
|
|
202
|
+
/\/(webhooks|stream|mcp)(\/|$)|\/export(\/|$)|\.md$/.test(found.path)) {
|
|
203
|
+
throw new CliError(2, "This is not a registered non-administrative JSON read operation.");
|
|
204
|
+
}
|
|
205
|
+
operation = found.operationId;
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
const two = `${args[0]} ${args[1]}`;
|
|
209
|
+
const key = commandMap.has(two) ? two : args[0];
|
|
210
|
+
const command = commandMap.get(key);
|
|
211
|
+
if (!command)
|
|
212
|
+
throw new CliError(2, "Unknown product command. Use --help.");
|
|
213
|
+
args.splice(0, key.split(" ").length);
|
|
214
|
+
if (args.length !== (command.argument ? 1 : 0))
|
|
215
|
+
throw new CliError(2, `${key} ${command.argument ? `requires one ${command.argument}` : "takes no positional arguments"}.`);
|
|
216
|
+
if (command.argument) {
|
|
217
|
+
const target = command.query ? options.query : options.path;
|
|
218
|
+
if (Object.hasOwn(target, command.argument))
|
|
219
|
+
throw new CliError(2, "The positional parameter was also supplied as a flag.");
|
|
220
|
+
target[command.argument] = args[0];
|
|
221
|
+
}
|
|
222
|
+
if (key === "reports daily" && options.query.date === undefined)
|
|
223
|
+
options.query.date = new Date().toISOString().slice(0, 10);
|
|
224
|
+
operation = command.operation;
|
|
225
|
+
}
|
|
226
|
+
const contract = API_CLIENT_OPERATIONS.find((item) => item.operationId === operation);
|
|
227
|
+
if (!contract)
|
|
228
|
+
throw new CliError(6, "The CLI command has no SDK operation.");
|
|
229
|
+
const names = [...contract.path.matchAll(/\{([^}]+)\}/g)].map((match) => match[1]);
|
|
230
|
+
if (names.some((name) => !options.path[name]) ||
|
|
231
|
+
Object.keys(options.path).some((name) => !names.includes(name))) {
|
|
232
|
+
throw new CliError(2, `Required path parameters: ${names.join(", ") || "none"}.`);
|
|
233
|
+
}
|
|
234
|
+
return operation;
|
|
235
|
+
}
|
|
236
|
+
function completions(shell) {
|
|
237
|
+
const words = [...roots]
|
|
238
|
+
.filter((word) => !word.startsWith("-"))
|
|
239
|
+
.concat(["init", "serve", "help"])
|
|
240
|
+
.join(" ");
|
|
241
|
+
if (shell === "bash")
|
|
242
|
+
console.log(`complete -W '${words}' 0xinsider 0xinsider-mcp`);
|
|
243
|
+
else if (shell === "zsh")
|
|
244
|
+
console.log(`#compdef 0xinsider 0xinsider-mcp\n_arguments '1:command:(${words})'`);
|
|
245
|
+
else if (shell === "fish")
|
|
246
|
+
console.log(`complete -c 0xinsider -f -a '${words}'\ncomplete -c 0xinsider-mcp -f -a '${words}'`);
|
|
247
|
+
else
|
|
248
|
+
throw new CliError(2, "Choose completions bash, zsh, or fish.");
|
|
249
|
+
}
|
|
250
|
+
export async function runProductCli(args) {
|
|
251
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
252
|
+
console.log(productHelp());
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (args[0] === "--version" || args[0] === "-v") {
|
|
256
|
+
if (args.length !== 1)
|
|
257
|
+
throw new CliError(2, "--version takes no arguments.");
|
|
258
|
+
console.log(VERSION);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (args[0] === "completions") {
|
|
262
|
+
if (args.length !== 2)
|
|
263
|
+
throw new CliError(2, "Use completions bash, zsh, or fish.");
|
|
264
|
+
completions(args[1]);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const options = parse(args);
|
|
268
|
+
const controller = new AbortController();
|
|
269
|
+
const interrupt = () => controller.abort();
|
|
270
|
+
process.once("SIGINT", interrupt);
|
|
271
|
+
process.once("SIGTERM", interrupt);
|
|
272
|
+
try {
|
|
273
|
+
if (["login", "logout"].includes(args[0])) {
|
|
274
|
+
if (options.positionals.length !== 1 ||
|
|
275
|
+
args.some((arg) => arg.startsWith("--") && !["--json", "--table"].includes(arg)))
|
|
276
|
+
throw new CliError(2, "Login/logout accept only --json or --table.");
|
|
277
|
+
emit(await (args[0] === "login" ? login(controller.signal) : logout(controller.signal)), options.table);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
const operation = selectOperation(options);
|
|
281
|
+
const client = new CliApiClient(options.timeoutMs, options.retries);
|
|
282
|
+
const request = {
|
|
283
|
+
path: options.path,
|
|
284
|
+
query: options.query,
|
|
285
|
+
signal: controller.signal,
|
|
286
|
+
};
|
|
287
|
+
if (options.pages === 1) {
|
|
288
|
+
emit(await client.call(operation, request), options.table);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (options.table)
|
|
292
|
+
throw new CliError(2, "Multi-page output uses JSON to preserve each page's metadata.");
|
|
293
|
+
const pages = [];
|
|
294
|
+
try {
|
|
295
|
+
for await (const page of paginatePages(client, operation, {
|
|
296
|
+
...request,
|
|
297
|
+
query: options.query,
|
|
298
|
+
maxPages: options.pages,
|
|
299
|
+
}))
|
|
300
|
+
pages.push(page);
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
// A resume cursor is useful only if the caller also receives earlier pages.
|
|
304
|
+
if (pages.length > 0)
|
|
305
|
+
emit({
|
|
306
|
+
object: "cli_pages",
|
|
307
|
+
pages,
|
|
308
|
+
complete: false,
|
|
309
|
+
next_cursor: paginationResumePoint(error)?.cursor ?? pages.at(-1)?.next_cursor ?? null,
|
|
310
|
+
}, false);
|
|
311
|
+
throw error;
|
|
312
|
+
}
|
|
313
|
+
const last = pages.at(-1);
|
|
314
|
+
const complete = last?.has_more === false;
|
|
315
|
+
emit({ object: "cli_pages", pages, complete, next_cursor: last?.next_cursor ?? null }, false);
|
|
316
|
+
if (!complete)
|
|
317
|
+
throw new CliError(8, "Traversal is incomplete at the requested page bound or a missing cursor. Returned pages preserve their metadata; use next_cursor to continue.");
|
|
318
|
+
}
|
|
319
|
+
catch (error) {
|
|
320
|
+
if (controller.signal.aborted)
|
|
321
|
+
throw new CliError(130, "Interrupted. No partial traversal was reported as complete.");
|
|
322
|
+
const resume = paginationResumePoint(error);
|
|
323
|
+
if (resume)
|
|
324
|
+
console.error(JSON.stringify({
|
|
325
|
+
pages_fetched: resume.pagesFetched,
|
|
326
|
+
resume_cursor: resume.cursor ?? null,
|
|
327
|
+
}));
|
|
328
|
+
if (error instanceof RequestTimeoutError)
|
|
329
|
+
throw new CliError(7, error.message);
|
|
330
|
+
if (error instanceof OxinsiderApiError) {
|
|
331
|
+
const code = error.status === 401
|
|
332
|
+
? 3
|
|
333
|
+
: [402, 403, 423].includes(error.status)
|
|
334
|
+
? 4
|
|
335
|
+
: error.status === 429
|
|
336
|
+
? 5
|
|
337
|
+
: error.status === 0
|
|
338
|
+
? 7
|
|
339
|
+
: error.status === 400
|
|
340
|
+
? 2
|
|
341
|
+
: 6;
|
|
342
|
+
const requestId = error.meta?.request_id;
|
|
343
|
+
throw new CliError(code, `${error.message}${requestId ? ` (request ${requestId})` : ""}`);
|
|
344
|
+
}
|
|
345
|
+
if (error instanceof CliError)
|
|
346
|
+
throw error;
|
|
347
|
+
throw new CliError(6, "The CLI operation failed. Check your config directory permissions and retry. Credentials were not printed.");
|
|
348
|
+
}
|
|
349
|
+
finally {
|
|
350
|
+
process.removeListener("SIGINT", interrupt);
|
|
351
|
+
process.removeListener("SIGTERM", interrupt);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/product-cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,qBAAqB,GAMtB,MAAM,MAAM,CAAC;AACd,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,QAAQ,GAAG;IACf,gBAAgB,EAAE,EAAE,SAAS,EAAE,eAAe,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;IAC5E,iBAAiB,EAAE,EAAE,SAAS,EAAE,gBAAgB,EAAE;IAClD,iBAAiB,EAAE,EAAE,SAAS,EAAE,mBAAmB,EAAE,QAAQ,EAAE,cAAc,EAAE;IAC/E,cAAc,EAAE,EAAE,SAAS,EAAE,gBAAgB,EAAE,QAAQ,EAAE,cAAc,EAAE;IACzE,aAAa,EAAE,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE;IAC9D,aAAa,EAAE,EAAE,SAAS,EAAE,cAAc,EAAE,QAAQ,EAAE,SAAS,EAAE;IACjE,kBAAkB,EAAE,EAAE,SAAS,EAAE,iBAAiB,EAAE;IACpD,aAAa,EAAE,EAAE,SAAS,EAAE,iBAAiB,EAAE;IAC/C,gBAAgB,EAAE,EAAE,SAAS,EAAE,eAAe,EAAE;IAChD,YAAY,EAAE,EAAE,SAAS,EAAE,iBAAiB,EAAE;IAC9C,eAAe,EAAE,EAAE,SAAS,EAAE,wBAAwB,EAAE;IACxD,KAAK,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;IAChC,MAAM,EAAE,EAAE,SAAS,EAAE,oBAAoB,EAAE;CACgD,CAAC;AAC9F,MAAM,UAAU,GAAG,IAAI,GAAG,CAGxB,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC5B,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC;IACpB,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,OAAO;IACP,QAAQ;IACR,KAAK;IACL,aAAa;IACb,WAAW;IACX,IAAI;CACL,CAAC,CAAC;AAEH,MAAM,UAAU,gBAAgB,CAAC,KAAyB;IACxD,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wEAoC+D,CAAC;AACzE,CAAC;AAYD,SAAS,OAAO,CAAC,KAAyB,EAAE,IAAY,EAAE,GAAW,EAAE,GAAW;IAChF,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;QAC7C,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,GAAG,IAAI,6BAA6B,GAAG,OAAO,GAAG,GAAG,CAAC,CAAC;IAC9E,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7B,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,GAAG,IAAI,MAAM,GAAG,GAAG;QAC/D,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,GAAG,IAAI,oBAAoB,GAAG,QAAQ,GAAG,GAAG,CAAC,CAAC;IACtE,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,IAAI,CACX,KAAyB,EACzB,MAAqC,EACrC,IAAY;IAEZ,MAAM,KAAK,GAAG,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IACxC,IAAI,CAAC,KAAK,IAAI,KAAK,GAAG,CAAC;QAAE,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,GAAG,IAAI,uBAAuB,CAAC,CAAC;IAC/E,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAClC,IAAI,CAAC,yBAAyB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC3F,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,GAAG,IAAI,6CAA6C,CAAC,CAAC;IAC9E,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,eAAe,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;QACnF,MAAM,IAAI,QAAQ,CAChB,CAAC,EACD,kFAAkF,CACnF,CAAC;IACJ,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACpC,IAAI,IAAI,KAAK,SAAS,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;QAC1C,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;YAAE,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,2BAA2B,CAAC,CAAC;QACnF,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;SAAM,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;QACtC,IAAI,GAAG,KAAK,QAAQ;YAAE,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,4BAA4B,CAAC,CAAC;QAC1E,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC;IACzF,CAAC;;QAAM,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;AAC5B,CAAC;AAED,SAAS,KAAK,CAAC,IAAuB;IACpC,MAAM,MAAM,GAAY;QACtB,WAAW,EAAE,EAAE;QACf,KAAK,EAAE,EAAE;QACT,IAAI,EAAE,EAAE;QACR,KAAK,EAAE,CAAC;QACR,SAAS,EAAE,MAAM;QACjB,OAAO,EAAE,CAAC;QACV,KAAK,EAAE,KAAK;KACb,CAAC;IACF,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,OAAO,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACrC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC7B,SAAS;QACX,CAAC;QACD,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACjB,OAAO,GAAG,IAAI,CAAC;YACf,SAAS;QACX,CAAC;QACD,IAAI,CAAC,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YACvD,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,mBAAmB,GAAG,GAAG,CAAC,CAAC;QACnD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,QAAQ,GAAG,EAAE,CAAC;YACZ,KAAK,QAAQ;gBACX,MAAM;YACR,KAAK,SAAS;gBACZ,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC;gBACpB,MAAM;YACR,KAAK,SAAS;gBACZ,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;gBACnC,MAAM;YACR,KAAK,QAAQ;gBACX,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;gBAClC,MAAM;YACR,KAAK,SAAS;gBACZ,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC;oBACtC,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,2BAA2B,CAAC,CAAC;gBACrD,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;gBACtD,MAAM;YACR,KAAK,UAAU,EAAE,CAAC;gBAChB,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBACxB,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;oBAAE,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,4BAA4B,CAAC,CAAC;gBAC1F,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC;oBACvC,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,4BAA4B,CAAC,CAAC;gBACtD,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;gBAC5B,MAAM;YACR,CAAC;YACD,KAAK,SAAS;gBACZ,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;gBAC/C,MAAM;YACR,KAAK,cAAc;gBACjB,MAAM,CAAC,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;gBACvD,MAAM;YACR,KAAK,WAAW;gBACd,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC/C,MAAM;YACR;gBACE,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,kBAAkB,GAAG,eAAe,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IACD,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC;QAC3C,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,qCAAqC,CAAC,CAAC;IAC/D,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,KAAK,CAAC,KAAc;IAC3B,IAAI,cAAc,CAAU,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAClC,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,CAAC,CAAC,CAAE,GAA+B,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAC5F,CAAC;QACF,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACtE,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAChC,KAAK,MAAM,GAAG,IAAI,IAAI;YACpB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACrF,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,GAAG,KAAK,CAAC;QAC3C,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC1C,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC;AACH,CAAC;AAED,SAAS,IAAI,CAAC,KAAc,EAAE,KAAc;IAC1C,IAAI,KAAK;QAAE,KAAK,CAAC,KAAK,CAAC,CAAC;;QACnB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,eAAe,CAAC,OAAgB;IACvC,MAAM,IAAI,GAAG,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACtC,IAAI,SAAyB,CAAC;IAC9B,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;QAC5C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YACnB,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,iEAAiE,CAAC,CAAC;QAC3F,MAAM,KAAK,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACjF,IACE,CAAC,KAAK;YACN,KAAK,CAAC,MAAM,KAAK,KAAK;YACtB,oDAAoD,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EACrE,CAAC;YACD,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,kEAAkE,CAAC,CAAC;QAC5F,CAAC;QACD,SAAS,GAAG,KAAK,CAAC,WAAW,CAAC;IAChC,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACpC,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAChD,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,sCAAsC,CAAC,CAAC;QAC5E,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;QACtC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5C,MAAM,IAAI,QAAQ,CAChB,CAAC,EACD,GAAG,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,gBAAgB,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,+BAA+B,GAAG,CACrG,CAAC;QACJ,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YACrB,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;YAC5D,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC;gBACzC,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,uDAAuD,CAAC,CAAC;YACjF,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACrC,CAAC;QACD,IAAI,GAAG,KAAK,eAAe,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS;YAC7D,OAAO,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7D,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IAChC,CAAC;IACD,MAAM,QAAQ,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC;IACtF,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,uCAAuC,CAAC,CAAC;IAC9E,MAAM,KAAK,GAAG,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACnF,IACE,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAC/D,CAAC;QACD,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,6BAA6B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,GAAG,CAAC,CAAC;IACpF,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,WAAW,CAAC,KAAyB;IAC5C,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC;SACrB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;SACvC,MAAM,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;SACjC,IAAI,CAAC,GAAG,CAAC,CAAC;IACb,IAAI,KAAK,KAAK,MAAM;QAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,KAAK,2BAA2B,CAAC,CAAC;SAC/E,IAAI,KAAK,KAAK,KAAK;QACtB,OAAO,CAAC,GAAG,CAAC,4DAA4D,KAAK,IAAI,CAAC,CAAC;SAChF,IAAI,KAAK,KAAK,MAAM;QACvB,OAAO,CAAC,GAAG,CACT,gCAAgC,KAAK,uCAAuC,KAAK,GAAG,CACrF,CAAC;;QACC,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,wCAAwC,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAuB;IACzD,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;QAC3B,OAAO;IACT,CAAC;IACD,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,WAAW,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAChD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,+BAA+B,CAAC,CAAC;QAC9E,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IACD,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,aAAa,EAAE,CAAC;QAC9B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,qCAAqC,CAAC,CAAC;QACpF,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IACD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IAC3C,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAClC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IACnC,IAAI,CAAC;QACH,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1C,IACE,OAAO,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC;gBAChC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;gBAEhF,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,6CAA6C,CAAC,CAAC;YACvE,IAAI,CACF,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,EAClF,OAAO,CAAC,KAAK,CACd,CAAC;YACF,OAAO;QACT,CAAC;QACD,MAAM,SAAS,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;QAC3C,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QACpE,MAAM,OAAO,GAAsB;YACjC,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC;QACF,IAAI,OAAO,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;YACxB,IAAI,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;YAC3D,OAAO;QACT,CAAC;QACD,IAAI,OAAO,CAAC,KAAK;YACf,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,+DAA+D,CAAC,CAAC;QACzF,MAAM,KAAK,GAA+B,EAAE,CAAC;QAC7C,IAAI,CAAC;YACH,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE;gBACxD,GAAG,OAAO;gBACV,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,QAAQ,EAAE,OAAO,CAAC,KAAK;aACxB,CAAC;gBACA,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,4EAA4E;YAC5E,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;gBAClB,IAAI,CACF;oBACE,MAAM,EAAE,WAAW;oBACnB,KAAK;oBACL,QAAQ,EAAE,KAAK;oBACf,WAAW,EAAE,qBAAqB,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,IAAI,IAAI;iBACvF,EACD,KAAK,CACN,CAAC;YACJ,MAAM,KAAK,CAAC;QACd,CAAC;QACD,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1B,MAAM,QAAQ,GAAG,IAAI,EAAE,QAAQ,KAAK,KAAK,CAAC;QAC1C,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,IAAI,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC;QAC9F,IAAI,CAAC,QAAQ;YACX,MAAM,IAAI,QAAQ,CAChB,CAAC,EACD,+IAA+I,CAChJ,CAAC;IACN,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO;YAC3B,MAAM,IAAI,QAAQ,CAAC,GAAG,EAAE,6DAA6D,CAAC,CAAC;QACzF,MAAM,MAAM,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;QAC5C,IAAI,MAAM;YACR,OAAO,CAAC,KAAK,CACX,IAAI,CAAC,SAAS,CAAC;gBACb,aAAa,EAAE,MAAM,CAAC,YAAY;gBAClC,aAAa,EAAE,MAAM,CAAC,MAAM,IAAI,IAAI;aACrC,CAAC,CACH,CAAC;QACJ,IAAI,KAAK,YAAY,mBAAmB;YAAE,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QAC/E,IAAI,KAAK,YAAY,iBAAiB,EAAE,CAAC;YACvC,MAAM,IAAI,GACR,KAAK,CAAC,MAAM,KAAK,GAAG;gBAClB,CAAC,CAAC,CAAC;gBACH,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;oBACtC,CAAC,CAAC,CAAC;oBACH,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,GAAG;wBACpB,CAAC,CAAC,CAAC;wBACH,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;4BAClB,CAAC,CAAC,CAAC;4BACH,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,GAAG;gCACpB,CAAC,CAAC,CAAC;gCACH,CAAC,CAAC,CAAC,CAAC;YAChB,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC;YACzC,MAAM,IAAI,QAAQ,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,aAAa,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5F,CAAC;QACD,IAAI,KAAK,YAAY,QAAQ;YAAE,MAAM,KAAK,CAAC;QAC3C,MAAM,IAAI,QAAQ,CAChB,CAAC,EACD,4GAA4G,CAC7G,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QAC5C,OAAO,CAAC,cAAc,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAC/C,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const API_ORIGIN = "https://api.0xinsider.com";
|
|
2
|
+
export declare function apiOrigin(): string;
|
|
3
|
+
export declare function login(signal: AbortSignal): Promise<unknown>;
|
|
4
|
+
export declare function credentials(signal: AbortSignal, rejectedAccessToken?: string): Promise<{
|
|
5
|
+
apiKey: string;
|
|
6
|
+
origin: string;
|
|
7
|
+
source: string;
|
|
8
|
+
}>;
|
|
9
|
+
export declare function logout(signal: AbortSignal): Promise<unknown>;
|
|
10
|
+
//# sourceMappingURL=oauth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../src/product-cli/oauth.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,UAAU,8BAA8B,CAAC;AAkBtD,wBAAgB,SAAS,IAAI,MAAM,CAyBlC;AAkGD,wBAAsB,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CA2GjE;AAED,wBAAsB,WAAW,CAC/B,MAAM,EAAE,WAAW,EACnB,mBAAmB,CAAC,EAAE,MAAM,GAC3B,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CA6B7D;AAED,wBAAsB,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAiClE"}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { CliError } from "./errors.js";
|
|
4
|
+
import { readSession, saveSession, removeSession, withSessionLock, } from "./storage.js";
|
|
5
|
+
export const API_ORIGIN = "https://api.0xinsider.com";
|
|
6
|
+
const DEVICE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
|
|
7
|
+
const LOGIN_SCOPE = "read usage";
|
|
8
|
+
const tokenSchema = z.object({
|
|
9
|
+
access_token: z.string().min(1),
|
|
10
|
+
refresh_token: z.string().min(1),
|
|
11
|
+
token_type: z.literal("Bearer"),
|
|
12
|
+
expires_in: z.number().int().positive(),
|
|
13
|
+
scope: z.string().min(1),
|
|
14
|
+
});
|
|
15
|
+
const deviceSchema = z.object({
|
|
16
|
+
device_code: z.string().min(1),
|
|
17
|
+
user_code: z.string().min(1),
|
|
18
|
+
verification_uri: z.string().url(),
|
|
19
|
+
expires_in: z.number().int().positive().max(3600),
|
|
20
|
+
interval: z.number().int().positive().max(60),
|
|
21
|
+
});
|
|
22
|
+
export function apiOrigin() {
|
|
23
|
+
let value;
|
|
24
|
+
try {
|
|
25
|
+
value = new URL(process.env.OXINSIDER_API_URL || API_ORIGIN);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
throw new CliError(2, "OXINSIDER_API_URL must be the production API origin or an explicit loopback development origin.");
|
|
29
|
+
}
|
|
30
|
+
const loopback = ["localhost", "127.0.0.1", "[::1]"].includes(value.hostname);
|
|
31
|
+
if (value.username ||
|
|
32
|
+
value.password ||
|
|
33
|
+
value.search ||
|
|
34
|
+
value.hash ||
|
|
35
|
+
value.pathname !== "/" ||
|
|
36
|
+
(value.origin !== API_ORIGIN && !(loopback && ["http:", "https:"].includes(value.protocol)))) {
|
|
37
|
+
throw new CliError(2, "Product CLI credentials can only be sent to the production API or a loopback development origin.");
|
|
38
|
+
}
|
|
39
|
+
return value.origin;
|
|
40
|
+
}
|
|
41
|
+
class OAuthFailure extends CliError {
|
|
42
|
+
oauthCode;
|
|
43
|
+
constructor(oauthCode, status) {
|
|
44
|
+
const messages = {
|
|
45
|
+
access_denied: "The account owner denied access. Start login again if this was unintended.",
|
|
46
|
+
expired_token: "The login code expired. Run login again.",
|
|
47
|
+
invalid_grant: "This CLI session is expired or revoked. Run logout, then login again.",
|
|
48
|
+
invalid_client: "The CLI application is no longer registered. Run login again.",
|
|
49
|
+
invalid_scope: "The requested scopes are not available to this application.",
|
|
50
|
+
invalid_request: "The authorization request was rejected. Check the CLI version and retry login.",
|
|
51
|
+
temporarily_unavailable: "Authorization is rate-limited. Wait before starting again.",
|
|
52
|
+
};
|
|
53
|
+
super(status === 429 ? 5 : status >= 500 ? 6 : 3, messages[oauthCode] ||
|
|
54
|
+
"Authorization could not be completed. Retry later; no credential was printed.");
|
|
55
|
+
this.oauthCode = oauthCode;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function oauthRequest(origin, path, body, signal, json = false) {
|
|
59
|
+
let response;
|
|
60
|
+
try {
|
|
61
|
+
response = await fetch(`${origin}${path}`, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
redirect: "error",
|
|
64
|
+
headers: { "Content-Type": json ? "application/json" : "application/x-www-form-urlencoded" },
|
|
65
|
+
body: json ? JSON.stringify(body) : new URLSearchParams(body),
|
|
66
|
+
signal: AbortSignal.any([signal, AbortSignal.timeout(30_000)]),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
if (signal.aborted)
|
|
71
|
+
throw new CliError(130, "Interrupted. If you already approved access, review connected applications in Developers.");
|
|
72
|
+
if (error instanceof Error && ["TimeoutError", "AbortError"].includes(error.name))
|
|
73
|
+
throw new CliError(7, "Authorization request timed out.");
|
|
74
|
+
throw new CliError(7, "Authorization network request failed.");
|
|
75
|
+
}
|
|
76
|
+
let value;
|
|
77
|
+
try {
|
|
78
|
+
value = await response.json();
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
throw new CliError(6, "Authorization returned an unreadable response.");
|
|
82
|
+
}
|
|
83
|
+
if (!response.ok) {
|
|
84
|
+
const parsed = z.object({ error: z.string() }).safeParse(value);
|
|
85
|
+
throw new OAuthFailure(parsed.success ? parsed.data.error : "unknown", response.status);
|
|
86
|
+
}
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
89
|
+
function tokenSession(value, origin, clientId, ceiling) {
|
|
90
|
+
const parsed = tokenSchema.safeParse(value);
|
|
91
|
+
if (!parsed.success)
|
|
92
|
+
throw new CliError(6, "Authorization returned an invalid token response.");
|
|
93
|
+
const token = parsed.data;
|
|
94
|
+
const allowed = new Set(ceiling.split(" "));
|
|
95
|
+
if (token.scope.split(" ").some((scope) => !allowed.has(scope))) {
|
|
96
|
+
throw new CliError(6, "Authorization returned scopes beyond the approved request. Review and revoke this app in Developers.");
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
version: 1,
|
|
100
|
+
origin,
|
|
101
|
+
clientId,
|
|
102
|
+
accessToken: token.access_token,
|
|
103
|
+
refreshToken: token.refresh_token,
|
|
104
|
+
expiresAt: Date.now() + token.expires_in * 1000,
|
|
105
|
+
scope: token.scope,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
async function persistIssued(directory, session) {
|
|
109
|
+
try {
|
|
110
|
+
await saveSession(directory, session);
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
throw new CliError(6, "Authorization succeeded but the session could not be saved reliably. Revoke this CLI connection in Developers, fix local storage, then log in again.", { cause: error });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
export async function login(signal) {
|
|
117
|
+
const origin = apiOrigin();
|
|
118
|
+
return withSessionLock(async (directory) => {
|
|
119
|
+
if (await readSession(directory))
|
|
120
|
+
throw new CliError(2, "A CLI session is already stored. Run logout before connecting another account.");
|
|
121
|
+
if (process.env.OXINSIDER_API_KEY)
|
|
122
|
+
console.error("OXINSIDER_API_KEY is set. It will continue to override the stored session for data commands.");
|
|
123
|
+
const registration = await oauthRequest(origin, "/oauth/register", {
|
|
124
|
+
client_name: "0xinsider product CLI",
|
|
125
|
+
grant_types: [DEVICE_GRANT, "refresh_token"],
|
|
126
|
+
response_types: [],
|
|
127
|
+
scope: LOGIN_SCOPE,
|
|
128
|
+
}, signal, true);
|
|
129
|
+
const client = z.object({ client_id: z.string().min(1) }).safeParse(registration);
|
|
130
|
+
if (!client.success)
|
|
131
|
+
throw new CliError(6, "Authorization returned an invalid client registration.");
|
|
132
|
+
const started = deviceSchema.safeParse(await oauthRequest(origin, "/oauth/device/code", { client_id: client.data.client_id, scope: LOGIN_SCOPE }, signal));
|
|
133
|
+
if (!started.success)
|
|
134
|
+
throw new CliError(6, "Authorization returned an invalid device request.");
|
|
135
|
+
const device = started.data;
|
|
136
|
+
const verification = new URL(device.verification_uri);
|
|
137
|
+
const expectedHost = origin === API_ORIGIN ? "0xinsider.com" : new URL(origin).hostname;
|
|
138
|
+
if (verification.hostname !== expectedHost ||
|
|
139
|
+
verification.pathname !== "/oauth/device" ||
|
|
140
|
+
verification.username ||
|
|
141
|
+
verification.password ||
|
|
142
|
+
verification.search ||
|
|
143
|
+
verification.hash ||
|
|
144
|
+
(origin === API_ORIGIN
|
|
145
|
+
? verification.protocol !== "https:"
|
|
146
|
+
: !["http:", "https:"].includes(verification.protocol))) {
|
|
147
|
+
throw new CliError(6, "Authorization returned an unexpected verification URL.");
|
|
148
|
+
}
|
|
149
|
+
console.error(`Open ${verification.href}\nEnter code: ${device.user_code}\nReview the account and scopes, then approve in your browser. Waiting for your decision...`);
|
|
150
|
+
let interval = device.interval * 1000;
|
|
151
|
+
const deadline = performance.now() + device.expires_in * 1000;
|
|
152
|
+
while (performance.now() < deadline) {
|
|
153
|
+
try {
|
|
154
|
+
await delay(Math.min(interval, Math.max(0, deadline - performance.now())), undefined, {
|
|
155
|
+
signal,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
throw new CliError(130, "Login interrupted. If you approved access, review connected applications in Developers.");
|
|
160
|
+
}
|
|
161
|
+
if (performance.now() >= deadline)
|
|
162
|
+
break;
|
|
163
|
+
try {
|
|
164
|
+
const value = await oauthRequest(origin, "/oauth/token", {
|
|
165
|
+
grant_type: DEVICE_GRANT,
|
|
166
|
+
client_id: client.data.client_id,
|
|
167
|
+
device_code: device.device_code,
|
|
168
|
+
}, signal);
|
|
169
|
+
const session = tokenSession(value, origin, client.data.client_id, LOGIN_SCOPE);
|
|
170
|
+
await persistIssued(directory, session);
|
|
171
|
+
return {
|
|
172
|
+
signed_in: true,
|
|
173
|
+
source: "stored_session",
|
|
174
|
+
scope: session.scope,
|
|
175
|
+
expires_at: new Date(session.expiresAt).toISOString(),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
if (error instanceof OAuthFailure && error.oauthCode === "authorization_pending")
|
|
180
|
+
continue;
|
|
181
|
+
if (error instanceof OAuthFailure && error.oauthCode === "slow_down") {
|
|
182
|
+
interval += 5000;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (error instanceof CliError && error.exitCode === 7) {
|
|
186
|
+
interval = Math.max(interval * 2, interval + 5000);
|
|
187
|
+
console.error("Authorization polling was interrupted by the network; reducing polling frequency.");
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
throw new CliError(3, "The login code expired. Run login again.");
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
export async function credentials(signal, rejectedAccessToken) {
|
|
197
|
+
const origin = apiOrigin();
|
|
198
|
+
const environment = process.env.OXINSIDER_API_KEY;
|
|
199
|
+
if (environment)
|
|
200
|
+
return { apiKey: environment, origin, source: "environment" };
|
|
201
|
+
return withSessionLock(async (directory) => {
|
|
202
|
+
let session = await readSession(directory);
|
|
203
|
+
if (!session)
|
|
204
|
+
throw new CliError(3, "Sign in with `0xinsider login` or set OXINSIDER_API_KEY.");
|
|
205
|
+
if (session.origin !== origin)
|
|
206
|
+
throw new CliError(3, "The stored session belongs to a different API origin. Restore OXINSIDER_API_URL or use a separate OXINSIDER_CONFIG_DIR.");
|
|
207
|
+
signal.throwIfAborted();
|
|
208
|
+
if (session.expiresAt <= Date.now() + 60_000 || session.accessToken === rejectedAccessToken) {
|
|
209
|
+
const response = await oauthRequest(origin, "/oauth/token", {
|
|
210
|
+
grant_type: "refresh_token",
|
|
211
|
+
client_id: session.clientId,
|
|
212
|
+
refresh_token: session.refreshToken,
|
|
213
|
+
}, signal);
|
|
214
|
+
session = tokenSession(response, origin, session.clientId, session.scope);
|
|
215
|
+
await persistIssued(directory, session);
|
|
216
|
+
}
|
|
217
|
+
return { apiKey: session.accessToken, origin, source: "stored_session" };
|
|
218
|
+
}, signal);
|
|
219
|
+
}
|
|
220
|
+
export async function logout(signal) {
|
|
221
|
+
const origin = apiOrigin();
|
|
222
|
+
return withSessionLock(async (directory) => {
|
|
223
|
+
const session = await readSession(directory);
|
|
224
|
+
if (!session)
|
|
225
|
+
return {
|
|
226
|
+
signed_out: true,
|
|
227
|
+
stored_session: false,
|
|
228
|
+
environment_credential_unchanged: Boolean(process.env.OXINSIDER_API_KEY),
|
|
229
|
+
};
|
|
230
|
+
if (session.origin !== origin)
|
|
231
|
+
throw new CliError(3, "Restore the stored session's API origin before logout; credentials will not be sent elsewhere.");
|
|
232
|
+
// Refresh-token revocation still works after access-token expiry or subscription lapse.
|
|
233
|
+
await oauthRequest(origin, "/oauth/revoke", {
|
|
234
|
+
client_id: session.clientId,
|
|
235
|
+
token: session.refreshToken,
|
|
236
|
+
token_type_hint: "refresh_token",
|
|
237
|
+
}, signal);
|
|
238
|
+
await removeSession(directory);
|
|
239
|
+
return {
|
|
240
|
+
signed_out: true,
|
|
241
|
+
stored_session: true,
|
|
242
|
+
environment_credential_unchanged: Boolean(process.env.OXINSIDER_API_KEY),
|
|
243
|
+
};
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
//# sourceMappingURL=oauth.js.map
|