@parix/cli 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +123 -0
  2. package/dist/cli.cjs +30 -0
  3. package/dist/cli.d.cts +2 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +28 -0
  6. package/dist/commands/api.cjs +81 -0
  7. package/dist/commands/api.d.cts +2 -0
  8. package/dist/commands/api.d.ts +2 -0
  9. package/dist/commands/api.js +78 -0
  10. package/dist/commands/auth.cjs +128 -0
  11. package/dist/commands/auth.d.cts +2 -0
  12. package/dist/commands/auth.d.ts +2 -0
  13. package/dist/commands/auth.js +125 -0
  14. package/dist/commands/database.cjs +259 -0
  15. package/dist/commands/database.d.cts +2 -0
  16. package/dist/commands/database.d.ts +2 -0
  17. package/dist/commands/database.js +256 -0
  18. package/dist/commands/tb.cjs +242 -0
  19. package/dist/commands/tb.d.cts +2 -0
  20. package/dist/commands/tb.d.ts +2 -0
  21. package/dist/commands/tb.js +239 -0
  22. package/dist/lib/api-client.cjs +44 -0
  23. package/dist/lib/api-client.d.cts +10 -0
  24. package/dist/lib/api-client.d.ts +10 -0
  25. package/dist/lib/api-client.js +40 -0
  26. package/dist/lib/config.cjs +24 -0
  27. package/dist/lib/config.d.cts +10 -0
  28. package/dist/lib/config.d.ts +10 -0
  29. package/dist/lib/config.js +18 -0
  30. package/dist/lib/loopback-server.cjs +158 -0
  31. package/dist/lib/loopback-server.d.cts +20 -0
  32. package/dist/lib/loopback-server.d.ts +20 -0
  33. package/dist/lib/loopback-server.js +155 -0
  34. package/dist/lib/oauth-api.cjs +73 -0
  35. package/dist/lib/oauth-api.d.cts +31 -0
  36. package/dist/lib/oauth-api.d.ts +31 -0
  37. package/dist/lib/oauth-api.js +68 -0
  38. package/dist/lib/oauth-session.cjs +108 -0
  39. package/dist/lib/oauth-session.d.cts +32 -0
  40. package/dist/lib/oauth-session.d.ts +32 -0
  41. package/dist/lib/oauth-session.js +103 -0
  42. package/dist/lib/oauth.cjs +58 -0
  43. package/dist/lib/oauth.d.cts +19 -0
  44. package/dist/lib/oauth.d.ts +19 -0
  45. package/dist/lib/oauth.js +49 -0
  46. package/dist/lib/open-url.cjs +38 -0
  47. package/dist/lib/open-url.d.cts +1 -0
  48. package/dist/lib/open-url.d.ts +1 -0
  49. package/dist/lib/open-url.js +32 -0
  50. package/dist/lib/output.cjs +20 -0
  51. package/dist/lib/output.d.cts +6 -0
  52. package/dist/lib/output.d.ts +6 -0
  53. package/dist/lib/output.js +15 -0
  54. package/dist/lib/parix-api.cjs +59 -0
  55. package/dist/lib/parix-api.d.cts +12 -0
  56. package/dist/lib/parix-api.d.ts +12 -0
  57. package/dist/lib/parix-api.js +55 -0
  58. package/dist/lib/session.cjs +80 -0
  59. package/dist/lib/session.d.cts +28 -0
  60. package/dist/lib/session.d.ts +28 -0
  61. package/dist/lib/session.js +74 -0
  62. package/dist/lib/tb-payloads.cjs +153 -0
  63. package/dist/lib/tb-payloads.d.cts +61 -0
  64. package/dist/lib/tb-payloads.d.ts +61 -0
  65. package/dist/lib/tb-payloads.js +144 -0
  66. package/package.json +74 -0
