@parix/cli 0.1.8 → 0.1.9
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/dist/cli.cjs +4296 -35
- package/package.json +2 -2
- package/dist/cli.d.cts +0 -2
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +0 -36
- package/dist/commands/api.cjs +0 -81
- package/dist/commands/api.d.cts +0 -2
- package/dist/commands/api.d.ts +0 -2
- package/dist/commands/api.js +0 -78
- package/dist/commands/auth.cjs +0 -131
- package/dist/commands/auth.d.cts +0 -2
- package/dist/commands/auth.d.ts +0 -2
- package/dist/commands/auth.js +0 -128
- package/dist/commands/database.cjs +0 -259
- package/dist/commands/database.d.cts +0 -2
- package/dist/commands/database.d.ts +0 -2
- package/dist/commands/database.js +0 -256
- package/dist/commands/skill-content.cjs +0 -77
- package/dist/commands/skill-content.d.cts +0 -1
- package/dist/commands/skill-content.d.ts +0 -1
- package/dist/commands/skill-content.js +0 -74
- package/dist/commands/skill.cjs +0 -10
- package/dist/commands/skill.d.cts +0 -2
- package/dist/commands/skill.d.ts +0 -2
- package/dist/commands/skill.js +0 -7
- package/dist/commands/tb.cjs +0 -254
- package/dist/commands/tb.d.cts +0 -2
- package/dist/commands/tb.d.ts +0 -2
- package/dist/commands/tb.js +0 -251
- package/dist/lib/api-client.cjs +0 -44
- package/dist/lib/api-client.d.cts +0 -10
- package/dist/lib/api-client.d.ts +0 -10
- package/dist/lib/api-client.js +0 -40
- package/dist/lib/config.cjs +0 -24
- package/dist/lib/config.d.cts +0 -10
- package/dist/lib/config.d.ts +0 -10
- package/dist/lib/config.js +0 -18
- package/dist/lib/loopback-server.cjs +0 -180
- package/dist/lib/loopback-server.d.cts +0 -20
- package/dist/lib/loopback-server.d.ts +0 -20
- package/dist/lib/loopback-server.js +0 -177
- package/dist/lib/oauth-api.cjs +0 -73
- package/dist/lib/oauth-api.d.cts +0 -31
- package/dist/lib/oauth-api.d.ts +0 -31
- package/dist/lib/oauth-api.js +0 -68
- package/dist/lib/oauth-session.cjs +0 -108
- package/dist/lib/oauth-session.d.cts +0 -32
- package/dist/lib/oauth-session.d.ts +0 -32
- package/dist/lib/oauth-session.js +0 -103
- package/dist/lib/oauth.cjs +0 -58
- package/dist/lib/oauth.d.cts +0 -19
- package/dist/lib/oauth.d.ts +0 -19
- package/dist/lib/oauth.js +0 -49
- package/dist/lib/open-url.cjs +0 -38
- package/dist/lib/open-url.d.cts +0 -1
- package/dist/lib/open-url.d.ts +0 -1
- package/dist/lib/open-url.js +0 -32
- package/dist/lib/output.cjs +0 -20
- package/dist/lib/output.d.cts +0 -6
- package/dist/lib/output.d.ts +0 -6
- package/dist/lib/output.js +0 -15
- package/dist/lib/parix-api.cjs +0 -59
- package/dist/lib/parix-api.d.cts +0 -12
- package/dist/lib/parix-api.d.ts +0 -12
- package/dist/lib/parix-api.js +0 -55
- package/dist/lib/session.cjs +0 -80
- package/dist/lib/session.d.cts +0 -28
- package/dist/lib/session.d.ts +0 -28
- package/dist/lib/session.js +0 -74
- package/dist/lib/tb-payloads.cjs +0 -258
- package/dist/lib/tb-payloads.d.cts +0 -66
- package/dist/lib/tb-payloads.d.ts +0 -66
- package/dist/lib/tb-payloads.js +0 -249
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { StoredSession } from "./session.cjs";
|
|
2
|
-
interface ApiClientOptions {
|
|
3
|
-
session: StoredSession;
|
|
4
|
-
}
|
|
5
|
-
export declare class ApiClient {
|
|
6
|
-
private session;
|
|
7
|
-
constructor(options: ApiClientOptions);
|
|
8
|
-
request(pathname: string, init?: RequestInit): Promise<Response>;
|
|
9
|
-
}
|
|
10
|
-
export {};
|
package/dist/lib/api-client.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { StoredSession } from "./session.js";
|
|
2
|
-
interface ApiClientOptions {
|
|
3
|
-
session: StoredSession;
|
|
4
|
-
}
|
|
5
|
-
export declare class ApiClient {
|
|
6
|
-
private session;
|
|
7
|
-
constructor(options: ApiClientOptions);
|
|
8
|
-
request(pathname: string, init?: RequestInit): Promise<Response>;
|
|
9
|
-
}
|
|
10
|
-
export {};
|
package/dist/lib/api-client.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { ensureFreshSession } from "./oauth-session.js";
|
|
2
|
-
const TRAILING_SLASHES_REGEX = /\/+$/;
|
|
3
|
-
export class ApiClient {
|
|
4
|
-
session;
|
|
5
|
-
constructor(options) {
|
|
6
|
-
this.session = options.session;
|
|
7
|
-
}
|
|
8
|
-
async request(pathname, init = {}) {
|
|
9
|
-
this.session = await ensureFreshSession(this.session);
|
|
10
|
-
const url = toRequestUrl(pathname, this.session.baseUrl);
|
|
11
|
-
const headers = new Headers(init.headers);
|
|
12
|
-
headers.set('authorization', `Bearer ${this.session.accessToken}`);
|
|
13
|
-
const response = await fetch(url, {
|
|
14
|
-
...init,
|
|
15
|
-
headers,
|
|
16
|
-
});
|
|
17
|
-
if (response.status !== 401) {
|
|
18
|
-
return response;
|
|
19
|
-
}
|
|
20
|
-
this.session = await ensureFreshSession({
|
|
21
|
-
...this.session,
|
|
22
|
-
accessTokenExpiresAt: new Date(0).toISOString(),
|
|
23
|
-
});
|
|
24
|
-
const retryHeaders = new Headers(init.headers);
|
|
25
|
-
retryHeaders.set('authorization', `Bearer ${this.session.accessToken}`);
|
|
26
|
-
return fetch(url, {
|
|
27
|
-
...init,
|
|
28
|
-
headers: retryHeaders,
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
function toRequestUrl(pathname, baseUrl) {
|
|
33
|
-
if (pathname.startsWith('http://') || pathname.startsWith('https://')) {
|
|
34
|
-
return pathname;
|
|
35
|
-
}
|
|
36
|
-
return new URL(pathname, `${trimTrailingSlash(baseUrl)}/`).toString();
|
|
37
|
-
}
|
|
38
|
-
function trimTrailingSlash(value) {
|
|
39
|
-
return value.replace(TRAILING_SLASHES_REGEX, '');
|
|
40
|
-
}
|
package/dist/lib/config.cjs
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.LOGIN_TIMEOUT_MS = exports.DEFAULT_BASE_URL = void 0;
|
|
4
|
-
exports.resolveBaseUrl = resolveBaseUrl;
|
|
5
|
-
exports.trimTrailingSlash = trimTrailingSlash;
|
|
6
|
-
exports.parsePortOption = parsePortOption;
|
|
7
|
-
const TRAILING_SLASHES_REGEX = /\/+$/;
|
|
8
|
-
exports.DEFAULT_BASE_URL = 'https://parix.io';
|
|
9
|
-
exports.LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
10
|
-
function resolveBaseUrl(input, storedSession, options = {}) {
|
|
11
|
-
const storedSessionBaseUrl = options.useStoredSession === false ? undefined : storedSession?.baseUrl;
|
|
12
|
-
const value = input ?? storedSessionBaseUrl ?? process.env.PARIX_BASE_URL ?? exports.DEFAULT_BASE_URL;
|
|
13
|
-
return trimTrailingSlash(new URL(value).toString());
|
|
14
|
-
}
|
|
15
|
-
function trimTrailingSlash(value) {
|
|
16
|
-
return value.replace(TRAILING_SLASHES_REGEX, '');
|
|
17
|
-
}
|
|
18
|
-
function parsePortOption(value) {
|
|
19
|
-
const parsed = Number.parseInt(value, 10);
|
|
20
|
-
if (!Number.isInteger(parsed) || parsed < 0 || parsed > 65535) {
|
|
21
|
-
throw new Error(`Invalid port: ${value}`);
|
|
22
|
-
}
|
|
23
|
-
return parsed;
|
|
24
|
-
}
|
package/dist/lib/config.d.cts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { StoredSession } from "./session.cjs";
|
|
2
|
-
export declare const DEFAULT_BASE_URL = "https://parix.io";
|
|
3
|
-
export declare const LOGIN_TIMEOUT_MS: number;
|
|
4
|
-
interface ResolveBaseUrlOptions {
|
|
5
|
-
useStoredSession?: boolean;
|
|
6
|
-
}
|
|
7
|
-
export declare function resolveBaseUrl(input: string | undefined, storedSession?: StoredSession | null, options?: ResolveBaseUrlOptions): string;
|
|
8
|
-
export declare function trimTrailingSlash(value: string): string;
|
|
9
|
-
export declare function parsePortOption(value: string): number;
|
|
10
|
-
export {};
|
package/dist/lib/config.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { StoredSession } from "./session.js";
|
|
2
|
-
export declare const DEFAULT_BASE_URL = "https://parix.io";
|
|
3
|
-
export declare const LOGIN_TIMEOUT_MS: number;
|
|
4
|
-
interface ResolveBaseUrlOptions {
|
|
5
|
-
useStoredSession?: boolean;
|
|
6
|
-
}
|
|
7
|
-
export declare function resolveBaseUrl(input: string | undefined, storedSession?: StoredSession | null, options?: ResolveBaseUrlOptions): string;
|
|
8
|
-
export declare function trimTrailingSlash(value: string): string;
|
|
9
|
-
export declare function parsePortOption(value: string): number;
|
|
10
|
-
export {};
|
package/dist/lib/config.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
const TRAILING_SLASHES_REGEX = /\/+$/;
|
|
2
|
-
export const DEFAULT_BASE_URL = 'https://parix.io';
|
|
3
|
-
export const LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
4
|
-
export function resolveBaseUrl(input, storedSession, options = {}) {
|
|
5
|
-
const storedSessionBaseUrl = options.useStoredSession === false ? undefined : storedSession?.baseUrl;
|
|
6
|
-
const value = input ?? storedSessionBaseUrl ?? process.env.PARIX_BASE_URL ?? DEFAULT_BASE_URL;
|
|
7
|
-
return trimTrailingSlash(new URL(value).toString());
|
|
8
|
-
}
|
|
9
|
-
export function trimTrailingSlash(value) {
|
|
10
|
-
return value.replace(TRAILING_SLASHES_REGEX, '');
|
|
11
|
-
}
|
|
12
|
-
export function parsePortOption(value) {
|
|
13
|
-
const parsed = Number.parseInt(value, 10);
|
|
14
|
-
if (!Number.isInteger(parsed) || parsed < 0 || parsed > 65535) {
|
|
15
|
-
throw new Error(`Invalid port: ${value}`);
|
|
16
|
-
}
|
|
17
|
-
return parsed;
|
|
18
|
-
}
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.startLoopbackServer = startLoopbackServer;
|
|
4
|
-
const node_http_1 = require("node:http");
|
|
5
|
-
async function startLoopbackServer(options) {
|
|
6
|
-
const server = (0, node_http_1.createServer)();
|
|
7
|
-
let completed = false;
|
|
8
|
-
let expectedState = options.state ?? null;
|
|
9
|
-
let resolveResult = null;
|
|
10
|
-
let rejectResult = null;
|
|
11
|
-
let timeout = null;
|
|
12
|
-
const clearTimeoutIfNeeded = () => {
|
|
13
|
-
if (timeout) {
|
|
14
|
-
clearTimeout(timeout);
|
|
15
|
-
timeout = null;
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
const settle = (handler, value, reason) => {
|
|
19
|
-
if (completed) {
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
completed = true;
|
|
23
|
-
clearTimeoutIfNeeded();
|
|
24
|
-
if (!handler) {
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
if (reason !== undefined) {
|
|
28
|
-
handler(reason);
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
if (value) {
|
|
32
|
-
handler(value);
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
const result = new Promise((resolve, reject) => {
|
|
36
|
-
resolveResult = resolve;
|
|
37
|
-
rejectResult = reject;
|
|
38
|
-
});
|
|
39
|
-
server.on('request', (request, response) => {
|
|
40
|
-
// One-shot OAuth loopback: do not keep browser sockets alive after the response.
|
|
41
|
-
response.setHeader('connection', 'close');
|
|
42
|
-
const requestUrl = new URL(request.url ?? '/', 'http://127.0.0.1');
|
|
43
|
-
if (requestUrl.pathname === '/favicon.ico') {
|
|
44
|
-
response.writeHead(204);
|
|
45
|
-
response.end();
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
if (requestUrl.pathname !== options.callbackPath) {
|
|
49
|
-
response.writeHead(404, { 'content-type': 'text/plain; charset=utf-8' });
|
|
50
|
-
response.end('Not found');
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
const returnedState = requestUrl.searchParams.get('state');
|
|
54
|
-
const code = requestUrl.searchParams.get('code');
|
|
55
|
-
const error = requestUrl.searchParams.get('error');
|
|
56
|
-
const errorDescription = requestUrl.searchParams.get('error_description');
|
|
57
|
-
if (!expectedState || returnedState !== expectedState) {
|
|
58
|
-
response.writeHead(400, { 'content-type': 'text/html; charset=utf-8' });
|
|
59
|
-
response.end(buildHtml('Authentication failed', 'The callback state did not match.'));
|
|
60
|
-
settle(rejectResult, undefined, new Error('The callback state did not match the original login request.'));
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
if (error) {
|
|
64
|
-
response.writeHead(400, { 'content-type': 'text/html; charset=utf-8' });
|
|
65
|
-
response.end(buildHtml('Authentication failed', errorDescription ?? error));
|
|
66
|
-
settle(rejectResult, undefined, new Error(errorDescription ?? error));
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
if (!code) {
|
|
70
|
-
response.writeHead(400, { 'content-type': 'text/html; charset=utf-8' });
|
|
71
|
-
response.end(buildHtml('Authentication failed', 'No authorization code was returned.'));
|
|
72
|
-
settle(rejectResult, undefined, new Error('No authorization code was returned from the browser callback.'));
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
if (options.successPage) {
|
|
76
|
-
response.writeHead(302, {
|
|
77
|
-
'cache-control': 'no-store',
|
|
78
|
-
location: options.successPage.redirectUrl,
|
|
79
|
-
});
|
|
80
|
-
response.end();
|
|
81
|
-
settle(resolveResult, { code });
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
response.writeHead(200, {
|
|
85
|
-
'cache-control': 'no-store',
|
|
86
|
-
'content-type': 'text/html; charset=utf-8',
|
|
87
|
-
});
|
|
88
|
-
response.end(buildHtml('Authentication complete', 'You can close the tab and return to the terminal.'));
|
|
89
|
-
settle(resolveResult, { code });
|
|
90
|
-
});
|
|
91
|
-
await listen(server, options.port ?? 0);
|
|
92
|
-
timeout = setTimeout(() => {
|
|
93
|
-
settle(rejectResult, undefined, new Error('Timed out waiting for the browser callback.'));
|
|
94
|
-
void closeServer(server);
|
|
95
|
-
}, options.timeoutMs);
|
|
96
|
-
const address = server.address();
|
|
97
|
-
if (!address || typeof address === 'string') {
|
|
98
|
-
throw new Error('Failed to determine the loopback callback port.');
|
|
99
|
-
}
|
|
100
|
-
return {
|
|
101
|
-
callbackUrl: `http://127.0.0.1:${address.port}${options.callbackPath}`,
|
|
102
|
-
setState: (state) => {
|
|
103
|
-
expectedState = state;
|
|
104
|
-
},
|
|
105
|
-
waitForResult: async () => {
|
|
106
|
-
return await result;
|
|
107
|
-
},
|
|
108
|
-
close: async () => {
|
|
109
|
-
clearTimeoutIfNeeded();
|
|
110
|
-
await closeServer(server);
|
|
111
|
-
},
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
function listen(server, port) {
|
|
115
|
-
return new Promise((resolve, reject) => {
|
|
116
|
-
server.once('error', reject);
|
|
117
|
-
server.listen({ host: '127.0.0.1', port }, () => {
|
|
118
|
-
server.off('error', reject);
|
|
119
|
-
resolve();
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
function closeServer(server) {
|
|
124
|
-
return new Promise((resolve, reject) => {
|
|
125
|
-
let settled = false;
|
|
126
|
-
const finish = (error) => {
|
|
127
|
-
if (settled) {
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
settled = true;
|
|
131
|
-
if (error) {
|
|
132
|
-
reject(error);
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
resolve();
|
|
136
|
-
};
|
|
137
|
-
server.close((error) => {
|
|
138
|
-
if (error) {
|
|
139
|
-
if ('code' in error && error.code === 'ERR_SERVER_NOT_RUNNING') {
|
|
140
|
-
finish();
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
finish(error);
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
finish();
|
|
147
|
-
});
|
|
148
|
-
// `server.close()` waits for existing connections. Browsers often keep the
|
|
149
|
-
// loopback socket open via HTTP keep-alive, which would hang login forever
|
|
150
|
-
// after the session is already stored. Force those sockets closed (Node >= 18.2).
|
|
151
|
-
server.closeAllConnections();
|
|
152
|
-
// Last resort: never let close block the CLI indefinitely.
|
|
153
|
-
setTimeout(() => {
|
|
154
|
-
finish();
|
|
155
|
-
}, 250).unref();
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
function buildHtml(title, message) {
|
|
159
|
-
return `<!doctype html>
|
|
160
|
-
<html lang="en">
|
|
161
|
-
<head>
|
|
162
|
-
<meta charset="utf-8" />
|
|
163
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
164
|
-
<title>${escapeHtml(title)}</title>
|
|
165
|
-
</head>
|
|
166
|
-
<body style="font-family: sans-serif; padding: 2rem; line-height: 1.5;">
|
|
167
|
-
<h1>${escapeHtml(title)}</h1>
|
|
168
|
-
<p>${escapeHtml(message)}</p>
|
|
169
|
-
<script>window.close()</script>
|
|
170
|
-
</body>
|
|
171
|
-
</html>`;
|
|
172
|
-
}
|
|
173
|
-
function escapeHtml(value) {
|
|
174
|
-
return value
|
|
175
|
-
.replaceAll('&', '&')
|
|
176
|
-
.replaceAll('<', '<')
|
|
177
|
-
.replaceAll('>', '>')
|
|
178
|
-
.replaceAll('"', '"')
|
|
179
|
-
.replaceAll("'", ''');
|
|
180
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
interface StartLoopbackServerOptions {
|
|
2
|
-
callbackPath: string;
|
|
3
|
-
port?: number;
|
|
4
|
-
successPage?: {
|
|
5
|
-
redirectUrl: string;
|
|
6
|
-
};
|
|
7
|
-
state?: string;
|
|
8
|
-
timeoutMs: number;
|
|
9
|
-
}
|
|
10
|
-
interface CallbackResult {
|
|
11
|
-
code: string;
|
|
12
|
-
}
|
|
13
|
-
export interface LoopbackServer {
|
|
14
|
-
callbackUrl: string;
|
|
15
|
-
setState: (state: string) => void;
|
|
16
|
-
waitForResult: () => Promise<CallbackResult>;
|
|
17
|
-
close: () => Promise<void>;
|
|
18
|
-
}
|
|
19
|
-
export declare function startLoopbackServer(options: StartLoopbackServerOptions): Promise<LoopbackServer>;
|
|
20
|
-
export {};
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
interface StartLoopbackServerOptions {
|
|
2
|
-
callbackPath: string;
|
|
3
|
-
port?: number;
|
|
4
|
-
successPage?: {
|
|
5
|
-
redirectUrl: string;
|
|
6
|
-
};
|
|
7
|
-
state?: string;
|
|
8
|
-
timeoutMs: number;
|
|
9
|
-
}
|
|
10
|
-
interface CallbackResult {
|
|
11
|
-
code: string;
|
|
12
|
-
}
|
|
13
|
-
export interface LoopbackServer {
|
|
14
|
-
callbackUrl: string;
|
|
15
|
-
setState: (state: string) => void;
|
|
16
|
-
waitForResult: () => Promise<CallbackResult>;
|
|
17
|
-
close: () => Promise<void>;
|
|
18
|
-
}
|
|
19
|
-
export declare function startLoopbackServer(options: StartLoopbackServerOptions): Promise<LoopbackServer>;
|
|
20
|
-
export {};
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import { createServer } from 'node:http';
|
|
2
|
-
export async function startLoopbackServer(options) {
|
|
3
|
-
const server = createServer();
|
|
4
|
-
let completed = false;
|
|
5
|
-
let expectedState = options.state ?? null;
|
|
6
|
-
let resolveResult = null;
|
|
7
|
-
let rejectResult = null;
|
|
8
|
-
let timeout = null;
|
|
9
|
-
const clearTimeoutIfNeeded = () => {
|
|
10
|
-
if (timeout) {
|
|
11
|
-
clearTimeout(timeout);
|
|
12
|
-
timeout = null;
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
|
-
const settle = (handler, value, reason) => {
|
|
16
|
-
if (completed) {
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
completed = true;
|
|
20
|
-
clearTimeoutIfNeeded();
|
|
21
|
-
if (!handler) {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
if (reason !== undefined) {
|
|
25
|
-
handler(reason);
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
if (value) {
|
|
29
|
-
handler(value);
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
const result = new Promise((resolve, reject) => {
|
|
33
|
-
resolveResult = resolve;
|
|
34
|
-
rejectResult = reject;
|
|
35
|
-
});
|
|
36
|
-
server.on('request', (request, response) => {
|
|
37
|
-
// One-shot OAuth loopback: do not keep browser sockets alive after the response.
|
|
38
|
-
response.setHeader('connection', 'close');
|
|
39
|
-
const requestUrl = new URL(request.url ?? '/', 'http://127.0.0.1');
|
|
40
|
-
if (requestUrl.pathname === '/favicon.ico') {
|
|
41
|
-
response.writeHead(204);
|
|
42
|
-
response.end();
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
if (requestUrl.pathname !== options.callbackPath) {
|
|
46
|
-
response.writeHead(404, { 'content-type': 'text/plain; charset=utf-8' });
|
|
47
|
-
response.end('Not found');
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
const returnedState = requestUrl.searchParams.get('state');
|
|
51
|
-
const code = requestUrl.searchParams.get('code');
|
|
52
|
-
const error = requestUrl.searchParams.get('error');
|
|
53
|
-
const errorDescription = requestUrl.searchParams.get('error_description');
|
|
54
|
-
if (!expectedState || returnedState !== expectedState) {
|
|
55
|
-
response.writeHead(400, { 'content-type': 'text/html; charset=utf-8' });
|
|
56
|
-
response.end(buildHtml('Authentication failed', 'The callback state did not match.'));
|
|
57
|
-
settle(rejectResult, undefined, new Error('The callback state did not match the original login request.'));
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
if (error) {
|
|
61
|
-
response.writeHead(400, { 'content-type': 'text/html; charset=utf-8' });
|
|
62
|
-
response.end(buildHtml('Authentication failed', errorDescription ?? error));
|
|
63
|
-
settle(rejectResult, undefined, new Error(errorDescription ?? error));
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
if (!code) {
|
|
67
|
-
response.writeHead(400, { 'content-type': 'text/html; charset=utf-8' });
|
|
68
|
-
response.end(buildHtml('Authentication failed', 'No authorization code was returned.'));
|
|
69
|
-
settle(rejectResult, undefined, new Error('No authorization code was returned from the browser callback.'));
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
if (options.successPage) {
|
|
73
|
-
response.writeHead(302, {
|
|
74
|
-
'cache-control': 'no-store',
|
|
75
|
-
location: options.successPage.redirectUrl,
|
|
76
|
-
});
|
|
77
|
-
response.end();
|
|
78
|
-
settle(resolveResult, { code });
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
response.writeHead(200, {
|
|
82
|
-
'cache-control': 'no-store',
|
|
83
|
-
'content-type': 'text/html; charset=utf-8',
|
|
84
|
-
});
|
|
85
|
-
response.end(buildHtml('Authentication complete', 'You can close the tab and return to the terminal.'));
|
|
86
|
-
settle(resolveResult, { code });
|
|
87
|
-
});
|
|
88
|
-
await listen(server, options.port ?? 0);
|
|
89
|
-
timeout = setTimeout(() => {
|
|
90
|
-
settle(rejectResult, undefined, new Error('Timed out waiting for the browser callback.'));
|
|
91
|
-
void closeServer(server);
|
|
92
|
-
}, options.timeoutMs);
|
|
93
|
-
const address = server.address();
|
|
94
|
-
if (!address || typeof address === 'string') {
|
|
95
|
-
throw new Error('Failed to determine the loopback callback port.');
|
|
96
|
-
}
|
|
97
|
-
return {
|
|
98
|
-
callbackUrl: `http://127.0.0.1:${address.port}${options.callbackPath}`,
|
|
99
|
-
setState: (state) => {
|
|
100
|
-
expectedState = state;
|
|
101
|
-
},
|
|
102
|
-
waitForResult: async () => {
|
|
103
|
-
return await result;
|
|
104
|
-
},
|
|
105
|
-
close: async () => {
|
|
106
|
-
clearTimeoutIfNeeded();
|
|
107
|
-
await closeServer(server);
|
|
108
|
-
},
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
function listen(server, port) {
|
|
112
|
-
return new Promise((resolve, reject) => {
|
|
113
|
-
server.once('error', reject);
|
|
114
|
-
server.listen({ host: '127.0.0.1', port }, () => {
|
|
115
|
-
server.off('error', reject);
|
|
116
|
-
resolve();
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
function closeServer(server) {
|
|
121
|
-
return new Promise((resolve, reject) => {
|
|
122
|
-
let settled = false;
|
|
123
|
-
const finish = (error) => {
|
|
124
|
-
if (settled) {
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
settled = true;
|
|
128
|
-
if (error) {
|
|
129
|
-
reject(error);
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
resolve();
|
|
133
|
-
};
|
|
134
|
-
server.close((error) => {
|
|
135
|
-
if (error) {
|
|
136
|
-
if ('code' in error && error.code === 'ERR_SERVER_NOT_RUNNING') {
|
|
137
|
-
finish();
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
finish(error);
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
finish();
|
|
144
|
-
});
|
|
145
|
-
// `server.close()` waits for existing connections. Browsers often keep the
|
|
146
|
-
// loopback socket open via HTTP keep-alive, which would hang login forever
|
|
147
|
-
// after the session is already stored. Force those sockets closed (Node >= 18.2).
|
|
148
|
-
server.closeAllConnections();
|
|
149
|
-
// Last resort: never let close block the CLI indefinitely.
|
|
150
|
-
setTimeout(() => {
|
|
151
|
-
finish();
|
|
152
|
-
}, 250).unref();
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
function buildHtml(title, message) {
|
|
156
|
-
return `<!doctype html>
|
|
157
|
-
<html lang="en">
|
|
158
|
-
<head>
|
|
159
|
-
<meta charset="utf-8" />
|
|
160
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
161
|
-
<title>${escapeHtml(title)}</title>
|
|
162
|
-
</head>
|
|
163
|
-
<body style="font-family: sans-serif; padding: 2rem; line-height: 1.5;">
|
|
164
|
-
<h1>${escapeHtml(title)}</h1>
|
|
165
|
-
<p>${escapeHtml(message)}</p>
|
|
166
|
-
<script>window.close()</script>
|
|
167
|
-
</body>
|
|
168
|
-
</html>`;
|
|
169
|
-
}
|
|
170
|
-
function escapeHtml(value) {
|
|
171
|
-
return value
|
|
172
|
-
.replaceAll('&', '&')
|
|
173
|
-
.replaceAll('<', '<')
|
|
174
|
-
.replaceAll('>', '>')
|
|
175
|
-
.replaceAll('"', '"')
|
|
176
|
-
.replaceAll("'", ''');
|
|
177
|
-
}
|
package/dist/lib/oauth-api.cjs
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.exchangeAuthorizationCode = exchangeAuthorizationCode;
|
|
4
|
-
exports.refreshOAuthTokens = refreshOAuthTokens;
|
|
5
|
-
exports.fetchOAuthUserInfo = fetchOAuthUserInfo;
|
|
6
|
-
const oauth_1 = require("./oauth.cjs");
|
|
7
|
-
const WHITESPACE_REGEX = /\s+/;
|
|
8
|
-
async function exchangeAuthorizationCode(input) {
|
|
9
|
-
const body = new URLSearchParams({
|
|
10
|
-
client_id: oauth_1.PARIX_CLIENT_ID,
|
|
11
|
-
code: input.code,
|
|
12
|
-
code_verifier: input.codeVerifier,
|
|
13
|
-
grant_type: 'authorization_code',
|
|
14
|
-
redirect_uri: (0, oauth_1.buildOAuthCallbackUrl)(input.baseUrl),
|
|
15
|
-
});
|
|
16
|
-
const response = await fetch((0, oauth_1.buildOAuthTokenUrl)(input.baseUrl), {
|
|
17
|
-
body,
|
|
18
|
-
headers: {
|
|
19
|
-
'content-type': 'application/x-www-form-urlencoded',
|
|
20
|
-
},
|
|
21
|
-
method: 'POST',
|
|
22
|
-
});
|
|
23
|
-
return await parseTokenResponse(response);
|
|
24
|
-
}
|
|
25
|
-
async function refreshOAuthTokens(input) {
|
|
26
|
-
const body = new URLSearchParams({
|
|
27
|
-
client_id: oauth_1.PARIX_CLIENT_ID,
|
|
28
|
-
grant_type: 'refresh_token',
|
|
29
|
-
refresh_token: input.refreshToken,
|
|
30
|
-
});
|
|
31
|
-
const response = await fetch((0, oauth_1.buildOAuthTokenUrl)(input.baseUrl), {
|
|
32
|
-
body,
|
|
33
|
-
headers: {
|
|
34
|
-
'content-type': 'application/x-www-form-urlencoded',
|
|
35
|
-
},
|
|
36
|
-
method: 'POST',
|
|
37
|
-
});
|
|
38
|
-
return await parseTokenResponse(response);
|
|
39
|
-
}
|
|
40
|
-
async function fetchOAuthUserInfo(input) {
|
|
41
|
-
const response = await fetch((0, oauth_1.buildOAuthUserInfoUrl)(input.baseUrl), {
|
|
42
|
-
headers: {
|
|
43
|
-
authorization: `Bearer ${input.accessToken}`,
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
if (!response.ok) {
|
|
47
|
-
throw new Error(await readErrorMessage(response, 'Unable to fetch the authenticated user.'));
|
|
48
|
-
}
|
|
49
|
-
return (await response.json());
|
|
50
|
-
}
|
|
51
|
-
async function parseTokenResponse(response) {
|
|
52
|
-
if (!response.ok) {
|
|
53
|
-
throw new Error(await readErrorMessage(response, 'Unable to exchange OAuth tokens.'));
|
|
54
|
-
}
|
|
55
|
-
const payload = (await response.json());
|
|
56
|
-
return {
|
|
57
|
-
accessToken: payload.access_token,
|
|
58
|
-
expiresIn: payload.expires_in,
|
|
59
|
-
refreshToken: payload.refresh_token ?? null,
|
|
60
|
-
scope: payload.scope?.split(WHITESPACE_REGEX).filter(Boolean) ?? [],
|
|
61
|
-
tokenType: payload.token_type,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
async function readErrorMessage(response, fallback) {
|
|
65
|
-
try {
|
|
66
|
-
const payload = (await response.clone().json());
|
|
67
|
-
return payload.error_description ?? payload.message ?? payload.error ?? fallback;
|
|
68
|
-
}
|
|
69
|
-
catch {
|
|
70
|
-
const text = await response.text();
|
|
71
|
-
return text.trim() || fallback;
|
|
72
|
-
}
|
|
73
|
-
}
|
package/dist/lib/oauth-api.d.cts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
export interface OAuthTokenSet {
|
|
2
|
-
accessToken: string;
|
|
3
|
-
expiresIn: number;
|
|
4
|
-
refreshToken: string | null;
|
|
5
|
-
scope: string[];
|
|
6
|
-
tokenType: string;
|
|
7
|
-
}
|
|
8
|
-
export interface OAuthUserInfo {
|
|
9
|
-
sub: string;
|
|
10
|
-
email?: string | null;
|
|
11
|
-
email_verified?: boolean;
|
|
12
|
-
name?: string | null;
|
|
13
|
-
picture?: string | null;
|
|
14
|
-
organization_id?: string | null;
|
|
15
|
-
organization_name?: string | null;
|
|
16
|
-
organization_slug?: string | null;
|
|
17
|
-
member_role?: string | null;
|
|
18
|
-
}
|
|
19
|
-
export declare function exchangeAuthorizationCode(input: {
|
|
20
|
-
baseUrl: string;
|
|
21
|
-
code: string;
|
|
22
|
-
codeVerifier: string;
|
|
23
|
-
}): Promise<OAuthTokenSet>;
|
|
24
|
-
export declare function refreshOAuthTokens(input: {
|
|
25
|
-
baseUrl: string;
|
|
26
|
-
refreshToken: string;
|
|
27
|
-
}): Promise<OAuthTokenSet>;
|
|
28
|
-
export declare function fetchOAuthUserInfo(input: {
|
|
29
|
-
accessToken: string;
|
|
30
|
-
baseUrl: string;
|
|
31
|
-
}): Promise<OAuthUserInfo>;
|
package/dist/lib/oauth-api.d.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
export interface OAuthTokenSet {
|
|
2
|
-
accessToken: string;
|
|
3
|
-
expiresIn: number;
|
|
4
|
-
refreshToken: string | null;
|
|
5
|
-
scope: string[];
|
|
6
|
-
tokenType: string;
|
|
7
|
-
}
|
|
8
|
-
export interface OAuthUserInfo {
|
|
9
|
-
sub: string;
|
|
10
|
-
email?: string | null;
|
|
11
|
-
email_verified?: boolean;
|
|
12
|
-
name?: string | null;
|
|
13
|
-
picture?: string | null;
|
|
14
|
-
organization_id?: string | null;
|
|
15
|
-
organization_name?: string | null;
|
|
16
|
-
organization_slug?: string | null;
|
|
17
|
-
member_role?: string | null;
|
|
18
|
-
}
|
|
19
|
-
export declare function exchangeAuthorizationCode(input: {
|
|
20
|
-
baseUrl: string;
|
|
21
|
-
code: string;
|
|
22
|
-
codeVerifier: string;
|
|
23
|
-
}): Promise<OAuthTokenSet>;
|
|
24
|
-
export declare function refreshOAuthTokens(input: {
|
|
25
|
-
baseUrl: string;
|
|
26
|
-
refreshToken: string;
|
|
27
|
-
}): Promise<OAuthTokenSet>;
|
|
28
|
-
export declare function fetchOAuthUserInfo(input: {
|
|
29
|
-
accessToken: string;
|
|
30
|
-
baseUrl: string;
|
|
31
|
-
}): Promise<OAuthUserInfo>;
|