@alpic-ai/sdk 0.0.0 → 1.142.2

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 (47) hide show
  1. package/dist/api/auth-scope.d.ts +2 -0
  2. package/dist/api/auth-scope.js +7 -0
  3. package/dist/api/auth-scope.js.map +1 -0
  4. package/dist/api/client.d.ts +11 -0
  5. package/dist/api/client.js +25 -0
  6. package/dist/api/client.js.map +1 -0
  7. package/dist/auth/constants.d.ts +2 -0
  8. package/dist/auth/constants.js +3 -0
  9. package/dist/auth/constants.js.map +1 -0
  10. package/dist/auth/index.d.ts +21 -0
  11. package/dist/auth/index.js +34 -0
  12. package/dist/auth/index.js.map +1 -0
  13. package/dist/auth/login.d.ts +7 -0
  14. package/dist/auth/login.js +21 -0
  15. package/dist/auth/login.js.map +1 -0
  16. package/dist/auth/oauth-client.d.ts +28 -0
  17. package/dist/auth/oauth-client.js +116 -0
  18. package/dist/auth/oauth-client.js.map +1 -0
  19. package/dist/auth/server/assets/alpic-mountain.png +0 -0
  20. package/dist/auth/server/assets/authorize.html +234 -0
  21. package/dist/auth/server/assets/callback.html +88 -0
  22. package/dist/auth/server/index.d.ts +14 -0
  23. package/dist/auth/server/index.js +128 -0
  24. package/dist/auth/server/index.js.map +1 -0
  25. package/dist/auth/store.d.ts +30 -0
  26. package/dist/auth/store.js +76 -0
  27. package/dist/auth/store.js.map +1 -0
  28. package/dist/client.d.ts +9 -0
  29. package/dist/client.js +18 -0
  30. package/dist/client.js.map +1 -0
  31. package/dist/env.d.ts +4 -0
  32. package/dist/env.js +10 -0
  33. package/dist/env.js.map +1 -0
  34. package/dist/index.d.ts +7 -0
  35. package/dist/index.js +5 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/lib/emitter.d.ts +7 -0
  38. package/dist/lib/emitter.js +29 -0
  39. package/dist/lib/emitter.js.map +1 -0
  40. package/dist/tunnel/tunnel.d.ts +43 -0
  41. package/dist/tunnel/tunnel.js +127 -0
  42. package/dist/tunnel/tunnel.js.map +1 -0
  43. package/dist/vendor/pipenet.d.ts +1 -0
  44. package/dist/vendor/pipenet.js +16877 -0
  45. package/dist/vendor/pipenet.js.LEGAL.txt +17 -0
  46. package/dist/vendor/pipenet.js.map +7 -0
  47. package/package.json +55 -4
