@moda-ai/cli 1.13.0 → 1.13.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.
@@ -0,0 +1,35 @@
1
+ import { type ProfileIOOptions } from './profiles.js';
2
+ /**
3
+ * Kind of credential stored in the auth session file. Absent means a legacy
4
+ * Auth0 access token: legacy save paths deliberately leave the field out so
5
+ * a downgraded CLI reading the same file sees a byte-identical shape (the
6
+ * safest downgrade-compat choice). Only the PKCE exchange path writes
7
+ * 'moda_ct'.
8
+ */
9
+ export type StoredAuthTokenKind = 'moda_ct' | 'auth0';
10
+ export interface StoredAuth {
11
+ access_token: string;
12
+ expires_at: number;
13
+ profile?: string;
14
+ token_type?: 'bearer';
15
+ token_kind?: StoredAuthTokenKind;
16
+ user_email?: string;
17
+ }
18
+ export interface AuthSessionOptions extends ProfileIOOptions {
19
+ includeExpired?: boolean;
20
+ }
21
+ /** moda_ct CLI session tokens get renewal + revocation semantics. */
22
+ export declare function isModaCtSession(session: StoredAuth): boolean;
23
+ export declare function isAuthSessionValid(data: StoredAuth | null, now?: number): data is StoredAuth;
24
+ export declare function loadToken(options?: AuthSessionOptions): string | null;
25
+ export declare function loadAuthSession(options?: AuthSessionOptions): StoredAuth | null;
26
+ export declare function resolveStorageProfile(options: ProfileIOOptions): string | undefined;
27
+ export declare function saveAuthSession(accessToken: string, expiresIn: number, options?: ProfileIOOptions, extras?: Pick<StoredAuth, 'token_kind' | 'user_email'>): StoredAuth;
28
+ /**
29
+ * Write a fully-formed session to disk, honoring the profile named on the
30
+ * session itself (so renewals rewrite exactly the file the session was
31
+ * loaded from, profile-scoped or legacy).
32
+ */
33
+ export declare function persistAuthSession(data: StoredAuth, options?: ProfileIOOptions): StoredAuth;
34
+ export declare function clearAuthSession(options?: ProfileIOOptions): void;
35
+ //# sourceMappingURL=auth-storage.d.ts.map
@@ -0,0 +1,159 @@
1
+ // src/auth-storage.ts — StoredAuth model and on-disk auth session persistence
2
+ // (legacy ~/.moda/auth.json and profile-scoped ~/.moda/secrets/auth/<profile>.json).
3
+ import { existsSync, readFileSync, rmSync } from 'node:fs';
4
+ import { getModaPaths, loadProfileConfig, saveProfileConfig, secretFilePathFromRef, writeSecretJsonFile, } from './profiles.js';
5
+ function tokenPath(options = {}) {
6
+ return getModaPaths(options).legacyAuthPath;
7
+ }
8
+ function authTokenRefForProfile(profile) {
9
+ return `file:secrets/auth/${profile}.json`;
10
+ }
11
+ function parseStoredAuth(value) {
12
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
13
+ return null;
14
+ }
15
+ const data = value;
16
+ if (typeof data.access_token !== 'string' ||
17
+ typeof data.expires_at !== 'number') {
18
+ return null;
19
+ }
20
+ return {
21
+ access_token: data.access_token,
22
+ expires_at: data.expires_at,
23
+ profile: typeof data.profile === 'string' ? data.profile : undefined,
24
+ token_type: data.token_type === 'bearer' ? 'bearer' : undefined,
25
+ // Additive fields: old files simply lack them (-> undefined).
26
+ token_kind: data.token_kind === 'moda_ct' || data.token_kind === 'auth0'
27
+ ? data.token_kind
28
+ : undefined,
29
+ user_email: typeof data.user_email === 'string' ? data.user_email : undefined,
30
+ };
31
+ }
32
+ /** moda_ct CLI session tokens get renewal + revocation semantics. */
33
+ export function isModaCtSession(session) {
34
+ return (session.token_kind === 'moda_ct' ||
35
+ session.access_token.startsWith('moda_ct_'));
36
+ }
37
+ export function isAuthSessionValid(data, now = Date.now()) {
38
+ if (!data)
39
+ return false;
40
+ return now / 1000 <= data.expires_at - 60;
41
+ }
42
+ function readStoredAuth(path) {
43
+ try {
44
+ if (!existsSync(path))
45
+ return null;
46
+ return parseStoredAuth(JSON.parse(readFileSync(path, 'utf8')));
47
+ }
48
+ catch {
49
+ return null;
50
+ }
51
+ }
52
+ export function loadToken(options = {}) {
53
+ return loadAuthSession(options)?.access_token ?? null;
54
+ }
55
+ export function loadAuthSession(options = {}) {
56
+ const profileSession = loadProfileAuthSession(options);
57
+ if (profileSession)
58
+ return profileSession;
59
+ const legacy = readStoredAuth(tokenPath(options));
60
+ if (!legacy)
61
+ return null;
62
+ if (!options.includeExpired && !isAuthSessionValid(legacy)) {
63
+ return null;
64
+ }
65
+ return legacy;
66
+ }
67
+ function loadProfileAuthSession(options) {
68
+ const profileName = options.profile ?? (options.env ?? process.env).MODA_PROFILE;
69
+ if (!profileName)
70
+ return null;
71
+ try {
72
+ const profile = loadProfileConfig(options).profiles[profileName];
73
+ const tokenRef = profile?.auth_token_ref;
74
+ if (!tokenRef)
75
+ return null;
76
+ const path = secretFilePathFromRef(tokenRef, options);
77
+ if (!path)
78
+ return null;
79
+ const session = readStoredAuth(path);
80
+ if (!session)
81
+ return null;
82
+ if (!options.includeExpired && !isAuthSessionValid(session))
83
+ return null;
84
+ return session;
85
+ }
86
+ catch {
87
+ return null;
88
+ }
89
+ }
90
+ export function resolveStorageProfile(options) {
91
+ return options.profile ?? (options.env ?? process.env).MODA_PROFILE;
92
+ }
93
+ export function saveAuthSession(accessToken, expiresIn, options = {}, extras = {}) {
94
+ return persistAuthSession({
95
+ access_token: accessToken,
96
+ expires_at: Math.floor(Date.now() / 1000) + expiresIn,
97
+ profile: resolveStorageProfile(options),
98
+ token_type: 'bearer',
99
+ ...(extras.token_kind ? { token_kind: extras.token_kind } : {}),
100
+ ...(extras.user_email ? { user_email: extras.user_email } : {}),
101
+ }, options);
102
+ }
103
+ /**
104
+ * Write a fully-formed session to disk, honoring the profile named on the
105
+ * session itself (so renewals rewrite exactly the file the session was
106
+ * loaded from, profile-scoped or legacy).
107
+ */
108
+ export function persistAuthSession(data, options = {}) {
109
+ const profile = data.profile;
110
+ if (profile) {
111
+ const ref = authTokenRefForProfile(profile);
112
+ const path = secretFilePathFromRef(ref, options);
113
+ if (!path)
114
+ throw new Error(`Unsupported auth token reference: ${ref}`);
115
+ const config = loadProfileConfig(options);
116
+ const existing = config.profiles[profile];
117
+ if (!existing) {
118
+ throw new Error(`Moda profile '${profile}' does not exist.`);
119
+ }
120
+ writeSecretJsonFile(path, data);
121
+ saveProfileConfig({
122
+ ...config,
123
+ profiles: {
124
+ ...config.profiles,
125
+ [profile]: {
126
+ ...existing,
127
+ auth_token_ref: ref,
128
+ },
129
+ },
130
+ }, options);
131
+ return data;
132
+ }
133
+ writeSecretJsonFile(tokenPath(options), data);
134
+ return data;
135
+ }
136
+ export function clearAuthSession(options = {}) {
137
+ const profile = options.profile ?? (options.env ?? process.env).MODA_PROFILE;
138
+ if (profile) {
139
+ const ref = authTokenRefForProfile(profile);
140
+ const path = secretFilePathFromRef(ref, options);
141
+ if (path)
142
+ rmSync(path, { force: true });
143
+ const config = loadProfileConfig(options);
144
+ const existing = config.profiles[profile];
145
+ if (existing) {
146
+ const { auth_token_ref: _drop, ...profileWithoutToken } = existing;
147
+ saveProfileConfig({
148
+ ...config,
149
+ profiles: {
150
+ ...config.profiles,
151
+ [profile]: profileWithoutToken,
152
+ },
153
+ }, options);
154
+ }
155
+ return;
156
+ }
157
+ rmSync(tokenPath(options), { force: true });
158
+ }
159
+ //# sourceMappingURL=auth-storage.js.map
package/dist/auth.d.ts CHANGED
@@ -1,18 +1,22 @@
1
1
  import { type ProfileIOOptions } from './profiles.js';