@@ -0,0 +1,32 @@
1
+ import type { OAuthTokenSet } from "./oauth-api.cjs";
2
+ import type { StoredSession } from "./session.cjs";
3
+ export declare function createStoredSession(input: {
4
+ baseUrl: string;
5
+ createdAt?: string;
6
+ tokenSet: OAuthTokenSet;
7
+ }): Promise<{
8
+ version: 2;
9
+ accessToken: string;
10
+ accessTokenExpiresAt: string;
11
+ baseUrl: string;
12
+ createdAt: string;
13
+ refreshToken: string | null;
14
+ scopes: string[];
15
+ tokenType: string;
16
+ updatedAt: string;
17
+ user: {
18
+ email: string | null;
19
+ emailVerified: boolean | null;
20
+ id: string;
21
+ image: string | null;
22
+ name: string | null;
23
+ };
24
+ organization: {
25
+ id: string | null;
26
+ memberRole: string | null;
27
+ name: string | null;
28
+ slug: string | null;
29
+ };
30
+ }>;
31
+ export declare function hydrateStoredSessionOrganization(session: StoredSession): Promise<StoredSession>;
32
+ export declare function ensureFreshSession(session: StoredSession): Promise<StoredSession>;
@@ -0,0 +1,32 @@
1
+ import type { OAuthTokenSet } from "./oauth-api.js";
2
+ import type { StoredSession } from "./session.js";
3
+ export declare function createStoredSession(input: {
4
+ baseUrl: string;
5
+ createdAt?: string;
6
+ tokenSet: OAuthTokenSet;
7
+ }): Promise<{
8
+ version: 2;
9
+ accessToken: string;
10
+ accessTokenExpiresAt: string;
11
+ baseUrl: string;
12
+ createdAt: string;
13
+ refreshToken: string | null;
14
+ scopes: string[];
15
+ tokenType: string;
16
+ updatedAt: string;
17
+ user: {
18
+ email: string | null;
19
+ emailVerified: boolean | null;
20
+ id: string;
21
+ image: string | null;
22
+ name: string | null;
23
+ };
24
+ organization: {
25
+ id: string | null;
26
+ memberRole: string | null;
27
+ name: string | null;
28
+ slug: string | null;
29
+ };
30
+ }>;
31
+ export declare function hydrateStoredSessionOrganization(session: StoredSession): Promise<StoredSession>;
32
+ export declare function ensureFreshSession(session: StoredSession): Promise<StoredSession>;
@@ -0,0 +1,103 @@
1
+ import { fetchOAuthUserInfo, refreshOAuthTokens } from "./oauth-api.js";
2
+ import { writeStoredSession } from "./session.js";
3
+ const REFRESH_BUFFER_MS = 60_000;
4
+ export async function createStoredSession(input) {
5
+ const now = new Date();
6
+ const [userInfo, resolvedOrganization] = await Promise.all([
7
+ fetchOAuthUserInfo({
8
+ accessToken: input.tokenSet.accessToken,
9
+ baseUrl: input.baseUrl,
10
+ }),
11
+ fetchResolvedSessionOrganization({
12
+ accessToken: input.tokenSet.accessToken,
13
+ baseUrl: input.baseUrl,
14
+ }).catch(() => null),
15
+ ]);
16
+ return {
17
+ version: 2,
18
+ accessToken: input.tokenSet.accessToken,
19
+ accessTokenExpiresAt: new Date(now.getTime() + input.tokenSet.expiresIn * 1000).toISOString(),
20
+ baseUrl: input.baseUrl,
21
+ createdAt: input.createdAt ?? now.toISOString(),
22
+ refreshToken: input.tokenSet.refreshToken,
23
+ scopes: input.tokenSet.scope,
24
+ tokenType: input.tokenSet.tokenType,
25
+ updatedAt: now.toISOString(),
26
+ user: {
27
+ email: userInfo.email ?? null,
28
+ emailVerified: userInfo.email_verified ?? null,
29
+ id: userInfo.sub,
30
+ image: userInfo.picture ?? null,
31
+ name: userInfo.name ?? null,
32
+ },
33
+ organization: {
34
+ id: resolvedOrganization?.id ?? userInfo.organization_id ?? null,
35
+ memberRole: resolvedOrganization?.memberRole ?? userInfo.member_role ?? null,
36
+ name: resolvedOrganization?.name ?? userInfo.organization_name ?? null,
37
+ slug: resolvedOrganization?.slug ?? userInfo.organization_slug ?? null,
38
+ },
39
+ };
40
+ }
41
+ export async function hydrateStoredSessionOrganization(session) {
42
+ const resolvedOrganization = await fetchResolvedSessionOrganization({
43
+ accessToken: session.accessToken,
44
+ baseUrl: session.baseUrl,
45
+ }).catch(() => null);
46
+ if (!resolvedOrganization || organizationsEqual(session.organization, resolvedOrganization)) {
47
+ return session;
48
+ }
49
+ const nextSession = {
50
+ ...session,
51
+ organization: resolvedOrganization,
52
+ updatedAt: new Date().toISOString(),
53
+ };
54
+ await writeStoredSession(nextSession);
55
+ return nextSession;
56
+ }
57
+ export async function ensureFreshSession(session) {
58
+ if (!shouldRefresh(session)) {
59
+ return session;
60
+ }
61
+ if (!session.refreshToken) {
62
+ throw new Error('The local session has expired and cannot be refreshed. Run `parix auth login` again.');
63
+ }
64
+ const refreshedTokens = await refreshOAuthTokens({
65
+ baseUrl: session.baseUrl,
66
+ refreshToken: session.refreshToken,
67
+ });
68
+ const updatedSession = await createStoredSession({
69
+ baseUrl: session.baseUrl,
70
+ createdAt: session.createdAt,
71
+ tokenSet: refreshedTokens,
72
+ });
73
+ await writeStoredSession(updatedSession);
74
+ return updatedSession;
75
+ }
76
+ function shouldRefresh(session) {
77
+ const expiresAt = Date.parse(session.accessTokenExpiresAt);
78
+ return Number.isNaN(expiresAt) || expiresAt <= Date.now() + REFRESH_BUFFER_MS;
79
+ }
80
+ async function fetchResolvedSessionOrganization(input) {
81
+ const response = await fetch(new URL('/api/v1/session', input.baseUrl), {
82
+ headers: {
83
+ authorization: `Bearer ${input.accessToken}`,
84
+ },
85
+ });
86
+ if (!response.ok) {
87
+ throw new Error('Unable to resolve the authenticated organization.');
88
+ }
89
+ const payload = (await response.json());
90
+ const organization = payload.auth?.organization;
91
+ if (!organization) {
92
+ return null;
93
+ }
94
+ return {
95
+ id: typeof organization.id === 'string' ? organization.id : null,
96
+ memberRole: typeof organization.memberRole === 'string' ? organization.memberRole : null,
97
+ name: typeof organization.name === 'string' ? organization.name : null,
98
+ slug: typeof organization.slug === 'string' ? organization.slug : null,
99
+ };
100
+ }
101
+ function organizationsEqual(left, right) {
102
+ return (left.id === right.id && left.memberRole === right.memberRole && left.name === right.name && left.slug === right.slug);
103
+ }
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PARIX_DEFAULT_SCOPES = exports.CLI_OAUTH_CONSENT_GRANTED_PATH = exports.PARIX_LOCAL_CALLBACK_PATH = exports.PARIX_CLIENT_ID = void 0;
4
+ exports.createPkcePair = createPkcePair;
5
+ exports.buildAuthorizeUrl = buildAuthorizeUrl;
6
+ exports.buildCliState = buildCliState;
7
+ exports.buildOAuthCallbackUrl = buildOAuthCallbackUrl;
8
+ exports.buildOAuthTokenUrl = buildOAuthTokenUrl;
9
+ exports.buildOAuthUserInfoUrl = buildOAuthUserInfoUrl;
10
+ const node_buffer_1 = require("node:buffer");
11
+ const node_crypto_1 = require("node:crypto");
12
+ const config_1 = require("./config.cjs");
13
+ exports.PARIX_CLIENT_ID = 'parix';
14
+ exports.PARIX_LOCAL_CALLBACK_PATH = '/callback';
15
+ exports.CLI_OAUTH_CONSENT_GRANTED_PATH = '/cli-oauth-consent-granted';
16
+ exports.PARIX_DEFAULT_SCOPES = [
17
+ 'openid',
18
+ 'profile',
19
+ 'email',
20
+ 'offline_access',
21
+ 'org:read',
22
+ 'db:read',
23
+ 'db:write',
24
+ 'billing:read',
25
+ ];
26
+ function createPkcePair() {
27
+ const codeVerifier = (0, node_crypto_1.randomBytes)(32).toString('base64url');
28
+ const codeChallenge = (0, node_crypto_1.createHash)('sha256').update(codeVerifier).digest('base64url');
29
+ return {
30
+ codeChallenge,
31
+ codeVerifier,
32
+ };
33
+ }
34
+ function buildAuthorizeUrl(input) {
35
+ const url = new URL('/api/oauth/cli/authorize', (0, config_1.trimTrailingSlash)(input.baseUrl));
36
+ url.searchParams.set('state', input.state);
37
+ url.searchParams.set('code_challenge', input.codeChallenge);
38
+ url.searchParams.set('code_challenge_method', 'S256');
39
+ if (input.prompt) {
40
+ url.searchParams.set('prompt', input.prompt);
41
+ }
42
+ return url.toString();
43
+ }
44
+ function buildCliState(redirectUri) {
45
+ return node_buffer_1.Buffer.from(JSON.stringify({
46
+ nonce: (0, node_crypto_1.randomBytes)(16).toString('hex'),
47
+ redirectUri,
48
+ }), 'utf8').toString('base64url');
49
+ }
50
+ function buildOAuthCallbackUrl(baseUrl) {
51
+ return new URL('/api/oauth/cli/callback', (0, config_1.trimTrailingSlash)(baseUrl)).toString();
52
+ }
53
+ function buildOAuthTokenUrl(baseUrl) {
54
+ return new URL('/api/auth/oauth2/token', (0, config_1.trimTrailingSlash)(baseUrl)).toString();
55
+ }
56
+ function buildOAuthUserInfoUrl(baseUrl) {
57
+ return new URL('/api/auth/oauth2/userinfo', (0, config_1.trimTrailingSlash)(baseUrl)).toString();
58
+ }
@@ -0,0 +1,19 @@
1
+ export declare const PARIX_CLIENT_ID = "parix";
2
+ export declare const PARIX_LOCAL_CALLBACK_PATH = "/callback";
3
+ export declare const CLI_OAUTH_CONSENT_GRANTED_PATH = "/cli-oauth-consent-granted";
4
+ export declare const PARIX_DEFAULT_SCOPES: readonly ["openid", "profile", "email", "offline_access", "org:read", "db:read", "db:write", "billing:read"];
5
+ export interface PkcePair {
6
+ codeChallenge: string;
7
+ codeVerifier: string;
8
+ }
9
+ export declare function createPkcePair(): PkcePair;
10
+ export declare function buildAuthorizeUrl(input: {
11
+ baseUrl: string;
12
+ codeChallenge: string;
13
+ prompt?: string;
14
+ state: string;
15
+ }): string;
16
+ export declare function buildCliState(redirectUri: string): string;
17
+ export declare function buildOAuthCallbackUrl(baseUrl: string): string;
18
+ export declare function buildOAuthTokenUrl(baseUrl: string): string;
19
+ export declare function buildOAuthUserInfoUrl(baseUrl: string): string;
@@ -0,0 +1,19 @@
1
+ export declare const PARIX_CLIENT_ID = "parix";
2
+ export declare const PARIX_LOCAL_CALLBACK_PATH = "/callback";
3
+ export declare const CLI_OAUTH_CONSENT_GRANTED_PATH = "/cli-oauth-consent-granted";
4
+ export declare const PARIX_DEFAULT_SCOPES: readonly ["openid", "profile", "email", "offline_access", "org:read", "db:read", "db:write", "billing:read"];
5
+ export interface PkcePair {
6
+ codeChallenge: string;
7
+ codeVerifier: string;
8
+ }
9
+ export declare function createPkcePair(): PkcePair;
10
+ export declare function buildAuthorizeUrl(input: {
11
+ baseUrl: string;
12
+ codeChallenge: string;
13
+ prompt?: string;
14
+ state: string;
15
+ }): string;
16
+ export declare function buildCliState(redirectUri: string): string;
17
+ export declare function buildOAuthCallbackUrl(baseUrl: string): string;
18
+ export declare function buildOAuthTokenUrl(baseUrl: string): string;
19
+ export declare function buildOAuthUserInfoUrl(baseUrl: string): string;
@@ -0,0 +1,49 @@
1
+ import { Buffer } from 'node:buffer';
2
+ import { createHash, randomBytes } from 'node:crypto';
3
+ import { trimTrailingSlash } from "./config.js";
4
+ export const PARIX_CLIENT_ID = 'parix';
5
+ export const PARIX_LOCAL_CALLBACK_PATH = '/callback';
6
+ export const CLI_OAUTH_CONSENT_GRANTED_PATH = '/cli-oauth-consent-granted';
7
+ export const PARIX_DEFAULT_SCOPES = [
8
+ 'openid',
9
+ 'profile',
10
+ 'email',
11
+ 'offline_access',
12
+ 'org:read',
13
+ 'db:read',
14
+ 'db:write',
15
+ 'billing:read',
16
+ ];
17
+ export function createPkcePair() {
18
+ const codeVerifier = randomBytes(32).toString('base64url');
19
+ const codeChallenge = createHash('sha256').update(codeVerifier).digest('base64url');
20
+ return {
21
+ codeChallenge,
22
+ codeVerifier,
23
+ };
24
+ }
25
+ export function buildAuthorizeUrl(input) {
26
+ const url = new URL('/api/oauth/cli/authorize', trimTrailingSlash(input.baseUrl));
27
+ url.searchParams.set('state', input.state);
28
+ url.searchParams.set('code_challenge', input.codeChallenge);
29
+ url.searchParams.set('code_challenge_method', 'S256');
30
+ if (input.prompt) {
31
+ url.searchParams.set('prompt', input.prompt);
32
+ }
33
+ return url.toString();
34
+ }
35
+ export function buildCliState(redirectUri) {
36
+ return Buffer.from(JSON.stringify({
37
+ nonce: randomBytes(16).toString('hex'),
38
+ redirectUri,
39
+ }), 'utf8').toString('base64url');
40
+ }
41
+ export function buildOAuthCallbackUrl(baseUrl) {
42
+ return new URL('/api/oauth/cli/callback', trimTrailingSlash(baseUrl)).toString();
43
+ }
44
+ export function buildOAuthTokenUrl(baseUrl) {
45
+ return new URL('/api/auth/oauth2/token', trimTrailingSlash(baseUrl)).toString();
46
+ }
47
+ export function buildOAuthUserInfoUrl(baseUrl) {
48
+ return new URL('/api/auth/oauth2/userinfo', trimTrailingSlash(baseUrl)).toString();
49
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.openUrlInBrowser = openUrlInBrowser;
7
+ const node_child_process_1 = require("node:child_process");
8
+ const node_process_1 = __importDefault(require("node:process"));
9
+ async function openUrlInBrowser(url) {
10
+ const command = getOpenCommand(url);
11
+ if (!command) {
12
+ return false;
13
+ }
14
+ try {
15
+ const child = (0, node_child_process_1.spawn)(command.bin, command.args, {
16
+ detached: true,
17
+ stdio: 'ignore',
18
+ windowsHide: true,
19
+ });
20
+ child.unref();
21
+ return true;
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ }
27
+ function getOpenCommand(url) {
28
+ if (node_process_1.default.platform === 'darwin') {
29
+ return { bin: 'open', args: [url] };
30
+ }
31
+ if (node_process_1.default.platform === 'win32') {
32
+ return { bin: 'cmd', args: ['/c', 'start', '', url] };
33
+ }
34
+ if (node_process_1.default.platform === 'linux') {
35
+ return { bin: 'xdg-open', args: [url] };
36
+ }
37
+ return null;
38
+ }
@@ -0,0 +1 @@
1
+ export declare function openUrlInBrowser(url: string): Promise<boolean>;
@@ -0,0 +1 @@
1
+ export declare function openUrlInBrowser(url: string): Promise<boolean>;
@@ -0,0 +1,32 @@
1
+ import { spawn } from 'node:child_process';
2
+ import process from 'node:process';
3
+ export async function openUrlInBrowser(url) {
4
+ const command = getOpenCommand(url);
5
+ if (!command) {
6
+ return false;
7
+ }
8
+ try {
9
+ const child = spawn(command.bin, command.args, {
10
+ detached: true,
11
+ stdio: 'ignore',
12
+ windowsHide: true,
13
+ });
14
+ child.unref();
15
+ return true;
16
+ }
17
+ catch {
18
+ return false;
19
+ }
20
+ }
21
+ function getOpenCommand(url) {
22
+ if (process.platform === 'darwin') {
23
+ return { bin: 'open', args: [url] };
24
+ }
25
+ if (process.platform === 'win32') {
26
+ return { bin: 'cmd', args: ['/c', 'start', '', url] };
27
+ }
28
+ if (process.platform === 'linux') {
29
+ return { bin: 'xdg-open', args: [url] };
30
+ }
31
+ return null;
32
+ }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.printJson = printJson;
4
+ exports.printKeyValues = printKeyValues;
5
+ exports.printTable = printTable;
6
+ const prompts_1 = require("@clack/prompts");
7
+ function printJson(value) {
8
+ prompts_1.log.message(JSON.stringify(value, null, 2));
9
+ }
10
+ function printKeyValues(entries) {
11
+ const keyWidth = Math.max(...entries.map(([key]) => key.length), 0);
12
+ prompts_1.log.message(entries.map(([key, value]) => `${key.padEnd(keyWidth)} ${value}`).join('\n'));
13
+ }
14
+ function printTable(input) {
15
+ const widths = input.columns.map((column, index) => Math.max(column.length, ...input.rows.map((row) => row[index]?.length ?? 0)));
16
+ const header = input.columns.map((column, index) => column.padEnd(widths[index] ?? 0)).join(' ');
17
+ const divider = widths.map((width) => '-'.repeat(width)).join(' ');
18
+ const rows = input.rows.map((row) => row.map((value, index) => value.padEnd(widths[index] ?? 0)).join(' '));
19
+ prompts_1.log.message([header, divider, ...rows].join('\n'));
20
+ }
@@ -0,0 +1,6 @@
1
+ export declare function printJson(value: unknown): void;
2
+ export declare function printKeyValues(entries: Array<[string, string]>): void;
3
+ export declare function printTable(input: {
4
+ columns: string[];
5
+ rows: string[][];
6
+ }): void;
@@ -0,0 +1,6 @@
1
+ export declare function printJson(value: unknown): void;
2
+ export declare function printKeyValues(entries: Array<[string, string]>): void;
3
+ export declare function printTable(input: {
4
+ columns: string[];
5
+ rows: string[][];
6
+ }): void;
@@ -0,0 +1,15 @@
1
+ import { log } from '@clack/prompts';
2
+ export function printJson(value) {
3
+ log.message(JSON.stringify(value, null, 2));
4
+ }
5
+ export function printKeyValues(entries) {
6
+ const keyWidth = Math.max(...entries.map(([key]) => key.length), 0);
7
+ log.message(entries.map(([key, value]) => `${key.padEnd(keyWidth)} ${value}`).join('\n'));
8
+ }
9
+ export function printTable(input) {
10
+ const widths = input.columns.map((column, index) => Math.max(column.length, ...input.rows.map((row) => row[index]?.length ?? 0)));
11
+ const header = input.columns.map((column, index) => column.padEnd(widths[index] ?? 0)).join(' ');
12
+ const divider = widths.map((width) => '-'.repeat(width)).join(' ');
13
+ const rows = input.rows.map((row) => row.map((value, index) => value.padEnd(widths[index] ?? 0)).join(' '));
14
+ log.message([header, divider, ...rows].join('\n'));
15
+ }
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createApiClient = createApiClient;
4
+ exports.requestApiJson = requestApiJson;
5
+ const api_client_1 = require("./api-client.cjs");
6
+ const config_1 = require("./config.cjs");
7
+ const session_1 = require("./session.cjs");
8
+ async function createApiClient(baseUrlOverride) {
9
+ const storedSession = await (0, session_1.readStoredSession)();
10
+ if (!storedSession) {
11
+ throw new Error('No local session found. Run `parix auth login` first.');
12
+ }
13
+ const baseUrl = (0, config_1.resolveBaseUrl)(baseUrlOverride, storedSession);
14
+ return {
15
+ baseUrl,
16
+ client: new api_client_1.ApiClient({
17
+ session: {
18
+ ...storedSession,
19
+ baseUrl,
20
+ },
21
+ }),
22
+ };
23
+ }
24
+ async function requestApiJson(input) {
25
+ const { client } = await createApiClient(input.baseUrl);
26
+ const response = await client.request(input.path, {
27
+ body: input.body,
28
+ headers: input.headers,
29
+ method: input.method,
30
+ });
31
+ const raw = await response.text();
32
+ const payload = raw.length > 0 ? safeParseJson(raw) : null;
33
+ if (!response.ok) {
34
+ const message = getApiErrorMessage(payload) ?? (raw.trim() || `${response.status} ${response.statusText}`);
35
+ throw new Error(message);
36
+ }
37
+ return payload;
38
+ }
39
+ function safeParseJson(value) {
40
+ try {
41
+ return JSON.parse(value);
42
+ }
43
+ catch {
44
+ return value;
45
+ }
46
+ }
47
+ function getApiErrorMessage(payload) {
48
+ if (!payload || typeof payload !== 'object') {
49
+ return null;
50
+ }
51
+ const row = payload;
52
+ if (typeof row.message === 'string' && row.message.trim().length > 0) {
53
+ return row.message;
54
+ }
55
+ if (typeof row.error === 'string' && row.error.trim().length > 0) {
56
+ return row.error;
57
+ }
58
+ return null;
59
+ }
@@ -0,0 +1,12 @@
1
+ import { ApiClient } from "./api-client.cjs";
2
+ export declare function createApiClient(baseUrlOverride?: string): Promise<{
3
+ baseUrl: string;
4
+ client: ApiClient;
5
+ }>;
6
+ export declare function requestApiJson<T>(input: {
7
+ baseUrl?: string;
8
+ body?: RequestInit['body'];
9
+ headers?: RequestInit['headers'];
10
+ method?: string;
11
+ path: string;
12
+ }): Promise<T>;
@@ -0,0 +1,12 @@
1
+ import { ApiClient } from "./api-client.js";
2
+ export declare function createApiClient(baseUrlOverride?: string): Promise<{
3
+ baseUrl: string;
4
+ client: ApiClient;
5
+ }>;
6
+ export declare function requestApiJson<T>(input: {
7
+ baseUrl?: string;
8
+ body?: RequestInit['body'];
9
+ headers?: RequestInit['headers'];
10
+ method?: string;
11
+ path: string;
12
+ }): Promise<T>;
@@ -0,0 +1,55 @@
1
+ import { ApiClient } from "./api-client.js";
2
+ import { resolveBaseUrl } from "./config.js";
3
+ import { readStoredSession } from "./session.js";
4
+ export async function createApiClient(baseUrlOverride) {
5
+ const storedSession = await readStoredSession();
6
+ if (!storedSession) {
7
+ throw new Error('No local session found. Run `parix auth login` first.');
8
+ }
9
+ const baseUrl = resolveBaseUrl(baseUrlOverride, storedSession);
10
+ return {
11
+ baseUrl,
12
+ client: new ApiClient({
13
+ session: {
14
+ ...storedSession,
15
+ baseUrl,
16
+ },
17
+ }),
18
+ };
19
+ }
20
+ export async function requestApiJson(input) {
21
+ const { client } = await createApiClient(input.baseUrl);
22
+ const response = await client.request(input.path, {
23
+ body: input.body,
24
+ headers: input.headers,
25
+ method: input.method,
26
+ });
27
+ const raw = await response.text();
28
+ const payload = raw.length > 0 ? safeParseJson(raw) : null;
29
+ if (!response.ok) {
30
+ const message = getApiErrorMessage(payload) ?? (raw.trim() || `${response.status} ${response.statusText}`);
31
+ throw new Error(message);
32
+ }
33
+ return payload;
34
+ }
35
+ function safeParseJson(value) {
36
+ try {
37
+ return JSON.parse(value);
38
+ }
39
+ catch {
40
+ return value;
41
+ }
42
+ }
43
+ function getApiErrorMessage(payload) {
44
+ if (!payload || typeof payload !== 'object') {
45
+ return null;
46
+ }
47
+ const row = payload;
48
+ if (typeof row.message === 'string' && row.message.trim().length > 0) {
49
+ return row.message;
50
+ }
51
+ if (typeof row.error === 'string' && row.error.trim().length > 0) {
52
+ return row.error;
53
+ }
54
+ return null;
55
+ }
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SESSION_FILE_PATH = void 0;
4
+ exports.readStoredSession = readStoredSession;
5
+ exports.writeStoredSession = writeStoredSession;
6
+ exports.clearStoredSession = clearStoredSession;
7
+ const promises_1 = require("node:fs/promises");
8
+ const node_os_1 = require("node:os");
9
+ const node_path_1 = require("node:path");
10
+ exports.SESSION_FILE_PATH = (0, node_path_1.join)((0, node_os_1.homedir)(), '.config', 'parix', 'session.json');
11
+ async function readStoredSession() {
12
+ try {
13
+ const raw = await (0, promises_1.readFile)(exports.SESSION_FILE_PATH, 'utf8');
14
+ return parseStoredSession(JSON.parse(raw));
15
+ }
16
+ catch (error) {
17
+ if (isMissingFileError(error)) {
18
+ return null;
19
+ }
20
+ if (error instanceof SyntaxError) {
21
+ throw new TypeError(`Failed to parse ${exports.SESSION_FILE_PATH}`);
22
+ }
23
+ throw error;
24
+ }
25
+ }
26
+ async function writeStoredSession(session) {
27
+ await (0, promises_1.mkdir)((0, node_path_1.dirname)(exports.SESSION_FILE_PATH), { recursive: true });
28
+ await (0, promises_1.writeFile)(exports.SESSION_FILE_PATH, `${JSON.stringify(session, null, 2)}\n`, 'utf8');
29
+ }
30
+ async function clearStoredSession() {
31
+ await (0, promises_1.rm)(exports.SESSION_FILE_PATH, { force: true });
32
+ }
33
+ function parseStoredSession(value) {
34
+ if (!value || typeof value !== 'object') {
35
+ throw new Error(`Invalid session data in ${exports.SESSION_FILE_PATH}`);
36
+ }
37
+ const row = value;
38
+ const user = row.user;
39
+ const organization = row.organization;
40
+ if (row.version !== 2 ||
41
+ typeof row.baseUrl !== 'string' ||
42
+ typeof row.accessToken !== 'string' ||
43
+ typeof row.accessTokenExpiresAt !== 'string' ||
44
+ typeof row.createdAt !== 'string' ||
45
+ !Array.isArray(row.scopes) ||
46
+ typeof row.tokenType !== 'string' ||
47
+ typeof row.updatedAt !== 'string' ||
48
+ !user ||
49
+ typeof user.id !== 'string' ||
50
+ !organization) {
51
+ throw new Error(`Invalid session data in ${exports.SESSION_FILE_PATH}`);
52
+ }
53
+ return {
54
+ version: 2,
55
+ accessToken: row.accessToken,
56
+ accessTokenExpiresAt: row.accessTokenExpiresAt,
57
+ baseUrl: row.baseUrl,
58
+ createdAt: row.createdAt,
59
+ refreshToken: typeof row.refreshToken === 'string' ? row.refreshToken : null,
60
+ scopes: row.scopes.filter((scope) => typeof scope === 'string'),
61
+ tokenType: row.tokenType,
62
+ updatedAt: row.updatedAt,
63
+ user: {
64
+ id: user.id,
65
+ email: typeof user.email === 'string' ? user.email : null,
66
+ emailVerified: typeof user.emailVerified === 'boolean' ? user.emailVerified : null,
67
+ name: typeof user.name === 'string' ? user.name : null,
68
+ image: typeof user.image === 'string' ? user.image : null,
69
+ },
70
+ organization: {
71
+ id: typeof organization.id === 'string' ? organization.id : null,
72
+ memberRole: typeof organization.memberRole === 'string' ? organization.memberRole : null,
73
+ name: typeof organization.name === 'string' ? organization.name : null,
74
+ slug: typeof organization.slug === 'string' ? organization.slug : null,
75
+ },
76
+ };
77
+ }
78
+ function isMissingFileError(error) {
79
+ return error instanceof Error && 'code' in error && error.code === 'ENOENT';
80
+ }