@@ -0,0 +1,2 @@
1
+ import { type AuthScope } from "@alpic-ai/api";
2
+ export declare function getAuthScopeForPath(path: readonly string[]): AuthScope;
@@ -0,0 +1,7 @@
1
+ import { AUTH_SCOPE_ANY, contract } from "@alpic-ai/api";
2
+ import { getContractRouter } from "@orpc/contract";
3
+ export function getAuthScopeForPath(path) {
4
+ const procedure = getContractRouter(contract, path);
5
+ return procedure?.["~orpc"]?.meta?.authScope ?? AUTH_SCOPE_ANY;
6
+ }
7
+ //# sourceMappingURL=auth-scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-scope.js","sourceRoot":"","sources":["../../src/api/auth-scope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAkB,QAAQ,EAAsB,MAAM,eAAe,CAAC;AAC7F,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAQnD,MAAM,UAAU,mBAAmB,CAAC,IAAuB;IACzD,MAAM,SAAS,GAAG,iBAAiB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACpD,OAAQ,SAA2C,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,SAAS,IAAI,cAAc,CAAC;AACpG,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { contract } from "@alpic-ai/api";
2
+ import type { ContractRouterClient } from "@orpc/contract";
3
+ export type AlpicApi = ContractRouterClient<typeof contract>;
4
+ export type ApiAuth = {
5
+ getToken: () => Promise<string | undefined>;
6
+ getUserAccessToken: () => Promise<string | undefined>;
7
+ };
8
+ export declare function createApiClient(opts: {
9
+ apiBaseUrl: string;
10
+ auth: ApiAuth;
11
+ }): AlpicApi;
@@ -0,0 +1,25 @@
1
+ import { AUTH_SCOPE_USER, contract } from "@alpic-ai/api";
2
+ import { createORPCClient } from "@orpc/client";
3
+ import { ResponseValidationPlugin } from "@orpc/contract/plugins";
4
+ import { OpenAPILink } from "@orpc/openapi-client/fetch";
5
+ import { getAuthScopeForPath } from "./auth-scope.js";
6
+ export function createApiClient(opts) {
7
+ const link = new OpenAPILink(contract, {
8
+ url: opts.apiBaseUrl,
9
+ headers: async (options, path) => {
10
+ const headers = options.context?.request?.headers ?? {};
11
+ if (headers.Authorization) {
12
+ return headers;
13
+ }
14
+ const scope = getAuthScopeForPath(path);
15
+ const token = scope === AUTH_SCOPE_USER ? await opts.auth.getUserAccessToken() : await opts.auth.getToken();
16
+ if (token) {
17
+ headers.Authorization = `Bearer ${token}`;
18
+ }
19
+ return headers;
20
+ },
21
+ plugins: [new ResponseValidationPlugin(contract)],
22
+ });
23
+ return createORPCClient(link);
24
+ }
25
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAStD,MAAM,UAAU,eAAe,CAAC,IAA2C;IACzE,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,QAAQ,EAAE;QACrC,GAAG,EAAE,IAAI,CAAC,UAAU;QACpB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;YAC/B,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,IAAI,EAAE,CAAC;YACxD,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;gBAC1B,OAAO,OAAO,CAAC;YACjB,CAAC;YAED,MAAM,KAAK,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;YACxC,MAAM,KAAK,GAAG,KAAK,KAAK,eAAe,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YAE5G,IAAI,KAAK,EAAE,CAAC;gBACV,OAAO,CAAC,aAAa,GAAG,UAAU,KAAK,EAAE,CAAC;YAC5C,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,OAAO,EAAE,CAAC,IAAI,wBAAwB,CAAC,QAAQ,CAAC,CAAC;KAClD,CAAC,CAAC;IACH,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC;AAChC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const LOOPBACK_HOST = "127.0.0.1";
2
+ export declare const LOOPBACK_PORT = 38472;
@@ -0,0 +1,3 @@
1
+ export const LOOPBACK_HOST = "127.0.0.1";
2
+ export const LOOPBACK_PORT = 38472;
3
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/auth/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,aAAa,GAAG,WAAW,CAAC;AACzC,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { type LoginOptions } from "./login.js";
2
+ import { type Credentials } from "./store.js";
3
+ export type { Credentials, LoginOptions };
4
+ export type AuthEvents = {
5
+ authenticated: {
6
+ sub: string;
7
+ };
8
+ };
9
+ export declare class AlpicAuth {
10
+ private readonly events;
11
+ on<K extends keyof AuthEvents>(event: K, listener: (payload: AuthEvents[K]) => void): void;
12
+ off<K extends keyof AuthEvents>(event: K, listener: (payload: AuthEvents[K]) => void): void;
13
+ getValidAccessToken(): Promise<Credentials | null>;
14
+ isAuthenticated(): Promise<boolean>;
15
+ getToken(): Promise<string | undefined>;
16
+ getUserAccessToken(): Promise<string | undefined>;
17
+ login(opts?: LoginOptions): Promise<{
18
+ sub: string;
19
+ }>;
20
+ logout(): Promise<void>;
21
+ }
@@ -0,0 +1,34 @@
1
+ import { TypedEventEmitter } from "../lib/emitter.js";
2
+ import { oauthLogin } from "./login.js";
3
+ import { getOAuthClient } from "./oauth-client.js";
4
+ import { globalStore } from "./store.js";
5
+ export class AlpicAuth {
6
+ events = new TypedEventEmitter();
7
+ on(event, listener) {
8
+ this.events.on(event, listener);
9
+ }
10
+ off(event, listener) {
11
+ this.events.off(event, listener);
12
+ }
13
+ async getValidAccessToken() {
14
+ return getOAuthClient().getValidAccessToken();
15
+ }
16
+ async isAuthenticated() {
17
+ return (await this.getToken()) !== undefined;
18
+ }
19
+ async getToken() {
20
+ return process.env.ALPIC_API_KEY ?? (await getOAuthClient().getValidAccessToken())?.access_token;
21
+ }
22
+ async getUserAccessToken() {
23
+ return (await getOAuthClient().getValidAccessToken())?.access_token;
24
+ }
25
+ async login(opts) {
26
+ const result = await oauthLogin(opts);
27
+ this.events.emit("authenticated", { sub: result.sub });
28
+ return result;
29
+ }
30
+ async logout() {
31
+ globalStore.clearCredentials();
32
+ }
33
+ }
34
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAqB,UAAU,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAoB,WAAW,EAAE,MAAM,YAAY,CAAC;AAQ3D,MAAM,OAAO,SAAS;IACH,MAAM,GAAG,IAAI,iBAAiB,EAAc,CAAC;IAE9D,EAAE,CAA6B,KAAQ,EAAE,QAA0C;QACjF,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAClC,CAAC;IAED,GAAG,CAA6B,KAAQ,EAAE,QAA0C;QAClF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,mBAAmB;QACvB,OAAO,cAAc,EAAE,CAAC,mBAAmB,EAAE,CAAC;IAChD,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,OAAO,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC,KAAK,SAAS,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,OAAO,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,CAAC,MAAM,cAAc,EAAE,CAAC,mBAAmB,EAAE,CAAC,EAAE,YAAY,CAAC;IACnG,CAAC;IAED,KAAK,CAAC,kBAAkB;QACtB,OAAO,CAAC,MAAM,cAAc,EAAE,CAAC,mBAAmB,EAAE,CAAC,EAAE,YAAY,CAAC;IACtE,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAmB;QAC7B,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC;QACvD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,MAAM;QACV,WAAW,CAAC,gBAAgB,EAAE,CAAC;IACjC,CAAC;CACF"}
@@ -0,0 +1,7 @@
1
+ export type LoginOptions = {
2
+ onLoginUrl?: (url: string) => void;
3
+ openBrowser?: boolean;
4
+ };
5
+ export declare function oauthLogin(opts?: LoginOptions): Promise<{
6
+ sub: string;
7
+ }>;
@@ -0,0 +1,21 @@
1
+ import open from "open";
2
+ import { getOAuthClient } from "./oauth-client.js";
3
+ import { getLoginPageUrl, listenToOAuthCallback } from "./server/index.js";
4
+ import { globalStore } from "./store.js";
5
+ export async function oauthLogin(opts) {
6
+ const { authorizeUrl, state, nonce, codeVerifier } = await getOAuthClient().prepareOAuthConfig();
7
+ const loginUrl = getLoginPageUrl();
8
+ opts?.onLoginUrl?.(loginUrl);
9
+ if (opts?.openBrowser !== false && !process.env.ALPIC_NO_OPEN) {
10
+ await open(loginUrl);
11
+ }
12
+ const storedToken = await listenToOAuthCallback({
13
+ state,
14
+ nonce,
15
+ codeVerifier,
16
+ authorizeUrl: authorizeUrl.toString(),
17
+ });
18
+ globalStore.saveCredentials(storedToken);
19
+ return { sub: storedToken.sub };
20
+ }
21
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/auth/login.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAOzC,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAmB;IAClD,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,MAAM,cAAc,EAAE,CAAC,kBAAkB,EAAE,CAAC;IAEjG,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAC;IACnC,IAAI,EAAE,UAAU,EAAE,CAAC,QAAQ,CAAC,CAAC;IAE7B,IAAI,IAAI,EAAE,WAAW,KAAK,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAC9D,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvB,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,qBAAqB,CAAC;QAC9C,KAAK;QACL,KAAK;QACL,YAAY;QACZ,YAAY,EAAE,YAAY,CAAC,QAAQ,EAAE;KACtC,CAAC,CAAC;IAEH,WAAW,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;IAEzC,OAAO,EAAE,GAAG,EAAE,WAAW,CAAC,GAAG,EAAE,CAAC;AAClC,CAAC"}
@@ -0,0 +1,28 @@
1
+ import * as openid from "openid-client";
2
+ import { type Credentials } from "./store.js";
3
+ export declare class OAuthClient {
4
+ private config;
5
+ initialize: Promise<void>;
6
+ constructor();
7
+ getValidAccessToken(): Promise<Credentials | null>;
8
+ fetchUserInfo(credentials: Credentials): Promise<openid.UserInfoResponse>;
9
+ refreshAccessToken(credentials: Credentials): Promise<Credentials>;
10
+ prepareOAuthConfig(): Promise<{
11
+ authorizeUrl: URL;
12
+ state: string;
13
+ nonce: string;
14
+ codeVerifier: string;
15
+ }>;
16
+ exchangeAuthorizationCode({ url, codeVerifier, state, nonce, }: {
17
+ url: URL;
18
+ codeVerifier: string;
19
+ state: string;
20
+ nonce: string;
21
+ }): Promise<openid.TokenEndpointResponse & openid.TokenEndpointResponseHelpers>;
22
+ getExpiresAt(expiresInSeconds: number | undefined): number;
23
+ private loadConfig;
24
+ private fetchOAuthProtectedResourceConfig;
25
+ private getConfig;
26
+ private isAccessTokenExpired;
27
+ }
28
+ export declare function getOAuthClient(): OAuthClient;
@@ -0,0 +1,116 @@
1
+ import * as openid from "openid-client";
2
+ import { env } from "../env.js";
3
+ import { LOOPBACK_HOST, LOOPBACK_PORT } from "./constants.js";
4
+ import { globalStore } from "./store.js";
5
+ const SCOPES = ["openid", "email", "profile"];
6
+ export class OAuthClient {
7
+ config = null;
8
+ initialize;
9
+ constructor() {
10
+ this.initialize = this.loadConfig();
11
+ }
12
+ async getValidAccessToken() {
13
+ await this.initialize;
14
+ const stored = globalStore.getCredentials();
15
+ if (!stored) {
16
+ return null;
17
+ }
18
+ if (this.isAccessTokenExpired(stored)) {
19
+ try {
20
+ return await this.refreshAccessToken(stored);
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ }
26
+ return stored;
27
+ }
28
+ async fetchUserInfo(credentials) {
29
+ return openid.fetchUserInfo(await this.getConfig(), credentials.access_token, credentials.sub);
30
+ }
31
+ async refreshAccessToken(credentials) {
32
+ if (!credentials.refresh_token) {
33
+ throw new Error("No refresh token available");
34
+ }
35
+ const response = await openid.refreshTokenGrant(await this.getConfig(), credentials.refresh_token);
36
+ const refreshed = {
37
+ access_token: response.access_token,
38
+ refresh_token: response.refresh_token ?? credentials.refresh_token,
39
+ expires_at: this.getExpiresAt(response.expires_in),
40
+ sub: credentials.sub,
41
+ };
42
+ globalStore.saveCredentials(refreshed);
43
+ return refreshed;
44
+ }
45
+ async prepareOAuthConfig() {
46
+ await this.initialize;
47
+ if (!this.config) {
48
+ throw new Error("Config not loaded");
49
+ }
50
+ const codeVerifier = openid.randomPKCECodeVerifier();
51
+ const codeChallenge = await openid.calculatePKCECodeChallenge(codeVerifier);
52
+ const state = openid.randomState();
53
+ const nonce = openid.randomNonce();
54
+ const callbackUrl = new URL(`http://${LOOPBACK_HOST}:${LOOPBACK_PORT}/callback`);
55
+ const authorizeUrl = openid.buildAuthorizationUrl(this.config, {
56
+ redirect_uri: callbackUrl.toString(),
57
+ scope: SCOPES.join(" "),
58
+ code_challenge: codeChallenge,
59
+ code_challenge_method: "S256",
60
+ state,
61
+ nonce,
62
+ });
63
+ return { authorizeUrl, state, nonce, codeVerifier };
64
+ }
65
+ async exchangeAuthorizationCode({ url, codeVerifier, state, nonce, }) {
66
+ return await openid.authorizationCodeGrant(await this.getConfig(), url, {
67
+ pkceCodeVerifier: codeVerifier,
68
+ expectedState: state,
69
+ expectedNonce: nonce,
70
+ });
71
+ }
72
+ getExpiresAt(expiresInSeconds) {
73
+ return Math.floor(Date.now() / 1000) + (expiresInSeconds ?? 3600);
74
+ }
75
+ async loadConfig() {
76
+ const protectedResourceConfig = await this.fetchOAuthProtectedResourceConfig();
77
+ const issuer = protectedResourceConfig.authorization_servers[0];
78
+ if (!issuer) {
79
+ throw new Error("No authorization server in OAuth protected resource config");
80
+ }
81
+ const issuerUrl = new URL(issuer);
82
+ try {
83
+ this.config = await openid.discovery(issuerUrl, env.ALPIC_COGNITO_CLIENT_ID);
84
+ }
85
+ catch {
86
+ throw new Error("Failed to discover OAuth config");
87
+ }
88
+ }
89
+ async fetchOAuthProtectedResourceConfig() {
90
+ const baseUrl = env.ALPIC_API_BASE_URL;
91
+ const response = await fetch(`${baseUrl}/.well-known/oauth-protected-resource`);
92
+ if (!response.ok) {
93
+ throw new Error(`Failed to load service config from ${baseUrl} (${response.status} ${response.statusText})`);
94
+ }
95
+ return (await response.json());
96
+ }
97
+ async getConfig() {
98
+ await this.initialize;
99
+ if (!this.config) {
100
+ throw new Error("Config not loaded");
101
+ }
102
+ return this.config;
103
+ }
104
+ isAccessTokenExpired(credentials) {
105
+ const EXPIRATION_WINDOW_IN_SECONDS = 300;
106
+ return Date.now() / 1000 + EXPIRATION_WINDOW_IN_SECONDS >= credentials.expires_at;
107
+ }
108
+ }
109
+ let _oAuthClient = null;
110
+ export function getOAuthClient() {
111
+ if (!_oAuthClient) {
112
+ _oAuthClient = new OAuthClient();
113
+ }
114
+ return _oAuthClient;
115
+ }
116
+ //# sourceMappingURL=oauth-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-client.js","sourceRoot":"","sources":["../../src/auth/oauth-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,234 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Sign in to Alpic</title>
7
+ <link
8
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Mozilla+Text:wght@200..700&display=swap"
9
+ rel="stylesheet"
10
+ />
11
+ <style>
12
+ *,
13
+ *::before,
14
+ *::after {
15
+ margin: 0;
16
+ padding: 0;
17
+ box-sizing: border-box;
18
+ }
19
+
20
+ :root {
21
+ --midnight: #051413;
22
+ }
23
+
24
+ body {
25
+ font-family: "Inter", system-ui, sans-serif;
26
+ background-color: var(--midnight);
27
+ min-height: 100vh;
28
+ display: flex;
29
+ background-image: url("/assets/alpic-mountain.png");
30
+ background-repeat: no-repeat;
31
+ background-position: right bottom;
32
+ background-size: auto;
33
+ padding: 64px;
34
+ }
35
+
36
+ .card {
37
+ background: #fff;
38
+ border-radius: 2px;
39
+ padding: 48px;
40
+ width: 50%;
41
+ min-height: calc(100vh - 128px);
42
+ display: flex;
43
+ flex-direction: column;
44
+ align-items: center;
45
+ justify-content: center;
46
+ }
47
+
48
+ h1 {
49
+ font-family: "Mozilla Text", system-ui, sans-serif;
50
+ font-weight: 400;
51
+ font-size: 48px;
52
+ color: var(--midnight);
53
+ margin-bottom: 8px;
54
+ }
55
+
56
+ .subtitle {
57
+ font-size: 20px;
58
+ color: var(--midnight);
59
+ margin-bottom: 32px;
60
+ }
61
+
62
+ .buttons {
63
+ display: flex;
64
+ flex-direction: column;
65
+ gap: 12px;
66
+ }
67
+
68
+ .btn {
69
+ display: flex;
70
+ align-items: center;
71
+ justify-content: center;
72
+ gap: 8px;
73
+ height: 48px;
74
+ width: 256px;
75
+ padding: 0 16px;
76
+ border-radius: 2px;
77
+ border: 1px solid var(--midnight);
78
+ font-family: "Inter", system-ui, sans-serif;
79
+ font-size: 16px;
80
+ font-weight: 500;
81
+ cursor: pointer;
82
+ transition: background 0.15s;
83
+ }
84
+
85
+ .btn:focus-visible {
86
+ outline: none;
87
+ box-shadow: 0 0 0 2px var(--midnight);
88
+ }
89
+
90
+ .btn svg {
91
+ width: 20px;
92
+ height: 20px;
93
+ flex-shrink: 0;
94
+ }
95
+
96
+ .btn-github {
97
+ background: var(--midnight);
98
+ color: #fff;
99
+ }
100
+ .btn-github svg {
101
+ fill: #fff;
102
+ }
103
+ .btn-github:hover {
104
+ background: rgba(5, 20, 19, 0.9);
105
+ }
106
+
107
+ .btn-google {
108
+ background: #fff;
109
+ color: var(--midnight);
110
+ }
111
+ .btn-google svg {
112
+ fill: var(--midnight);
113
+ }
114
+ .btn-google:hover {
115
+ background: rgba(5, 20, 19, 0.1);
116
+ }
117
+
118
+ .btn-sso {
119
+ background: none;
120
+ border: none;
121
+ color: rgba(5, 20, 19, 0.6);
122
+ font-size: 14px;
123
+ font-weight: 500;
124
+ height: 32px;
125
+ width: auto;
126
+ padding: 0 8px;
127
+ gap: 4px;
128
+ border-radius: 6px;
129
+ }
130
+ .btn-sso svg {
131
+ width: 16px;
132
+ height: 16px;
133
+ stroke: rgba(5, 20, 19, 0.6);
134
+ fill: none;
135
+ }
136
+ .btn-sso:hover {
137
+ background: rgba(5, 20, 19, 0.06);
138
+ color: var(--midnight);
139
+ }
140
+ .btn-sso:hover svg {
141
+ stroke: var(--midnight);
142
+ }
143
+
144
+ .btn-loading {
145
+ opacity: 0.7;
146
+ pointer-events: none;
147
+ cursor: default;
148
+ transition: opacity 0.2s;
149
+ }
150
+ .spinner {
151
+ width: 20px;
152
+ height: 20px;
153
+ animation: spin 0.8s linear infinite;
154
+ }
155
+ @keyframes spin {
156
+ to {
157
+ transform: rotate(360deg);
158
+ }
159
+ }
160
+
161
+ @media (max-width: 768px) {
162
+ body {
163
+ padding: 16px;
164
+ }
165
+ .card {
166
+ width: 100%;
167
+ height: auto;
168
+ padding: 24px;
169
+ }
170
+ h1 {
171
+ font-size: 36px;
172
+ }
173
+ .subtitle {
174
+ font-size: 18px;
175
+ }
176
+ }
177
+ </style>
178
+ </head>
179
+ <body>
180
+ <div class="card">
181
+ <h1>Welcome to Alpic</h1>
182
+ <p class="subtitle">Sign in to use Alpic CLI</p>
183
+ <div class="buttons">
184
+ <button class="btn btn-github" onclick="login('Github', this)" type="button">
185
+ <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
186
+ <title>GitHub</title>
187
+ <path
188
+ d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
189
+ />
190
+ </svg>
191
+ Sign in with GitHub
192
+ </button>
193
+ <button class="btn btn-google" onclick="login('google', this)" type="button">
194
+ <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
195
+ <title>Google</title>
196
+ <path
197
+ d="M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.8 4.133-1.147 1.147-2.933 2.4-6.04 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z"
198
+ />
199
+ </svg>
200
+ Sign in with Google
201
+ </button>
202
+ <button class="btn btn-sso" onclick="login('WorkOS', this)" type="button">
203
+ <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
204
+ <title>SSO</title>
205
+ <path d="M6 22V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v18Z" />
206
+ <path d="M6 12H4a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h2" />
207
+ <path d="M18 9h2a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-2" />
208
+ <path d="M10 6h4" />
209
+ <path d="M10 10h4" />
210
+ <path d="M10 14h4" />
211
+ <path d="M10 18h4" />
212
+ </svg>
213
+ Sign in with SSO
214
+ </button>
215
+ </div>
216
+ </div>
217
+ <script>
218
+ var loaderSvg =
219
+ '<svg class="spinner" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2v4"/><path d="m16.2 7.8 2.9-2.9"/><path d="M18 12h4"/><path d="m16.2 16.2 2.9 2.9"/><path d="M12 18v4"/><path d="m4.9 19.1 2.9-2.9"/><path d="M2 12h4"/><path d="m4.9 4.9 2.9 2.9"/></svg>';
220
+ // biome-ignore lint/correctness/noUnusedVariables: The function is used within the buttons onclick handlers
221
+ function login(provider, btn) {
222
+ const icon = btn.querySelector("svg");
223
+ const wrap = document.createElement("div");
224
+ wrap.innerHTML = loaderSvg;
225
+ icon.replaceWith(wrap.firstChild);
226
+ btn.classList.add("btn-loading");
227
+ btn.disabled = true;
228
+ const url = new URL("%AUTH_BASE_URL%");
229
+ url.searchParams.set("identity_provider", provider);
230
+ window.location.href = url.toString();
231
+ }
232
+ </script>
233
+ </body>
234
+ </html>
@@ -0,0 +1,88 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Sign in to Alpic</title>
7
+ <link
8
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Mozilla+Text:wght@200..700&display=swap"
9
+ rel="stylesheet"
10
+ />
11
+ <style>
12
+ *,
13
+ *::before,
14
+ *::after {
15
+ margin: 0;
16
+ padding: 0;
17
+ box-sizing: border-box;
18
+ }
19
+
20
+ :root {
21
+ --midnight: #051413;
22
+ }
23
+
24
+ body {
25
+ font-family: "Inter", system-ui, sans-serif;
26
+ background-color: var(--midnight);
27
+ min-height: 100vh;
28
+ display: flex;
29
+ align-items: center;
30
+ justify-content: center;
31
+ padding: 64px;
32
+ }
33
+
34
+ .card {
35
+ background: #fff;
36
+ border-radius: 2px;
37
+ padding: 48px;
38
+ display: flex;
39
+ flex-direction: column;
40
+ align-items: center;
41
+ justify-content: center;
42
+ }
43
+
44
+ .message {
45
+ text-align: center;
46
+ }
47
+
48
+ .message-title {
49
+ font-size: 28px;
50
+ color: var(--midnight);
51
+ display: block;
52
+ }
53
+
54
+ .message-sub {
55
+ display: block;
56
+ margin-top: 0.5em;
57
+ color: #6b7280;
58
+ }
59
+
60
+ .message.error .message-title {
61
+ color: #b91c1c;
62
+ }
63
+
64
+ .message.error .message-sub {
65
+ display: none;
66
+ }
67
+
68
+ @media (max-width: 768px) {
69
+ body {
70
+ padding: 16px;
71
+ }
72
+ .card {
73
+ width: 100%;
74
+ height: auto;
75
+ padding: 24px;
76
+ }
77
+ }
78
+ </style>
79
+ </head>
80
+ <body>
81
+ <div class="card">
82
+ <p class="message __CALLBACK_STATUS__">
83
+ <span class="message-title">__CALLBACK_TITLE__</span>
84
+ <span class="message-sub">__CALLBACK_SUB__</span>
85
+ </p>
86
+ </div>
87
+ </body>
88
+ </html>
@@ -0,0 +1,14 @@
1
+ import type { Credentials } from "../store.js";
2
+ type Logger = {
3
+ log: (msg: string) => void;
4
+ error: (msg: string) => void;
5
+ };
6
+ export declare const getLoginPageUrl: () => string;
7
+ export declare const listenToOAuthCallback: ({ state, nonce, codeVerifier, authorizeUrl, logger, }: {
8
+ state: string;
9
+ nonce: string;
10
+ codeVerifier: string;
11
+ authorizeUrl?: string;
12
+ logger?: Logger;
13
+ }) => Promise<Credentials>;
14
+ export {};