2
- export interface StoredAuth {
3
- access_token: string;
4
- expires_at: number;
5
- profile?: string;
6
- token_type?: 'bearer';
2
+ import { type AuthSessionOptions } from './auth-storage.js';
3
+ export { clearAuthSession, isAuthSessionValid, loadAuthSession, loadToken, saveAuthSession, type AuthSessionOptions, type StoredAuth, type StoredAuthTokenKind, } from './auth-storage.js';
4
+ export { revokeCliSessionToken } from './cli-session.js';
5
+ /** base64url(sha256(verifier)) — the S256 code challenge for a verifier. */
6
+ export declare function computePkceChallenge(codeVerifier: string): string;
7
+ export interface CliLoginContext {
8
+ user: {
9
+ email: string;
10
+ name: string | null;
11
+ };
12
+ tenants: {
13
+ id: string;
14
+ name: string;
15
+ slug: string;
16
+ role: string;
17
+ }[];
7
18
  }
8
- export interface AuthSessionOptions extends ProfileIOOptions {
9
- includeExpired?: boolean;
10
- }
11
- export declare function isAuthSessionValid(data: StoredAuth | null, now?: number): data is StoredAuth;
12
- export declare function loadToken(options?: AuthSessionOptions): string | null;
13
- export declare function loadAuthSession(options?: AuthSessionOptions): StoredAuth | null;
14
- export declare function saveAuthSession(accessToken: string, expiresIn: number, options?: ProfileIOOptions): StoredAuth;
15
- export declare function clearAuthSession(options?: ProfileIOOptions): void;
19
+ export declare function getCachedLoginContext(options?: ProfileIOOptions): CliLoginContext | null;
16
20
  export interface LoginOptions extends ProfileIOOptions {
17
21
  /** Use this exact port for the localhost callback (no fallback ladder). */
18
22
  callbackPort?: number;