@intlayer/cli 8.9.6-canary.0 → 8.9.7

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.
@@ -1,6 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
3
3
  const require_utils_openBrowser = require('../utils/openBrowser.cjs');
4
+ const require_auth_sessionToken = require('./sessionToken.cjs');
4
5
  let _intlayer_chokidar_cli = require("@intlayer/chokidar/cli");
5
6
  let _intlayer_config_colors = require("@intlayer/config/colors");
6
7
  _intlayer_config_colors = require_runtime.__toESM(_intlayer_config_colors);
@@ -11,13 +12,65 @@ node_http = require_runtime.__toESM(node_http);
11
12
  let node_url = require("node:url");
12
13
 
13
14
  //#region src/auth/login.ts
15
+ const buildSuccessHtml = (message) => `
16
+ <!DOCTYPE html>
17
+ <html lang="en" data-theme="dark">
18
+ <head>
19
+ <meta charset="UTF-8">
20
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
21
+ <title>Intlayer CLI Login</title>
22
+ <style>
23
+ :root {
24
+ --color-background: rgba(23, 23, 23);
25
+ --color-card: rgba(39, 39, 39);
26
+ --color-text: rgba(255, 245, 237);
27
+ --color-neutral: rgba(93, 93, 93);
28
+ --font-sans: "Inter", -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
29
+ }
30
+ * { box-sizing: border-box; }
31
+ body {
32
+ font-family: var(--font-sans);
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: center;
36
+ min-height: 100vh;
37
+ margin: 0;
38
+ padding: 1rem;
39
+ background-color: var(--color-background);
40
+ color: var(--color-text);
41
+ }
42
+ .container {
43
+ text-align: center;
44
+ padding: 2rem;
45
+ border-radius: 1rem;
46
+ background-color: var(--color-card);
47
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
48
+ max-width: 400px;
49
+ width: 100%;
50
+ }
51
+ h1 { margin: 0 0 1rem 0; font-size: 1.5rem; font-weight: 700; color: var(--color-text); }
52
+ p { color: var(--color-neutral); font-size: 0.8rem; margin: 0 0 1.5rem 0; line-height: 1.5; }
53
+ </style>
54
+ </head>
55
+ <body>
56
+ <div class="container">
57
+ <h1>Login Successful</h1>
58
+ <p>${message}</p>
59
+ </div>
60
+ <script>
61
+ window.close();
62
+ setTimeout(() => { window.close(); }, 1000);
63
+ <\/script>
64
+ </body>
65
+ </html>
66
+ `;
14
67
  const login = async (options) => {
15
68
  const configuration = (0, _intlayer_config_node.getConfiguration)(options.configOptions);
16
69
  (0, _intlayer_chokidar_cli.logConfigDetails)(options?.configOptions);
17
70
  const logger = (0, _intlayer_config_logger.getAppLogger)(configuration);
18
71
  const cmsUrl = options.cmsUrl ?? configuration.editor.cmsURL;
19
72
  return new Promise((resolve) => {
20
- const server = node_http.default.createServer((req, res) => {
73
+ const server = node_http.default.createServer(async (req, res) => {
21
74
  const url = new node_url.URL(req.url ?? "", `http://${req.headers.host}`);
22
75
  res.setHeader("Access-Control-Allow-Origin", "*");
23
76
  res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
@@ -28,8 +81,24 @@ const login = async (options) => {
28
81
  return;
29
82
  }
30
83
  if (url.pathname === "/callback") {
84
+ const sessionToken = url.searchParams.get("sessionToken");
85
+ const sessionExpiresAt = url.searchParams.get("expiresAt");
31
86
  const clientId = url.searchParams.get("clientId");
32
87
  const clientSecret = url.searchParams.get("clientSecret");
88
+ if (sessionToken && sessionExpiresAt) {
89
+ logger("");
90
+ logger(`Log in successful. ${(0, _intlayer_config_logger.colorize)("2h", _intlayer_config_colors.BLUE)} session token received.`);
91
+ logger("");
92
+ logger((0, _intlayer_config_logger.colorize)(`Token expires at: ${new Date(sessionExpiresAt).toLocaleString()}`, _intlayer_config_colors.GREY));
93
+ await require_auth_sessionToken.writeCliSessionToken(configuration, sessionToken, new Date(sessionExpiresAt));
94
+ res.writeHead(200, { "Content-Type": "text/html" });
95
+ res.end(buildSuccessHtml("Your 2h session token has been stored. You can now close this tab and return to your terminal."));
96
+ server.close(() => {
97
+ resolve();
98
+ process.exit(0);
99
+ });
100
+ return;
101
+ }
33
102
  if (clientId && clientSecret) {
34
103
  logger("");
35
104
  logger("Log in successful. Client ID and Client Secret received.");
@@ -60,80 +129,7 @@ const login = async (options) => {
60
129
  });
61
130
  logger((0, _intlayer_config_logger.colorize)("--------------------------------", _intlayer_config_colors.GREY_DARK));
62
131
  res.writeHead(200, { "Content-Type": "text/html" });
63
- res.end(`
64
- <!DOCTYPE html>
65
- <html lang="en" data-theme="dark">
66
- <head>
67
- <meta charset="UTF-8">
68
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
69
- <title>Intlayer CLI Login</title>
70
- <style>
71
- :root {
72
- --color-background: rgba(23, 23, 23);
73
- --color-card: rgba(39, 39, 39);
74
- --color-text: rgba(255, 245, 237);
75
- --color-neutral: rgba(93, 93, 93);
76
- --font-sans: "Inter", -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
77
- }
78
-
79
- * {
80
- box-sizing: border-box;
81
- }
82
-
83
- body {
84
- font-family: var(--font-sans);
85
- display: flex;
86
- align-items: center;
87
- justify-content: center;
88
- min-height: 100vh;
89
- margin: 0;
90
- padding: 1rem;
91
- background-color: var(--color-background);
92
- color: var(--color-text);
93
- }
94
-
95
- .container {
96
- text-align: center;
97
- padding: 2rem;
98
- border-radius: 1rem;
99
- background-color: var(--color-card);
100
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
101
- max-width: 400px;
102
- width: 100%;
103
- }
104
-
105
- h1 {
106
- margin: 0 0 1rem 0;
107
- font-size: 1.5rem;
108
- font-weight: 700;
109
- color: var(--color-text);
110
- }
111
-
112
- p {
113
- color: var(--color-neutral);
114
- font-size: 0.8rem;
115
- margin: 0 0 1.5rem 0;
116
- line-height: 1.5;
117
- }
118
- </style>
119
- </head>
120
- <body>
121
- <div class="container">
122
- <h1>Login Successful</h1>
123
- <p>You have successfully logged in to Intlayer CLI. You can now close this tab and return to your terminal.</p>
124
- </div>
125
- <script>
126
- // Attempt to close the window
127
- window.close();
128
-
129
- // Fallback: if window.close() doesn't work, show a message
130
- setTimeout(() => {
131
- window.close();
132
- }, 1000);
133
- <\/script>
134
- </body>
135
- </html>
136
- `);
132
+ res.end(buildSuccessHtml("You have successfully logged in to Intlayer CLI. You can now close this tab and return to your terminal."));
137
133
  server.close(() => {
138
134
  resolve();
139
135
  process.exit(0);
@@ -1 +1 @@
1
- {"version":3,"file":"login.cjs","names":["http","URL","ANSIColors"],"sources":["../../../src/auth/login.ts"],"sourcesContent":["import http from 'node:http';\nimport { URL } from 'node:url';\nimport { logConfigDetails } from '@intlayer/chokidar/cli';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, colorizePath, getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { openBrowser } from '../utils/openBrowser';\n\ntype LoginOptions = {\n cmsUrl?: string;\n configOptions?: GetConfigurationOptions;\n};\n\nexport const login = async (options: LoginOptions) => {\n const configuration = getConfiguration(options.configOptions);\n logConfigDetails(options?.configOptions);\n\n const logger = getAppLogger(configuration);\n\n const cmsUrl = options.cmsUrl ?? configuration.editor.cmsURL;\n\n return new Promise<void>((resolve) => {\n const server = http.createServer((req, res) => {\n const url = new URL(req.url ?? '', `http://${req.headers.host}`);\n\n // Set CORS headers\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (url.pathname === '/callback') {\n const clientId = url.searchParams.get('clientId');\n const clientSecret = url.searchParams.get('clientSecret');\n\n if (clientId && clientSecret) {\n logger('');\n logger('Log in successful. Client ID and Client Secret received.');\n\n logger('');\n logger([\n '1. Insert the Client ID and Client Secret in your',\n colorizePath('.env'),\n 'file:',\n ]);\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n logger(\n [\n colorize('INTLAYER_CLIENT_ID=', ANSIColors.GREY_LIGHT),\n colorize(clientId, ANSIColors.BLUE),\n ].join('')\n );\n logger(\n [\n colorize('INTLAYER_CLIENT_SECRET=', ANSIColors.GREY_LIGHT),\n colorize(clientSecret, ANSIColors.BLUE),\n ].join('')\n );\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n logger('');\n logger('2. Insert in your Intlayer configuration file:');\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n [\n `${ANSIColors.GREY_LIGHT}{`,\n ` editor: {`,\n ` enabled: true,`,\n ` cmsURL: '${colorizePath(cmsUrl!, undefined, ANSIColors.GREY_LIGHT)}',`,\n ` clientId: '${colorize('process.env.INTLAYER_CLIENT_ID', ANSIColors.BLUE, ANSIColors.GREY_LIGHT)}',`,\n ` clientSecret: '${colorize('process.env.INTLAYER_CLIENT_SECRET', ANSIColors.BLUE, ANSIColors.GREY_LIGHT)}',`,\n ` },`,\n `}`,\n ].forEach((line) => {\n logger(line);\n });\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(`\n <!DOCTYPE html>\n <html lang=\"en\" data-theme=\"dark\">\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Intlayer CLI Login</title>\n <style>\n :root {\n --color-background: rgba(23, 23, 23);\n --color-card: rgba(39, 39, 39);\n --color-text: rgba(255, 245, 237);\n --color-neutral: rgba(93, 93, 93);\n --font-sans: \"Inter\", -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;\n }\n \n * {\n box-sizing: border-box;\n }\n \n body {\n font-family: var(--font-sans);\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n margin: 0;\n padding: 1rem;\n background-color: var(--color-background);\n color: var(--color-text);\n }\n \n .container {\n text-align: center;\n padding: 2rem;\n border-radius: 1rem;\n background-color: var(--color-card);\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n max-width: 400px;\n width: 100%;\n }\n \n h1 {\n margin: 0 0 1rem 0;\n font-size: 1.5rem;\n font-weight: 700;\n color: var(--color-text);\n }\n \n p {\n color: var(--color-neutral);\n font-size: 0.8rem;\n margin: 0 0 1.5rem 0;\n line-height: 1.5;\n }\n </style>\n </head>\n <body>\n <div class=\"container\">\n <h1>Login Successful</h1>\n <p>You have successfully logged in to Intlayer CLI. You can now close this tab and return to your terminal.</p>\n </div>\n <script>\n // Attempt to close the window\n window.close();\n \n // Fallback: if window.close() doesn't work, show a message\n setTimeout(() => {\n window.close();\n }, 1000);\n </script>\n </body>\n </html>\n `);\n\n server.close(() => {\n resolve();\n process.exit(0);\n });\n } else {\n res.writeHead(400, { 'Content-Type': 'text/plain' });\n res.end('Missing parameters');\n }\n } else {\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('Not found');\n }\n });\n\n server.listen(0, () => {\n const address = server.address();\n const port = typeof address === 'object' && address ? address.port : 0;\n const state = Math.random().toString(36).substring(7);\n\n const websiteUrl =\n cmsUrl ?? process.env.INTLAYER_SITE_URL ?? 'http://localhost:3000';\n const loginUrl = `${websiteUrl}/auth/cli-login?port=${port}&state=${state}`;\n\n logger('Opening browser for login...');\n logger(`If browser does not open, visit: ${colorizePath(loginUrl)}`);\n\n openBrowser(loginUrl);\n });\n });\n};\n"],"mappings":";;;;;;;;;;;;;AAgBA,MAAa,QAAQ,OAAO,YAA0B;CACpD,MAAM,4DAAiC,QAAQ,cAAc;CAC7D,6CAAiB,SAAS,cAAc;CAExC,MAAM,mDAAsB,cAAc;CAE1C,MAAM,SAAS,QAAQ,UAAU,cAAc,OAAO;CAEtD,OAAO,IAAI,SAAe,YAAY;EACpC,MAAM,SAASA,kBAAK,cAAc,KAAK,QAAQ;GAC7C,MAAM,MAAM,IAAIC,aAAI,IAAI,OAAO,IAAI,UAAU,IAAI,QAAQ,OAAO;GAGhE,IAAI,UAAU,+BAA+B,IAAI;GACjD,IAAI,UAAU,gCAAgC,eAAe;GAC7D,IAAI,UAAU,gCAAgC,eAAe;GAE7D,IAAI,IAAI,WAAW,WAAW;IAC5B,IAAI,UAAU,IAAI;IAClB,IAAI,KAAK;IACT;;GAGF,IAAI,IAAI,aAAa,aAAa;IAChC,MAAM,WAAW,IAAI,aAAa,IAAI,WAAW;IACjD,MAAM,eAAe,IAAI,aAAa,IAAI,eAAe;IAEzD,IAAI,YAAY,cAAc;KAC5B,OAAO,GAAG;KACV,OAAO,2DAA2D;KAElE,OAAO,GAAG;KACV,OAAO;MACL;gDACa,OAAO;MACpB;MACD,CAAC;KACF,6CACW,oCAAoCC,wBAAW,UAAU,CACnE;KACD,OACE,uCACW,uBAAuBA,wBAAW,WAAW,wCAC7C,UAAUA,wBAAW,KAAK,CACpC,CAAC,KAAK,GAAG,CACX;KACD,OACE,uCACW,2BAA2BA,wBAAW,WAAW,wCACjD,cAAcA,wBAAW,KAAK,CACxC,CAAC,KAAK,GAAG,CACX;KACD,6CACW,oCAAoCA,wBAAW,UAAU,CACnE;KACD,OAAO,GAAG;KACV,OAAO,iDAAiD;KACxD,6CACW,oCAAoCA,wBAAW,UAAU,CACnE;KACD;MACE,GAAGA,wBAAW,WAAW;MACzB;MACA;MACA,2DAA8B,QAAS,QAAWA,wBAAW,WAAW,CAAC;MACzE,yDAA4B,kCAAkCA,wBAAW,MAAMA,wBAAW,WAAW,CAAC;MACtG,6DAAgC,sCAAsCA,wBAAW,MAAMA,wBAAW,WAAW,CAAC;MAC9G;MACA;MACD,CAAC,SAAS,SAAS;MAClB,OAAO,KAAK;OACZ;KACF,6CACW,oCAAoCA,wBAAW,UAAU,CACnE;KAED,IAAI,UAAU,KAAK,EAAE,gBAAgB,aAAa,CAAC;KACnD,IAAI,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAyEN;KAEF,OAAO,YAAY;MACjB,SAAS;MACT,QAAQ,KAAK,EAAE;OACf;WACG;KACL,IAAI,UAAU,KAAK,EAAE,gBAAgB,cAAc,CAAC;KACpD,IAAI,IAAI,qBAAqB;;UAE1B;IACL,IAAI,UAAU,KAAK,EAAE,gBAAgB,cAAc,CAAC;IACpD,IAAI,IAAI,YAAY;;IAEtB;EAEF,OAAO,OAAO,SAAS;GACrB,MAAM,UAAU,OAAO,SAAS;GAChC,MAAM,OAAO,OAAO,YAAY,YAAY,UAAU,QAAQ,OAAO;GACrE,MAAM,QAAQ,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,UAAU,EAAE;GAIrD,MAAM,WAAW,GADf,UAAU,QAAQ,IAAI,qBAAqB,wBACd,uBAAuB,KAAK,SAAS;GAEpE,OAAO,+BAA+B;GACtC,OAAO,8EAAiD,SAAS,GAAG;GAEpE,sCAAY,SAAS;IACrB;GACF"}
1
+ {"version":3,"file":"login.cjs","names":["http","URL","ANSIColors","writeCliSessionToken"],"sources":["../../../src/auth/login.ts"],"sourcesContent":["import http from 'node:http';\nimport { relative } from 'node:path';\nimport { URL } from 'node:url';\nimport { logConfigDetails } from '@intlayer/chokidar/cli';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, colorizePath, getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { openBrowser } from '../utils/openBrowser';\nimport { writeCliSessionToken } from './sessionToken';\n\nconst buildSuccessHtml = (message: string): string => `\n <!DOCTYPE html>\n <html lang=\"en\" data-theme=\"dark\">\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Intlayer CLI Login</title>\n <style>\n :root {\n --color-background: rgba(23, 23, 23);\n --color-card: rgba(39, 39, 39);\n --color-text: rgba(255, 245, 237);\n --color-neutral: rgba(93, 93, 93);\n --font-sans: \"Inter\", -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;\n }\n * { box-sizing: border-box; }\n body {\n font-family: var(--font-sans);\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n margin: 0;\n padding: 1rem;\n background-color: var(--color-background);\n color: var(--color-text);\n }\n .container {\n text-align: center;\n padding: 2rem;\n border-radius: 1rem;\n background-color: var(--color-card);\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n max-width: 400px;\n width: 100%;\n }\n h1 { margin: 0 0 1rem 0; font-size: 1.5rem; font-weight: 700; color: var(--color-text); }\n p { color: var(--color-neutral); font-size: 0.8rem; margin: 0 0 1.5rem 0; line-height: 1.5; }\n </style>\n </head>\n <body>\n <div class=\"container\">\n <h1>Login Successful</h1>\n <p>${message}</p>\n </div>\n <script>\n window.close();\n setTimeout(() => { window.close(); }, 1000);\n </script>\n </body>\n </html>\n`;\n\ntype LoginOptions = {\n cmsUrl?: string;\n configOptions?: GetConfigurationOptions;\n};\n\nexport const login = async (options: LoginOptions) => {\n const configuration = getConfiguration(options.configOptions);\n logConfigDetails(options?.configOptions);\n\n const logger = getAppLogger(configuration);\n\n const cmsUrl = options.cmsUrl ?? configuration.editor.cmsURL;\n\n return new Promise<void>((resolve) => {\n const server = http.createServer(async (req, res) => {\n const url = new URL(req.url ?? '', `http://${req.headers.host}`);\n\n // Set CORS headers\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (url.pathname === '/callback') {\n const sessionToken = url.searchParams.get('sessionToken');\n const sessionExpiresAt = url.searchParams.get('expiresAt');\n const clientId = url.searchParams.get('clientId');\n const clientSecret = url.searchParams.get('clientSecret');\n\n if (sessionToken && sessionExpiresAt) {\n logger('');\n logger(\n `Log in successful. ${colorize('2h', ANSIColors.BLUE)} session token received.`\n );\n logger('');\n\n logger(\n colorize(\n `Token expires at: ${new Date(sessionExpiresAt).toLocaleString()}`,\n ANSIColors.GREY\n )\n );\n\n await writeCliSessionToken(\n configuration,\n sessionToken,\n new Date(sessionExpiresAt)\n );\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(\n buildSuccessHtml(\n 'Your 2h session token has been stored. You can now close this tab and return to your terminal.'\n )\n );\n\n server.close(() => {\n resolve();\n process.exit(0);\n });\n return;\n }\n\n if (clientId && clientSecret) {\n logger('');\n logger('Log in successful. Client ID and Client Secret received.');\n\n logger('');\n logger([\n '1. Insert the Client ID and Client Secret in your',\n colorizePath('.env'),\n 'file:',\n ]);\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n logger(\n [\n colorize('INTLAYER_CLIENT_ID=', ANSIColors.GREY_LIGHT),\n colorize(clientId, ANSIColors.BLUE),\n ].join('')\n );\n logger(\n [\n colorize('INTLAYER_CLIENT_SECRET=', ANSIColors.GREY_LIGHT),\n colorize(clientSecret, ANSIColors.BLUE),\n ].join('')\n );\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n logger('');\n logger('2. Insert in your Intlayer configuration file:');\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n [\n `${ANSIColors.GREY_LIGHT}{`,\n ` editor: {`,\n ` enabled: true,`,\n ` cmsURL: '${colorizePath(cmsUrl!, undefined, ANSIColors.GREY_LIGHT)}',`,\n ` clientId: '${colorize('process.env.INTLAYER_CLIENT_ID', ANSIColors.BLUE, ANSIColors.GREY_LIGHT)}',`,\n ` clientSecret: '${colorize('process.env.INTLAYER_CLIENT_SECRET', ANSIColors.BLUE, ANSIColors.GREY_LIGHT)}',`,\n ` },`,\n `}`,\n ].forEach((line) => {\n logger(line);\n });\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(\n buildSuccessHtml(\n 'You have successfully logged in to Intlayer CLI. You can now close this tab and return to your terminal.'\n )\n );\n\n server.close(() => {\n resolve();\n process.exit(0);\n });\n } else {\n res.writeHead(400, { 'Content-Type': 'text/plain' });\n res.end('Missing parameters');\n }\n } else {\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('Not found');\n }\n });\n\n server.listen(0, () => {\n const address = server.address();\n const port = typeof address === 'object' && address ? address.port : 0;\n const state = Math.random().toString(36).substring(7);\n\n const websiteUrl =\n cmsUrl ?? process.env.INTLAYER_SITE_URL ?? 'http://localhost:3000';\n const loginUrl = `${websiteUrl}/auth/cli-login?port=${port}&state=${state}`;\n\n logger('Opening browser for login...');\n logger(`If browser does not open, visit: ${colorizePath(loginUrl)}`);\n\n openBrowser(loginUrl);\n });\n });\n};\n"],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
3
+ let node_path = require("node:path");
4
+ let node_fs_promises = require("node:fs/promises");
5
+
6
+ //#region src/auth/sessionToken.ts
7
+ const SESSION_FILE_NAME = "cli-session.json";
8
+ const getSessionFilePath = (config) => (0, node_path.join)(config.system.tempDir, SESSION_FILE_NAME);
9
+ const writeCliSessionToken = async (config, token, expiresAt) => {
10
+ const filePath = getSessionFilePath(config);
11
+ await (0, node_fs_promises.mkdir)(config.system.tempDir, { recursive: true });
12
+ const data = {
13
+ token,
14
+ expiresAt: expiresAt.toISOString()
15
+ };
16
+ await (0, node_fs_promises.writeFile)(filePath, JSON.stringify(data, null, 2), { mode: 384 });
17
+ };
18
+ const readCliSessionToken = async (config) => {
19
+ const filePath = getSessionFilePath(config);
20
+ try {
21
+ const raw = await (0, node_fs_promises.readFile)(filePath, "utf8");
22
+ const data = JSON.parse(raw);
23
+ if (!data.token || !data.expiresAt) return null;
24
+ if (/* @__PURE__ */ new Date() >= new Date(data.expiresAt)) {
25
+ await clearCliSessionToken(config);
26
+ return null;
27
+ }
28
+ return data;
29
+ } catch {
30
+ return null;
31
+ }
32
+ };
33
+ const clearCliSessionToken = async (config) => {
34
+ try {
35
+ await (0, node_fs_promises.unlink)(getSessionFilePath(config));
36
+ } catch {}
37
+ };
38
+
39
+ //#endregion
40
+ exports.clearCliSessionToken = clearCliSessionToken;
41
+ exports.readCliSessionToken = readCliSessionToken;
42
+ exports.writeCliSessionToken = writeCliSessionToken;
43
+ //# sourceMappingURL=sessionToken.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionToken.cjs","names":[],"sources":["../../../src/auth/sessionToken.ts"],"sourcesContent":["import { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { IntlayerConfig } from '@intlayer/types/config';\n\nconst SESSION_FILE_NAME = 'cli-session.json';\n\ntype CliSessionData = {\n token: string;\n expiresAt: string; // ISO date string\n};\n\nconst getSessionFilePath = (config: IntlayerConfig): string =>\n join(config.system.tempDir, SESSION_FILE_NAME);\n\nexport const writeCliSessionToken = async (\n config: IntlayerConfig,\n token: string,\n expiresAt: Date\n): Promise<void> => {\n const filePath = getSessionFilePath(config);\n\n await mkdir(config.system.tempDir, { recursive: true });\n\n const data: CliSessionData = { token, expiresAt: expiresAt.toISOString() };\n await writeFile(filePath, JSON.stringify(data, null, 2), { mode: 0o600 });\n};\n\nexport const readCliSessionToken = async (\n config: IntlayerConfig\n): Promise<CliSessionData | null> => {\n const filePath = getSessionFilePath(config);\n\n try {\n const raw = await readFile(filePath, 'utf8');\n const data: CliSessionData = JSON.parse(raw);\n\n if (!data.token || !data.expiresAt) {\n return null;\n }\n\n if (new Date() >= new Date(data.expiresAt)) {\n await clearCliSessionToken(config);\n return null;\n }\n\n return data;\n } catch {\n return null;\n }\n};\n\nexport const clearCliSessionToken = async (\n config: IntlayerConfig\n): Promise<void> => {\n try {\n await unlink(getSessionFilePath(config));\n } catch {\n // Ignore errors (file may not exist)\n }\n};\n"],"mappings":";;;;;;AAIA,MAAM,oBAAoB;AAO1B,MAAM,sBAAsB,+BACrB,OAAO,OAAO,SAAS,kBAAkB;AAEhD,MAAa,uBAAuB,OAClC,QACA,OACA,cACkB;CAClB,MAAM,WAAW,mBAAmB,OAAO;CAE3C,kCAAY,OAAO,OAAO,SAAS,EAAE,WAAW,MAAM,CAAC;CAEvD,MAAM,OAAuB;EAAE;EAAO,WAAW,UAAU,aAAa;EAAE;CAC1E,sCAAgB,UAAU,KAAK,UAAU,MAAM,MAAM,EAAE,EAAE,EAAE,MAAM,KAAO,CAAC;;AAG3E,MAAa,sBAAsB,OACjC,WACmC;CACnC,MAAM,WAAW,mBAAmB,OAAO;CAE3C,IAAI;EACF,MAAM,MAAM,qCAAe,UAAU,OAAO;EAC5C,MAAM,OAAuB,KAAK,MAAM,IAAI;EAE5C,IAAI,CAAC,KAAK,SAAS,CAAC,KAAK,WACvB,OAAO;EAGT,oBAAI,IAAI,MAAM,IAAI,IAAI,KAAK,KAAK,UAAU,EAAE;GAC1C,MAAM,qBAAqB,OAAO;GAClC,OAAO;;EAGT,OAAO;SACD;EACN,OAAO;;;AAIX,MAAa,uBAAuB,OAClC,WACkB;CAClB,IAAI;EACF,mCAAa,mBAAmB,OAAO,CAAC;SAClC"}
@@ -1,5 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
3
+ const require_auth_sessionToken = require('../auth/sessionToken.cjs');
3
4
  const require_utils_checkConfigConsistency = require('./checkConfigConsistency.cjs');
4
5
  let _intlayer_config_colors = require("@intlayer/config/colors");
5
6
  _intlayer_config_colors = require_runtime.__toESM(_intlayer_config_colors);
@@ -8,12 +9,53 @@ let _intlayer_api = require("@intlayer/api");
8
9
  let _intlayer_config_utils = require("@intlayer/config/utils");
9
10
 
10
11
  //#region src/utils/checkAccess.ts
12
+ const checkProjectConfigConsistency = (project, configuration, appLogger) => {
13
+ if (!project?.configuration) return;
14
+ try {
15
+ let remoteConfigToCheck = project.configuration;
16
+ if (remoteConfigToCheck.ai && "apiKeyConfigured" in remoteConfigToCheck.ai) {
17
+ const { apiKeyConfigured, ...restAi } = remoteConfigToCheck.ai;
18
+ remoteConfigToCheck = {
19
+ ...remoteConfigToCheck,
20
+ ai: restAi
21
+ };
22
+ }
23
+ require_utils_checkConfigConsistency.checkConfigConsistency(remoteConfigToCheck, configuration);
24
+ } catch {
25
+ appLogger([
26
+ "Remote configuration is not up to date. The project configuration does not match the local configuration.",
27
+ "You can push the configuration by running",
28
+ (0, _intlayer_config_logger.colorize)("npx intlayer configuration push", _intlayer_config_colors.CYAN),
29
+ (0, _intlayer_config_logger.colorize)("(see doc:", _intlayer_config_colors.GREY_DARK),
30
+ (0, _intlayer_config_logger.colorize)("https://intlayer.org/doc/concept/cli/push", _intlayer_config_colors.GREY),
31
+ (0, _intlayer_config_logger.colorize)(")", _intlayer_config_colors.GREY_DARK),
32
+ "."
33
+ ], { level: "warn" });
34
+ }
35
+ };
11
36
  const checkCMSAuth = async (configuration, shouldCheckConfigConsistency = true) => {
12
37
  const appLogger = (0, _intlayer_config_logger.getAppLogger)(configuration);
38
+ const sessionData = await require_auth_sessionToken.readCliSessionToken(configuration);
39
+ if (sessionData) {
40
+ const intlayerAPI = (0, _intlayer_api.getIntlayerAPIProxy)(void 0, configuration, sessionData.token);
41
+ try {
42
+ const project = (await intlayerAPI.oAuth.getCliSessionMe()).data?.project;
43
+ if (project && shouldCheckConfigConsistency) checkProjectConfigConsistency(project, configuration, appLogger);
44
+ return true;
45
+ } catch (error) {
46
+ appLogger((0, _intlayer_config_utils.extractErrorMessage)(error), { level: "error" });
47
+ return false;
48
+ }
49
+ }
13
50
  if (!(configuration.editor.clientId && configuration.editor.clientSecret)) {
14
51
  appLogger([
15
- "CMS auth not provided. You can either retreive the CMS access key on",
16
- (0, _intlayer_config_logger.colorize)("https://intlayer.org/dahboard", _intlayer_config_colors.GREY),
52
+ "CMS auth not provided. Run",
53
+ (0, _intlayer_config_logger.colorize)("npx intlayer login", _intlayer_config_colors.CYAN),
54
+ "to authenticate, or set",
55
+ (0, _intlayer_config_logger.colorize)("INTLAYER_CLIENT_ID", _intlayer_config_colors.GREY_LIGHT),
56
+ "and",
57
+ (0, _intlayer_config_logger.colorize)("INTLAYER_CLIENT_SECRET", _intlayer_config_colors.GREY_LIGHT),
58
+ "in your .env file",
17
59
  (0, _intlayer_config_logger.colorize)("(see doc:", _intlayer_config_colors.GREY_DARK),
18
60
  (0, _intlayer_config_logger.colorize)("https://intlayer.org/doc/concept/cms", _intlayer_config_colors.GREY),
19
61
  (0, _intlayer_config_logger.colorize)(")", _intlayer_config_colors.GREY_DARK),
@@ -28,27 +70,7 @@ const checkCMSAuth = async (configuration, shouldCheckConfigConsistency = true)
28
70
  appLogger("Project not found");
29
71
  return true;
30
72
  }
31
- if (project.configuration && shouldCheckConfigConsistency) try {
32
- let remoteConfigToCheck = project.configuration;
33
- if (remoteConfigToCheck.ai && "apiKeyConfigured" in remoteConfigToCheck.ai) {
34
- const { apiKeyConfigured, ...restAi } = remoteConfigToCheck.ai;
35
- remoteConfigToCheck = {
36
- ...remoteConfigToCheck,
37
- ai: restAi
38
- };
39
- }
40
- require_utils_checkConfigConsistency.checkConfigConsistency(remoteConfigToCheck, configuration);
41
- } catch {
42
- appLogger([
43
- "Remote configuration is not up to date. The project configuration does not match the local configuration.",
44
- "You can push the configuration by running",
45
- (0, _intlayer_config_logger.colorize)("npx intlayer configuration push", _intlayer_config_colors.CYAN),
46
- (0, _intlayer_config_logger.colorize)("(see doc:", _intlayer_config_colors.GREY_DARK),
47
- (0, _intlayer_config_logger.colorize)("https://intlayer.org/doc/concept/cli/push", _intlayer_config_colors.GREY),
48
- (0, _intlayer_config_logger.colorize)(")", _intlayer_config_colors.GREY_DARK),
49
- "."
50
- ], { level: "warn" });
51
- }
73
+ if (shouldCheckConfigConsistency) checkProjectConfigConsistency(project, configuration, appLogger);
52
74
  } catch (error) {
53
75
  appLogger((0, _intlayer_config_utils.extractErrorMessage)(error), { level: "error" });
54
76
  return false;
@@ -58,16 +80,15 @@ const checkCMSAuth = async (configuration, shouldCheckConfigConsistency = true)
58
80
  const checkAIAccess = async (configuration, aiOptions, shouldCheckConfigConsistency = true) => {
59
81
  const appLogger = (0, _intlayer_config_logger.getAppLogger)(configuration);
60
82
  const hasCMSAuth = Boolean(configuration.editor.clientId && configuration.editor.clientSecret);
83
+ const sessionData = await require_auth_sessionToken.readCliSessionToken(configuration);
84
+ const hasSessionToken = Boolean(sessionData);
61
85
  const isOllama = configuration.ai?.provider === "ollama" || aiOptions?.provider === "ollama";
62
86
  if (Boolean(configuration.ai?.apiKey || aiOptions?.apiKey) || isOllama) return true;
63
- if (!hasCMSAuth) {
87
+ if (!hasCMSAuth && !hasSessionToken) {
64
88
  appLogger([
65
- "AI options or API key not provided. You can either retreive the CMS access key on",
66
- (0, _intlayer_config_logger.colorize)("https://intlayer.org/dahboard", _intlayer_config_colors.GREY),
67
- (0, _intlayer_config_logger.colorize)("(see doc:", _intlayer_config_colors.GREY_DARK),
68
- (0, _intlayer_config_logger.colorize)("https://intlayer.org/doc/concept/cms", _intlayer_config_colors.GREY),
69
- (0, _intlayer_config_logger.colorize)(")", _intlayer_config_colors.GREY_DARK),
70
- ". Alternatively, you can add your own OpenAI API key in the settings",
89
+ "AI options or API key not provided. Run",
90
+ (0, _intlayer_config_logger.colorize)("npx intlayer login", _intlayer_config_colors.CYAN),
91
+ "to authenticate, or provide an API key",
71
92
  (0, _intlayer_config_logger.colorize)("(see doc:", _intlayer_config_colors.GREY_DARK),
72
93
  (0, _intlayer_config_logger.colorize)("https://intlayer.org/doc/concept/configuration", _intlayer_config_colors.GREY),
73
94
  (0, _intlayer_config_logger.colorize)(")", _intlayer_config_colors.GREY_DARK),
@@ -1 +1 @@
1
- {"version":3,"file":"checkAccess.cjs","names":["ANSIColors"],"sources":["../../../src/utils/checkAccess.ts"],"sourcesContent":["import type { AIOptions } from '@intlayer/api';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, getAppLogger } from '@intlayer/config/logger';\nimport { extractErrorMessage } from '@intlayer/config/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { checkConfigConsistency } from './checkConfigConsistency';\n\nexport const checkCMSAuth = async (\n configuration: IntlayerConfig,\n shouldCheckConfigConsistency: boolean = true\n): Promise<boolean> => {\n const appLogger = getAppLogger(configuration);\n\n const hasCMSAuth =\n configuration.editor.clientId && configuration.editor.clientSecret;\n if (!hasCMSAuth) {\n appLogger(\n [\n 'CMS auth not provided. You can either retreive the CMS access key on',\n colorize('https://intlayer.org/dahboard', ANSIColors.GREY),\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize('https://intlayer.org/doc/concept/cms', ANSIColors.GREY),\n colorize(')', ANSIColors.GREY_DARK),\n '.',\n ],\n {\n level: 'error',\n }\n );\n\n return false;\n }\n const intlayerAPI = getIntlayerAPIProxy(undefined, configuration);\n\n try {\n const result = await intlayerAPI.oAuth.getOAuth2AccessToken();\n\n const project = result.data?.project;\n\n if (!project) {\n appLogger('Project not found');\n\n return true;\n }\n\n if (project.configuration && shouldCheckConfigConsistency) {\n try {\n let remoteConfigToCheck = project.configuration;\n\n // Remove server-side computed flags (apiKeyConfigured)\n // We use destructuring + spread to avoid the 'delete' operator (performance)\n if (\n remoteConfigToCheck.ai &&\n 'apiKeyConfigured' in remoteConfigToCheck.ai\n ) {\n const { apiKeyConfigured, ...restAi } = remoteConfigToCheck.ai as any;\n\n remoteConfigToCheck = {\n ...remoteConfigToCheck,\n ai: restAi,\n };\n }\n\n // Recursively check if project.configuration (subset) matches configuration (superset)\n checkConfigConsistency(remoteConfigToCheck, configuration);\n } catch {\n appLogger(\n [\n 'Remote configuration is not up to date. The project configuration does not match the local configuration.',\n 'You can push the configuration by running',\n colorize('npx intlayer configuration push', ANSIColors.CYAN),\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize(\n 'https://intlayer.org/doc/concept/cli/push',\n ANSIColors.GREY\n ),\n colorize(')', ANSIColors.GREY_DARK),\n '.',\n ],\n {\n level: 'warn',\n }\n );\n }\n }\n } catch (error) {\n const message = extractErrorMessage(error);\n\n appLogger(message, {\n level: 'error',\n });\n return false;\n }\n\n return true;\n};\n\nexport const checkAIAccess = async (\n configuration: IntlayerConfig,\n aiOptions?: AIOptions,\n shouldCheckConfigConsistency: boolean = true\n): Promise<boolean> => {\n const appLogger = getAppLogger(configuration);\n\n const hasCMSAuth = Boolean(\n configuration.editor.clientId && configuration.editor.clientSecret\n );\n const isOllama =\n configuration.ai?.provider === 'ollama' || aiOptions?.provider === 'ollama';\n const hasHisOwnAIAPIKey = Boolean(\n configuration.ai?.apiKey || aiOptions?.apiKey\n );\n\n if (hasHisOwnAIAPIKey || isOllama) {\n return true;\n }\n\n // User need to provide either his own AI API key or the CMS auth\n if (!hasCMSAuth) {\n appLogger(\n [\n 'AI options or API key not provided. You can either retreive the CMS access key on',\n colorize('https://intlayer.org/dahboard', ANSIColors.GREY),\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize('https://intlayer.org/doc/concept/cms', ANSIColors.GREY),\n colorize(')', ANSIColors.GREY_DARK),\n '. Alternatively, you can add your own OpenAI API key in the settings',\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize(\n 'https://intlayer.org/doc/concept/configuration',\n ANSIColors.GREY\n ),\n colorize(')', ANSIColors.GREY_DARK),\n '.',\n ],\n {\n level: 'error',\n }\n );\n\n return false;\n }\n\n // If the user do not have his own AI API key, we need to check the CMS auth\n return await checkCMSAuth(configuration, shouldCheckConfigConsistency);\n};\n"],"mappings":";;;;;;;;;;AAQA,MAAa,eAAe,OAC1B,eACA,+BAAwC,SACnB;CACrB,MAAM,sDAAyB,cAAc;CAI7C,IAAI,EADF,cAAc,OAAO,YAAY,cAAc,OAAO,eACvC;EACf,UACE;GACE;yCACS,iCAAiCA,wBAAW,KAAK;yCACjD,aAAaA,wBAAW,UAAU;yCAClC,wCAAwCA,wBAAW,KAAK;yCACxD,KAAKA,wBAAW,UAAU;GACnC;GACD,EACD,EACE,OAAO,SACR,CACF;EAED,OAAO;;CAET,MAAM,qDAAkC,QAAW,cAAc;CAEjE,IAAI;EAGF,MAAM,WAAU,MAFK,YAAY,MAAM,sBAAsB,EAEtC,MAAM;EAE7B,IAAI,CAAC,SAAS;GACZ,UAAU,oBAAoB;GAE9B,OAAO;;EAGT,IAAI,QAAQ,iBAAiB,8BAC3B,IAAI;GACF,IAAI,sBAAsB,QAAQ;GAIlC,IACE,oBAAoB,MACpB,sBAAsB,oBAAoB,IAC1C;IACA,MAAM,EAAE,kBAAkB,GAAG,WAAW,oBAAoB;IAE5D,sBAAsB;KACpB,GAAG;KACH,IAAI;KACL;;GAIH,4DAAuB,qBAAqB,cAAc;UACpD;GACN,UACE;IACE;IACA;0CACS,mCAAmCA,wBAAW,KAAK;0CACnD,aAAaA,wBAAW,UAAU;0CAEzC,6CACAA,wBAAW,KACZ;0CACQ,KAAKA,wBAAW,UAAU;IACnC;IACD,EACD,EACE,OAAO,QACR,CACF;;UAGE,OAAO;EAGd,0DAFoC,MAEnB,EAAE,EACjB,OAAO,SACR,CAAC;EACF,OAAO;;CAGT,OAAO;;AAGT,MAAa,gBAAgB,OAC3B,eACA,WACA,+BAAwC,SACnB;CACrB,MAAM,sDAAyB,cAAc;CAE7C,MAAM,aAAa,QACjB,cAAc,OAAO,YAAY,cAAc,OAAO,aACvD;CACD,MAAM,WACJ,cAAc,IAAI,aAAa,YAAY,WAAW,aAAa;CAKrE,IAJ0B,QACxB,cAAc,IAAI,UAAU,WAAW,OAGpB,IAAI,UACvB,OAAO;CAIT,IAAI,CAAC,YAAY;EACf,UACE;GACE;yCACS,iCAAiCA,wBAAW,KAAK;yCACjD,aAAaA,wBAAW,UAAU;yCAClC,wCAAwCA,wBAAW,KAAK;yCACxD,KAAKA,wBAAW,UAAU;GACnC;yCACS,aAAaA,wBAAW,UAAU;yCAEzC,kDACAA,wBAAW,KACZ;yCACQ,KAAKA,wBAAW,UAAU;GACnC;GACD,EACD,EACE,OAAO,SACR,CACF;EAED,OAAO;;CAIT,OAAO,MAAM,aAAa,eAAe,6BAA6B"}
1
+ {"version":3,"file":"checkAccess.cjs","names":["ANSIColors","readCliSessionToken"],"sources":["../../../src/utils/checkAccess.ts"],"sourcesContent":["import type { AIOptions } from '@intlayer/api';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, getAppLogger } from '@intlayer/config/logger';\nimport { extractErrorMessage } from '@intlayer/config/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { readCliSessionToken } from '../auth/sessionToken';\nimport { checkConfigConsistency } from './checkConfigConsistency';\n\nconst checkProjectConfigConsistency = (\n project: { configuration?: any } | null | undefined,\n configuration: IntlayerConfig,\n appLogger: ReturnType<typeof getAppLogger>\n): void => {\n if (!project?.configuration) return;\n\n try {\n let remoteConfigToCheck = project.configuration;\n\n if (\n remoteConfigToCheck.ai &&\n 'apiKeyConfigured' in remoteConfigToCheck.ai\n ) {\n const { apiKeyConfigured, ...restAi } = remoteConfigToCheck.ai as any;\n remoteConfigToCheck = { ...remoteConfigToCheck, ai: restAi };\n }\n\n checkConfigConsistency(remoteConfigToCheck, configuration);\n } catch {\n appLogger(\n [\n 'Remote configuration is not up to date. The project configuration does not match the local configuration.',\n 'You can push the configuration by running',\n colorize('npx intlayer configuration push', ANSIColors.CYAN),\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize('https://intlayer.org/doc/concept/cli/push', ANSIColors.GREY),\n colorize(')', ANSIColors.GREY_DARK),\n '.',\n ],\n { level: 'warn' }\n );\n }\n};\n\nexport const checkCMSAuth = async (\n configuration: IntlayerConfig,\n shouldCheckConfigConsistency: boolean = true\n): Promise<boolean> => {\n const appLogger = getAppLogger(configuration);\n\n // Try CLI session token first (short-lived 2h token stored in tempDir)\n const sessionData = await readCliSessionToken(configuration);\n if (sessionData) {\n const intlayerAPI = getIntlayerAPIProxy(\n undefined,\n configuration,\n sessionData.token\n );\n\n try {\n const result = await intlayerAPI.oAuth.getCliSessionMe();\n const project = result.data?.project;\n\n if (project && shouldCheckConfigConsistency) {\n checkProjectConfigConsistency(project, configuration, appLogger);\n }\n\n return true;\n } catch (error) {\n const message = extractErrorMessage(error);\n appLogger(message, { level: 'error' });\n return false;\n }\n }\n\n // Fall back to client_credentials (access key)\n const hasCMSAuth =\n configuration.editor.clientId && configuration.editor.clientSecret;\n if (!hasCMSAuth) {\n appLogger(\n [\n 'CMS auth not provided. Run',\n colorize('npx intlayer login', ANSIColors.CYAN),\n 'to authenticate, or set',\n colorize('INTLAYER_CLIENT_ID', ANSIColors.GREY_LIGHT),\n 'and',\n colorize('INTLAYER_CLIENT_SECRET', ANSIColors.GREY_LIGHT),\n 'in your .env file',\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize('https://intlayer.org/doc/concept/cms', ANSIColors.GREY),\n colorize(')', ANSIColors.GREY_DARK),\n '.',\n ],\n { level: 'error' }\n );\n\n return false;\n }\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, configuration);\n\n try {\n const result = await intlayerAPI.oAuth.getOAuth2AccessToken();\n\n const project = result.data?.project;\n\n if (!project) {\n appLogger('Project not found');\n return true;\n }\n\n if (shouldCheckConfigConsistency) {\n checkProjectConfigConsistency(project, configuration, appLogger);\n }\n } catch (error) {\n const message = extractErrorMessage(error);\n appLogger(message, { level: 'error' });\n return false;\n }\n\n return true;\n};\n\nexport const checkAIAccess = async (\n configuration: IntlayerConfig,\n aiOptions?: AIOptions,\n shouldCheckConfigConsistency: boolean = true\n): Promise<boolean> => {\n const appLogger = getAppLogger(configuration);\n\n const hasCMSAuth = Boolean(\n configuration.editor.clientId && configuration.editor.clientSecret\n );\n\n const sessionData = await readCliSessionToken(configuration);\n const hasSessionToken = Boolean(sessionData);\n const isOllama =\n configuration.ai?.provider === 'ollama' || aiOptions?.provider === 'ollama';\n const hasHisOwnAIAPIKey = Boolean(\n configuration.ai?.apiKey || aiOptions?.apiKey\n );\n\n if (hasHisOwnAIAPIKey || isOllama) {\n return true;\n }\n\n // User need to provide either his own AI API key or the CMS auth\n if (!hasCMSAuth && !hasSessionToken) {\n appLogger(\n [\n 'AI options or API key not provided. Run',\n colorize('npx intlayer login', ANSIColors.CYAN),\n 'to authenticate, or provide an API key',\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize(\n 'https://intlayer.org/doc/concept/configuration',\n ANSIColors.GREY\n ),\n colorize(')', ANSIColors.GREY_DARK),\n '.',\n ],\n { level: 'error' }\n );\n\n return false;\n }\n\n // If the user do not have his own AI API key, we need to check the CMS auth\n return await checkCMSAuth(configuration, shouldCheckConfigConsistency);\n};\n"],"mappings":";;;;;;;;;;;AASA,MAAM,iCACJ,SACA,eACA,cACS;CACT,IAAI,CAAC,SAAS,eAAe;CAE7B,IAAI;EACF,IAAI,sBAAsB,QAAQ;EAElC,IACE,oBAAoB,MACpB,sBAAsB,oBAAoB,IAC1C;GACA,MAAM,EAAE,kBAAkB,GAAG,WAAW,oBAAoB;GAC5D,sBAAsB;IAAE,GAAG;IAAqB,IAAI;IAAQ;;EAG9D,4DAAuB,qBAAqB,cAAc;SACpD;EACN,UACE;GACE;GACA;yCACS,mCAAmCA,wBAAW,KAAK;yCACnD,aAAaA,wBAAW,UAAU;yCAClC,6CAA6CA,wBAAW,KAAK;yCAC7D,KAAKA,wBAAW,UAAU;GACnC;GACD,EACD,EAAE,OAAO,QAAQ,CAClB;;;AAIL,MAAa,eAAe,OAC1B,eACA,+BAAwC,SACnB;CACrB,MAAM,sDAAyB,cAAc;CAG7C,MAAM,cAAc,MAAMC,8CAAoB,cAAc;CAC5D,IAAI,aAAa;EACf,MAAM,qDACJ,QACA,eACA,YAAY,MACb;EAED,IAAI;GAEF,MAAM,WAAU,MADK,YAAY,MAAM,iBAAiB,EACjC,MAAM;GAE7B,IAAI,WAAW,8BACb,8BAA8B,SAAS,eAAe,UAAU;GAGlE,OAAO;WACA,OAAO;GAEd,0DADoC,MACnB,EAAE,EAAE,OAAO,SAAS,CAAC;GACtC,OAAO;;;CAOX,IAAI,EADF,cAAc,OAAO,YAAY,cAAc,OAAO,eACvC;EACf,UACE;GACE;yCACS,sBAAsBD,wBAAW,KAAK;GAC/C;yCACS,sBAAsBA,wBAAW,WAAW;GACrD;yCACS,0BAA0BA,wBAAW,WAAW;GACzD;yCACS,aAAaA,wBAAW,UAAU;yCAClC,wCAAwCA,wBAAW,KAAK;yCACxD,KAAKA,wBAAW,UAAU;GACnC;GACD,EACD,EAAE,OAAO,SAAS,CACnB;EAED,OAAO;;CAGT,MAAM,qDAAkC,QAAW,cAAc;CAEjE,IAAI;EAGF,MAAM,WAAU,MAFK,YAAY,MAAM,sBAAsB,EAEtC,MAAM;EAE7B,IAAI,CAAC,SAAS;GACZ,UAAU,oBAAoB;GAC9B,OAAO;;EAGT,IAAI,8BACF,8BAA8B,SAAS,eAAe,UAAU;UAE3D,OAAO;EAEd,0DADoC,MACnB,EAAE,EAAE,OAAO,SAAS,CAAC;EACtC,OAAO;;CAGT,OAAO;;AAGT,MAAa,gBAAgB,OAC3B,eACA,WACA,+BAAwC,SACnB;CACrB,MAAM,sDAAyB,cAAc;CAE7C,MAAM,aAAa,QACjB,cAAc,OAAO,YAAY,cAAc,OAAO,aACvD;CAED,MAAM,cAAc,MAAMC,8CAAoB,cAAc;CAC5D,MAAM,kBAAkB,QAAQ,YAAY;CAC5C,MAAM,WACJ,cAAc,IAAI,aAAa,YAAY,WAAW,aAAa;CAKrE,IAJ0B,QACxB,cAAc,IAAI,UAAU,WAAW,OAGpB,IAAI,UACvB,OAAO;CAIT,IAAI,CAAC,cAAc,CAAC,iBAAiB;EACnC,UACE;GACE;yCACS,sBAAsBD,wBAAW,KAAK;GAC/C;yCACS,aAAaA,wBAAW,UAAU;yCAEzC,kDACAA,wBAAW,KACZ;yCACQ,KAAKA,wBAAW,UAAU;GACnC;GACD,EACD,EAAE,OAAO,SAAS,CACnB;EAED,OAAO;;CAIT,OAAO,MAAM,aAAa,eAAe,6BAA6B"}
@@ -1,4 +1,5 @@
1
1
  import { openBrowser } from "../utils/openBrowser.mjs";
2
+ import { writeCliSessionToken } from "./sessionToken.mjs";
2
3
  import { logConfigDetails } from "@intlayer/chokidar/cli";
3
4
  import * as ANSIColors from "@intlayer/config/colors";
4
5
  import { colorize, colorizePath, getAppLogger } from "@intlayer/config/logger";
@@ -7,13 +8,65 @@ import http from "node:http";
7
8
  import { URL } from "node:url";
8
9
 
9
10
  //#region src/auth/login.ts
11
+ const buildSuccessHtml = (message) => `
12
+ <!DOCTYPE html>
13
+ <html lang="en" data-theme="dark">
14
+ <head>
15
+ <meta charset="UTF-8">
16
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
17
+ <title>Intlayer CLI Login</title>
18
+ <style>
19
+ :root {
20
+ --color-background: rgba(23, 23, 23);
21
+ --color-card: rgba(39, 39, 39);
22
+ --color-text: rgba(255, 245, 237);
23
+ --color-neutral: rgba(93, 93, 93);
24
+ --font-sans: "Inter", -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
25
+ }
26
+ * { box-sizing: border-box; }
27
+ body {
28
+ font-family: var(--font-sans);
29
+ display: flex;
30
+ align-items: center;
31
+ justify-content: center;
32
+ min-height: 100vh;
33
+ margin: 0;
34
+ padding: 1rem;
35
+ background-color: var(--color-background);
36
+ color: var(--color-text);
37
+ }
38
+ .container {
39
+ text-align: center;
40
+ padding: 2rem;
41
+ border-radius: 1rem;
42
+ background-color: var(--color-card);
43
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
44
+ max-width: 400px;
45
+ width: 100%;
46
+ }
47
+ h1 { margin: 0 0 1rem 0; font-size: 1.5rem; font-weight: 700; color: var(--color-text); }
48
+ p { color: var(--color-neutral); font-size: 0.8rem; margin: 0 0 1.5rem 0; line-height: 1.5; }
49
+ </style>
50
+ </head>
51
+ <body>
52
+ <div class="container">
53
+ <h1>Login Successful</h1>
54
+ <p>${message}</p>
55
+ </div>
56
+ <script>
57
+ window.close();
58
+ setTimeout(() => { window.close(); }, 1000);
59
+ <\/script>
60
+ </body>
61
+ </html>
62
+ `;
10
63
  const login = async (options) => {
11
64
  const configuration = getConfiguration(options.configOptions);
12
65
  logConfigDetails(options?.configOptions);
13
66
  const logger = getAppLogger(configuration);
14
67
  const cmsUrl = options.cmsUrl ?? configuration.editor.cmsURL;
15
68
  return new Promise((resolve) => {
16
- const server = http.createServer((req, res) => {
69
+ const server = http.createServer(async (req, res) => {
17
70
  const url = new URL(req.url ?? "", `http://${req.headers.host}`);
18
71
  res.setHeader("Access-Control-Allow-Origin", "*");
19
72
  res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
@@ -24,8 +77,24 @@ const login = async (options) => {
24
77
  return;
25
78
  }
26
79
  if (url.pathname === "/callback") {
80
+ const sessionToken = url.searchParams.get("sessionToken");
81
+ const sessionExpiresAt = url.searchParams.get("expiresAt");
27
82
  const clientId = url.searchParams.get("clientId");
28
83
  const clientSecret = url.searchParams.get("clientSecret");
84
+ if (sessionToken && sessionExpiresAt) {
85
+ logger("");
86
+ logger(`Log in successful. ${colorize("2h", ANSIColors.BLUE)} session token received.`);
87
+ logger("");
88
+ logger(colorize(`Token expires at: ${new Date(sessionExpiresAt).toLocaleString()}`, ANSIColors.GREY));
89
+ await writeCliSessionToken(configuration, sessionToken, new Date(sessionExpiresAt));
90
+ res.writeHead(200, { "Content-Type": "text/html" });
91
+ res.end(buildSuccessHtml("Your 2h session token has been stored. You can now close this tab and return to your terminal."));
92
+ server.close(() => {
93
+ resolve();
94
+ process.exit(0);
95
+ });
96
+ return;
97
+ }
29
98
  if (clientId && clientSecret) {
30
99
  logger("");
31
100
  logger("Log in successful. Client ID and Client Secret received.");
@@ -56,80 +125,7 @@ const login = async (options) => {
56
125
  });
57
126
  logger(colorize("--------------------------------", ANSIColors.GREY_DARK));
58
127
  res.writeHead(200, { "Content-Type": "text/html" });
59
- res.end(`
60
- <!DOCTYPE html>
61
- <html lang="en" data-theme="dark">
62
- <head>
63
- <meta charset="UTF-8">
64
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
65
- <title>Intlayer CLI Login</title>
66
- <style>
67
- :root {
68
- --color-background: rgba(23, 23, 23);
69
- --color-card: rgba(39, 39, 39);
70
- --color-text: rgba(255, 245, 237);
71
- --color-neutral: rgba(93, 93, 93);
72
- --font-sans: "Inter", -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
73
- }
74
-
75
- * {
76
- box-sizing: border-box;
77
- }
78
-
79
- body {
80
- font-family: var(--font-sans);
81
- display: flex;
82
- align-items: center;
83
- justify-content: center;
84
- min-height: 100vh;
85
- margin: 0;
86
- padding: 1rem;
87
- background-color: var(--color-background);
88
- color: var(--color-text);
89
- }
90
-
91
- .container {
92
- text-align: center;
93
- padding: 2rem;
94
- border-radius: 1rem;
95
- background-color: var(--color-card);
96
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
97
- max-width: 400px;
98
- width: 100%;
99
- }
100
-
101
- h1 {
102
- margin: 0 0 1rem 0;
103
- font-size: 1.5rem;
104
- font-weight: 700;
105
- color: var(--color-text);
106
- }
107
-
108
- p {
109
- color: var(--color-neutral);
110
- font-size: 0.8rem;
111
- margin: 0 0 1.5rem 0;
112
- line-height: 1.5;
113
- }
114
- </style>
115
- </head>
116
- <body>
117
- <div class="container">
118
- <h1>Login Successful</h1>
119
- <p>You have successfully logged in to Intlayer CLI. You can now close this tab and return to your terminal.</p>
120
- </div>
121
- <script>
122
- // Attempt to close the window
123
- window.close();
124
-
125
- // Fallback: if window.close() doesn't work, show a message
126
- setTimeout(() => {
127
- window.close();
128
- }, 1000);
129
- <\/script>
130
- </body>
131
- </html>
132
- `);
128
+ res.end(buildSuccessHtml("You have successfully logged in to Intlayer CLI. You can now close this tab and return to your terminal."));
133
129
  server.close(() => {
134
130
  resolve();
135
131
  process.exit(0);
@@ -1 +1 @@
1
- {"version":3,"file":"login.mjs","names":[],"sources":["../../../src/auth/login.ts"],"sourcesContent":["import http from 'node:http';\nimport { URL } from 'node:url';\nimport { logConfigDetails } from '@intlayer/chokidar/cli';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, colorizePath, getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { openBrowser } from '../utils/openBrowser';\n\ntype LoginOptions = {\n cmsUrl?: string;\n configOptions?: GetConfigurationOptions;\n};\n\nexport const login = async (options: LoginOptions) => {\n const configuration = getConfiguration(options.configOptions);\n logConfigDetails(options?.configOptions);\n\n const logger = getAppLogger(configuration);\n\n const cmsUrl = options.cmsUrl ?? configuration.editor.cmsURL;\n\n return new Promise<void>((resolve) => {\n const server = http.createServer((req, res) => {\n const url = new URL(req.url ?? '', `http://${req.headers.host}`);\n\n // Set CORS headers\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (url.pathname === '/callback') {\n const clientId = url.searchParams.get('clientId');\n const clientSecret = url.searchParams.get('clientSecret');\n\n if (clientId && clientSecret) {\n logger('');\n logger('Log in successful. Client ID and Client Secret received.');\n\n logger('');\n logger([\n '1. Insert the Client ID and Client Secret in your',\n colorizePath('.env'),\n 'file:',\n ]);\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n logger(\n [\n colorize('INTLAYER_CLIENT_ID=', ANSIColors.GREY_LIGHT),\n colorize(clientId, ANSIColors.BLUE),\n ].join('')\n );\n logger(\n [\n colorize('INTLAYER_CLIENT_SECRET=', ANSIColors.GREY_LIGHT),\n colorize(clientSecret, ANSIColors.BLUE),\n ].join('')\n );\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n logger('');\n logger('2. Insert in your Intlayer configuration file:');\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n [\n `${ANSIColors.GREY_LIGHT}{`,\n ` editor: {`,\n ` enabled: true,`,\n ` cmsURL: '${colorizePath(cmsUrl!, undefined, ANSIColors.GREY_LIGHT)}',`,\n ` clientId: '${colorize('process.env.INTLAYER_CLIENT_ID', ANSIColors.BLUE, ANSIColors.GREY_LIGHT)}',`,\n ` clientSecret: '${colorize('process.env.INTLAYER_CLIENT_SECRET', ANSIColors.BLUE, ANSIColors.GREY_LIGHT)}',`,\n ` },`,\n `}`,\n ].forEach((line) => {\n logger(line);\n });\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(`\n <!DOCTYPE html>\n <html lang=\"en\" data-theme=\"dark\">\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Intlayer CLI Login</title>\n <style>\n :root {\n --color-background: rgba(23, 23, 23);\n --color-card: rgba(39, 39, 39);\n --color-text: rgba(255, 245, 237);\n --color-neutral: rgba(93, 93, 93);\n --font-sans: \"Inter\", -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;\n }\n \n * {\n box-sizing: border-box;\n }\n \n body {\n font-family: var(--font-sans);\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n margin: 0;\n padding: 1rem;\n background-color: var(--color-background);\n color: var(--color-text);\n }\n \n .container {\n text-align: center;\n padding: 2rem;\n border-radius: 1rem;\n background-color: var(--color-card);\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n max-width: 400px;\n width: 100%;\n }\n \n h1 {\n margin: 0 0 1rem 0;\n font-size: 1.5rem;\n font-weight: 700;\n color: var(--color-text);\n }\n \n p {\n color: var(--color-neutral);\n font-size: 0.8rem;\n margin: 0 0 1.5rem 0;\n line-height: 1.5;\n }\n </style>\n </head>\n <body>\n <div class=\"container\">\n <h1>Login Successful</h1>\n <p>You have successfully logged in to Intlayer CLI. You can now close this tab and return to your terminal.</p>\n </div>\n <script>\n // Attempt to close the window\n window.close();\n \n // Fallback: if window.close() doesn't work, show a message\n setTimeout(() => {\n window.close();\n }, 1000);\n </script>\n </body>\n </html>\n `);\n\n server.close(() => {\n resolve();\n process.exit(0);\n });\n } else {\n res.writeHead(400, { 'Content-Type': 'text/plain' });\n res.end('Missing parameters');\n }\n } else {\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('Not found');\n }\n });\n\n server.listen(0, () => {\n const address = server.address();\n const port = typeof address === 'object' && address ? address.port : 0;\n const state = Math.random().toString(36).substring(7);\n\n const websiteUrl =\n cmsUrl ?? process.env.INTLAYER_SITE_URL ?? 'http://localhost:3000';\n const loginUrl = `${websiteUrl}/auth/cli-login?port=${port}&state=${state}`;\n\n logger('Opening browser for login...');\n logger(`If browser does not open, visit: ${colorizePath(loginUrl)}`);\n\n openBrowser(loginUrl);\n });\n });\n};\n"],"mappings":";;;;;;;;;AAgBA,MAAa,QAAQ,OAAO,YAA0B;CACpD,MAAM,gBAAgB,iBAAiB,QAAQ,cAAc;CAC7D,iBAAiB,SAAS,cAAc;CAExC,MAAM,SAAS,aAAa,cAAc;CAE1C,MAAM,SAAS,QAAQ,UAAU,cAAc,OAAO;CAEtD,OAAO,IAAI,SAAe,YAAY;EACpC,MAAM,SAAS,KAAK,cAAc,KAAK,QAAQ;GAC7C,MAAM,MAAM,IAAI,IAAI,IAAI,OAAO,IAAI,UAAU,IAAI,QAAQ,OAAO;GAGhE,IAAI,UAAU,+BAA+B,IAAI;GACjD,IAAI,UAAU,gCAAgC,eAAe;GAC7D,IAAI,UAAU,gCAAgC,eAAe;GAE7D,IAAI,IAAI,WAAW,WAAW;IAC5B,IAAI,UAAU,IAAI;IAClB,IAAI,KAAK;IACT;;GAGF,IAAI,IAAI,aAAa,aAAa;IAChC,MAAM,WAAW,IAAI,aAAa,IAAI,WAAW;IACjD,MAAM,eAAe,IAAI,aAAa,IAAI,eAAe;IAEzD,IAAI,YAAY,cAAc;KAC5B,OAAO,GAAG;KACV,OAAO,2DAA2D;KAElE,OAAO,GAAG;KACV,OAAO;MACL;MACA,aAAa,OAAO;MACpB;MACD,CAAC;KACF,OACE,SAAS,oCAAoC,WAAW,UAAU,CACnE;KACD,OACE,CACE,SAAS,uBAAuB,WAAW,WAAW,EACtD,SAAS,UAAU,WAAW,KAAK,CACpC,CAAC,KAAK,GAAG,CACX;KACD,OACE,CACE,SAAS,2BAA2B,WAAW,WAAW,EAC1D,SAAS,cAAc,WAAW,KAAK,CACxC,CAAC,KAAK,GAAG,CACX;KACD,OACE,SAAS,oCAAoC,WAAW,UAAU,CACnE;KACD,OAAO,GAAG;KACV,OAAO,iDAAiD;KACxD,OACE,SAAS,oCAAoC,WAAW,UAAU,CACnE;KACD;MACE,GAAG,WAAW,WAAW;MACzB;MACA;MACA,iBAAiB,aAAa,QAAS,QAAW,WAAW,WAAW,CAAC;MACzE,mBAAmB,SAAS,kCAAkC,WAAW,MAAM,WAAW,WAAW,CAAC;MACtG,uBAAuB,SAAS,sCAAsC,WAAW,MAAM,WAAW,WAAW,CAAC;MAC9G;MACA;MACD,CAAC,SAAS,SAAS;MAClB,OAAO,KAAK;OACZ;KACF,OACE,SAAS,oCAAoC,WAAW,UAAU,CACnE;KAED,IAAI,UAAU,KAAK,EAAE,gBAAgB,aAAa,CAAC;KACnD,IAAI,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAyEN;KAEF,OAAO,YAAY;MACjB,SAAS;MACT,QAAQ,KAAK,EAAE;OACf;WACG;KACL,IAAI,UAAU,KAAK,EAAE,gBAAgB,cAAc,CAAC;KACpD,IAAI,IAAI,qBAAqB;;UAE1B;IACL,IAAI,UAAU,KAAK,EAAE,gBAAgB,cAAc,CAAC;IACpD,IAAI,IAAI,YAAY;;IAEtB;EAEF,OAAO,OAAO,SAAS;GACrB,MAAM,UAAU,OAAO,SAAS;GAChC,MAAM,OAAO,OAAO,YAAY,YAAY,UAAU,QAAQ,OAAO;GACrE,MAAM,QAAQ,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,UAAU,EAAE;GAIrD,MAAM,WAAW,GADf,UAAU,QAAQ,IAAI,qBAAqB,wBACd,uBAAuB,KAAK,SAAS;GAEpE,OAAO,+BAA+B;GACtC,OAAO,oCAAoC,aAAa,SAAS,GAAG;GAEpE,YAAY,SAAS;IACrB;GACF"}
1
+ {"version":3,"file":"login.mjs","names":[],"sources":["../../../src/auth/login.ts"],"sourcesContent":["import http from 'node:http';\nimport { relative } from 'node:path';\nimport { URL } from 'node:url';\nimport { logConfigDetails } from '@intlayer/chokidar/cli';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, colorizePath, getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { openBrowser } from '../utils/openBrowser';\nimport { writeCliSessionToken } from './sessionToken';\n\nconst buildSuccessHtml = (message: string): string => `\n <!DOCTYPE html>\n <html lang=\"en\" data-theme=\"dark\">\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Intlayer CLI Login</title>\n <style>\n :root {\n --color-background: rgba(23, 23, 23);\n --color-card: rgba(39, 39, 39);\n --color-text: rgba(255, 245, 237);\n --color-neutral: rgba(93, 93, 93);\n --font-sans: \"Inter\", -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;\n }\n * { box-sizing: border-box; }\n body {\n font-family: var(--font-sans);\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n margin: 0;\n padding: 1rem;\n background-color: var(--color-background);\n color: var(--color-text);\n }\n .container {\n text-align: center;\n padding: 2rem;\n border-radius: 1rem;\n background-color: var(--color-card);\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n max-width: 400px;\n width: 100%;\n }\n h1 { margin: 0 0 1rem 0; font-size: 1.5rem; font-weight: 700; color: var(--color-text); }\n p { color: var(--color-neutral); font-size: 0.8rem; margin: 0 0 1.5rem 0; line-height: 1.5; }\n </style>\n </head>\n <body>\n <div class=\"container\">\n <h1>Login Successful</h1>\n <p>${message}</p>\n </div>\n <script>\n window.close();\n setTimeout(() => { window.close(); }, 1000);\n </script>\n </body>\n </html>\n`;\n\ntype LoginOptions = {\n cmsUrl?: string;\n configOptions?: GetConfigurationOptions;\n};\n\nexport const login = async (options: LoginOptions) => {\n const configuration = getConfiguration(options.configOptions);\n logConfigDetails(options?.configOptions);\n\n const logger = getAppLogger(configuration);\n\n const cmsUrl = options.cmsUrl ?? configuration.editor.cmsURL;\n\n return new Promise<void>((resolve) => {\n const server = http.createServer(async (req, res) => {\n const url = new URL(req.url ?? '', `http://${req.headers.host}`);\n\n // Set CORS headers\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (url.pathname === '/callback') {\n const sessionToken = url.searchParams.get('sessionToken');\n const sessionExpiresAt = url.searchParams.get('expiresAt');\n const clientId = url.searchParams.get('clientId');\n const clientSecret = url.searchParams.get('clientSecret');\n\n if (sessionToken && sessionExpiresAt) {\n logger('');\n logger(\n `Log in successful. ${colorize('2h', ANSIColors.BLUE)} session token received.`\n );\n logger('');\n\n logger(\n colorize(\n `Token expires at: ${new Date(sessionExpiresAt).toLocaleString()}`,\n ANSIColors.GREY\n )\n );\n\n await writeCliSessionToken(\n configuration,\n sessionToken,\n new Date(sessionExpiresAt)\n );\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(\n buildSuccessHtml(\n 'Your 2h session token has been stored. You can now close this tab and return to your terminal.'\n )\n );\n\n server.close(() => {\n resolve();\n process.exit(0);\n });\n return;\n }\n\n if (clientId && clientSecret) {\n logger('');\n logger('Log in successful. Client ID and Client Secret received.');\n\n logger('');\n logger([\n '1. Insert the Client ID and Client Secret in your',\n colorizePath('.env'),\n 'file:',\n ]);\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n logger(\n [\n colorize('INTLAYER_CLIENT_ID=', ANSIColors.GREY_LIGHT),\n colorize(clientId, ANSIColors.BLUE),\n ].join('')\n );\n logger(\n [\n colorize('INTLAYER_CLIENT_SECRET=', ANSIColors.GREY_LIGHT),\n colorize(clientSecret, ANSIColors.BLUE),\n ].join('')\n );\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n logger('');\n logger('2. Insert in your Intlayer configuration file:');\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n [\n `${ANSIColors.GREY_LIGHT}{`,\n ` editor: {`,\n ` enabled: true,`,\n ` cmsURL: '${colorizePath(cmsUrl!, undefined, ANSIColors.GREY_LIGHT)}',`,\n ` clientId: '${colorize('process.env.INTLAYER_CLIENT_ID', ANSIColors.BLUE, ANSIColors.GREY_LIGHT)}',`,\n ` clientSecret: '${colorize('process.env.INTLAYER_CLIENT_SECRET', ANSIColors.BLUE, ANSIColors.GREY_LIGHT)}',`,\n ` },`,\n `}`,\n ].forEach((line) => {\n logger(line);\n });\n logger(\n colorize('--------------------------------', ANSIColors.GREY_DARK)\n );\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(\n buildSuccessHtml(\n 'You have successfully logged in to Intlayer CLI. You can now close this tab and return to your terminal.'\n )\n );\n\n server.close(() => {\n resolve();\n process.exit(0);\n });\n } else {\n res.writeHead(400, { 'Content-Type': 'text/plain' });\n res.end('Missing parameters');\n }\n } else {\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('Not found');\n }\n });\n\n server.listen(0, () => {\n const address = server.address();\n const port = typeof address === 'object' && address ? address.port : 0;\n const state = Math.random().toString(36).substring(7);\n\n const websiteUrl =\n cmsUrl ?? process.env.INTLAYER_SITE_URL ?? 'http://localhost:3000';\n const loginUrl = `${websiteUrl}/auth/cli-login?port=${port}&state=${state}`;\n\n logger('Opening browser for login...');\n logger(`If browser does not open, visit: ${colorizePath(loginUrl)}`);\n\n openBrowser(loginUrl);\n });\n });\n};\n"],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { join } from "node:path";
2
+ import { mkdir, readFile, unlink, writeFile } from "node:fs/promises";
3
+
4
+ //#region src/auth/sessionToken.ts
5
+ const SESSION_FILE_NAME = "cli-session.json";
6
+ const getSessionFilePath = (config) => join(config.system.tempDir, SESSION_FILE_NAME);
7
+ const writeCliSessionToken = async (config, token, expiresAt) => {
8
+ const filePath = getSessionFilePath(config);
9
+ await mkdir(config.system.tempDir, { recursive: true });
10
+ const data = {
11
+ token,
12
+ expiresAt: expiresAt.toISOString()
13
+ };
14
+ await writeFile(filePath, JSON.stringify(data, null, 2), { mode: 384 });
15
+ };
16
+ const readCliSessionToken = async (config) => {
17
+ const filePath = getSessionFilePath(config);
18
+ try {
19
+ const raw = await readFile(filePath, "utf8");
20
+ const data = JSON.parse(raw);
21
+ if (!data.token || !data.expiresAt) return null;
22
+ if (/* @__PURE__ */ new Date() >= new Date(data.expiresAt)) {
23
+ await clearCliSessionToken(config);
24
+ return null;
25
+ }
26
+ return data;
27
+ } catch {
28
+ return null;
29
+ }
30
+ };
31
+ const clearCliSessionToken = async (config) => {
32
+ try {
33
+ await unlink(getSessionFilePath(config));
34
+ } catch {}
35
+ };
36
+
37
+ //#endregion
38
+ export { clearCliSessionToken, readCliSessionToken, writeCliSessionToken };
39
+ //# sourceMappingURL=sessionToken.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionToken.mjs","names":[],"sources":["../../../src/auth/sessionToken.ts"],"sourcesContent":["import { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { IntlayerConfig } from '@intlayer/types/config';\n\nconst SESSION_FILE_NAME = 'cli-session.json';\n\ntype CliSessionData = {\n token: string;\n expiresAt: string; // ISO date string\n};\n\nconst getSessionFilePath = (config: IntlayerConfig): string =>\n join(config.system.tempDir, SESSION_FILE_NAME);\n\nexport const writeCliSessionToken = async (\n config: IntlayerConfig,\n token: string,\n expiresAt: Date\n): Promise<void> => {\n const filePath = getSessionFilePath(config);\n\n await mkdir(config.system.tempDir, { recursive: true });\n\n const data: CliSessionData = { token, expiresAt: expiresAt.toISOString() };\n await writeFile(filePath, JSON.stringify(data, null, 2), { mode: 0o600 });\n};\n\nexport const readCliSessionToken = async (\n config: IntlayerConfig\n): Promise<CliSessionData | null> => {\n const filePath = getSessionFilePath(config);\n\n try {\n const raw = await readFile(filePath, 'utf8');\n const data: CliSessionData = JSON.parse(raw);\n\n if (!data.token || !data.expiresAt) {\n return null;\n }\n\n if (new Date() >= new Date(data.expiresAt)) {\n await clearCliSessionToken(config);\n return null;\n }\n\n return data;\n } catch {\n return null;\n }\n};\n\nexport const clearCliSessionToken = async (\n config: IntlayerConfig\n): Promise<void> => {\n try {\n await unlink(getSessionFilePath(config));\n } catch {\n // Ignore errors (file may not exist)\n }\n};\n"],"mappings":";;;;AAIA,MAAM,oBAAoB;AAO1B,MAAM,sBAAsB,WAC1B,KAAK,OAAO,OAAO,SAAS,kBAAkB;AAEhD,MAAa,uBAAuB,OAClC,QACA,OACA,cACkB;CAClB,MAAM,WAAW,mBAAmB,OAAO;CAE3C,MAAM,MAAM,OAAO,OAAO,SAAS,EAAE,WAAW,MAAM,CAAC;CAEvD,MAAM,OAAuB;EAAE;EAAO,WAAW,UAAU,aAAa;EAAE;CAC1E,MAAM,UAAU,UAAU,KAAK,UAAU,MAAM,MAAM,EAAE,EAAE,EAAE,MAAM,KAAO,CAAC;;AAG3E,MAAa,sBAAsB,OACjC,WACmC;CACnC,MAAM,WAAW,mBAAmB,OAAO;CAE3C,IAAI;EACF,MAAM,MAAM,MAAM,SAAS,UAAU,OAAO;EAC5C,MAAM,OAAuB,KAAK,MAAM,IAAI;EAE5C,IAAI,CAAC,KAAK,SAAS,CAAC,KAAK,WACvB,OAAO;EAGT,oBAAI,IAAI,MAAM,IAAI,IAAI,KAAK,KAAK,UAAU,EAAE;GAC1C,MAAM,qBAAqB,OAAO;GAClC,OAAO;;EAGT,OAAO;SACD;EACN,OAAO;;;AAIX,MAAa,uBAAuB,OAClC,WACkB;CAClB,IAAI;EACF,MAAM,OAAO,mBAAmB,OAAO,CAAC;SAClC"}
@@ -1,3 +1,4 @@
1
+ import { readCliSessionToken } from "../auth/sessionToken.mjs";
1
2
  import { checkConfigConsistency } from "./checkConfigConsistency.mjs";
2
3
  import * as ANSIColors from "@intlayer/config/colors";
3
4
  import { colorize, getAppLogger } from "@intlayer/config/logger";
@@ -5,12 +6,53 @@ import { getIntlayerAPIProxy } from "@intlayer/api";
5
6
  import { extractErrorMessage } from "@intlayer/config/utils";
6
7
 
7
8
  //#region src/utils/checkAccess.ts
9
+ const checkProjectConfigConsistency = (project, configuration, appLogger) => {
10
+ if (!project?.configuration) return;
11
+ try {
12
+ let remoteConfigToCheck = project.configuration;
13
+ if (remoteConfigToCheck.ai && "apiKeyConfigured" in remoteConfigToCheck.ai) {
14
+ const { apiKeyConfigured, ...restAi } = remoteConfigToCheck.ai;
15
+ remoteConfigToCheck = {
16
+ ...remoteConfigToCheck,
17
+ ai: restAi
18
+ };
19
+ }
20
+ checkConfigConsistency(remoteConfigToCheck, configuration);
21
+ } catch {
22
+ appLogger([
23
+ "Remote configuration is not up to date. The project configuration does not match the local configuration.",
24
+ "You can push the configuration by running",
25
+ colorize("npx intlayer configuration push", ANSIColors.CYAN),
26
+ colorize("(see doc:", ANSIColors.GREY_DARK),
27
+ colorize("https://intlayer.org/doc/concept/cli/push", ANSIColors.GREY),
28
+ colorize(")", ANSIColors.GREY_DARK),
29
+ "."
30
+ ], { level: "warn" });
31
+ }
32
+ };
8
33
  const checkCMSAuth = async (configuration, shouldCheckConfigConsistency = true) => {
9
34
  const appLogger = getAppLogger(configuration);
35
+ const sessionData = await readCliSessionToken(configuration);
36
+ if (sessionData) {
37
+ const intlayerAPI = getIntlayerAPIProxy(void 0, configuration, sessionData.token);
38
+ try {
39
+ const project = (await intlayerAPI.oAuth.getCliSessionMe()).data?.project;
40
+ if (project && shouldCheckConfigConsistency) checkProjectConfigConsistency(project, configuration, appLogger);
41
+ return true;
42
+ } catch (error) {
43
+ appLogger(extractErrorMessage(error), { level: "error" });
44
+ return false;
45
+ }
46
+ }
10
47
  if (!(configuration.editor.clientId && configuration.editor.clientSecret)) {
11
48
  appLogger([
12
- "CMS auth not provided. You can either retreive the CMS access key on",
13
- colorize("https://intlayer.org/dahboard", ANSIColors.GREY),
49
+ "CMS auth not provided. Run",
50
+ colorize("npx intlayer login", ANSIColors.CYAN),
51
+ "to authenticate, or set",
52
+ colorize("INTLAYER_CLIENT_ID", ANSIColors.GREY_LIGHT),
53
+ "and",
54
+ colorize("INTLAYER_CLIENT_SECRET", ANSIColors.GREY_LIGHT),
55
+ "in your .env file",
14
56
  colorize("(see doc:", ANSIColors.GREY_DARK),
15
57
  colorize("https://intlayer.org/doc/concept/cms", ANSIColors.GREY),
16
58
  colorize(")", ANSIColors.GREY_DARK),
@@ -25,27 +67,7 @@ const checkCMSAuth = async (configuration, shouldCheckConfigConsistency = true)
25
67
  appLogger("Project not found");
26
68
  return true;
27
69
  }
28
- if (project.configuration && shouldCheckConfigConsistency) try {
29
- let remoteConfigToCheck = project.configuration;
30
- if (remoteConfigToCheck.ai && "apiKeyConfigured" in remoteConfigToCheck.ai) {
31
- const { apiKeyConfigured, ...restAi } = remoteConfigToCheck.ai;
32
- remoteConfigToCheck = {
33
- ...remoteConfigToCheck,
34
- ai: restAi
35
- };
36
- }
37
- checkConfigConsistency(remoteConfigToCheck, configuration);
38
- } catch {
39
- appLogger([
40
- "Remote configuration is not up to date. The project configuration does not match the local configuration.",
41
- "You can push the configuration by running",
42
- colorize("npx intlayer configuration push", ANSIColors.CYAN),
43
- colorize("(see doc:", ANSIColors.GREY_DARK),
44
- colorize("https://intlayer.org/doc/concept/cli/push", ANSIColors.GREY),
45
- colorize(")", ANSIColors.GREY_DARK),
46
- "."
47
- ], { level: "warn" });
48
- }
70
+ if (shouldCheckConfigConsistency) checkProjectConfigConsistency(project, configuration, appLogger);
49
71
  } catch (error) {
50
72
  appLogger(extractErrorMessage(error), { level: "error" });
51
73
  return false;
@@ -55,16 +77,15 @@ const checkCMSAuth = async (configuration, shouldCheckConfigConsistency = true)
55
77
  const checkAIAccess = async (configuration, aiOptions, shouldCheckConfigConsistency = true) => {
56
78
  const appLogger = getAppLogger(configuration);
57
79
  const hasCMSAuth = Boolean(configuration.editor.clientId && configuration.editor.clientSecret);
80
+ const sessionData = await readCliSessionToken(configuration);
81
+ const hasSessionToken = Boolean(sessionData);
58
82
  const isOllama = configuration.ai?.provider === "ollama" || aiOptions?.provider === "ollama";
59
83
  if (Boolean(configuration.ai?.apiKey || aiOptions?.apiKey) || isOllama) return true;
60
- if (!hasCMSAuth) {
84
+ if (!hasCMSAuth && !hasSessionToken) {
61
85
  appLogger([
62
- "AI options or API key not provided. You can either retreive the CMS access key on",
63
- colorize("https://intlayer.org/dahboard", ANSIColors.GREY),
64
- colorize("(see doc:", ANSIColors.GREY_DARK),
65
- colorize("https://intlayer.org/doc/concept/cms", ANSIColors.GREY),
66
- colorize(")", ANSIColors.GREY_DARK),
67
- ". Alternatively, you can add your own OpenAI API key in the settings",
86
+ "AI options or API key not provided. Run",
87
+ colorize("npx intlayer login", ANSIColors.CYAN),
88
+ "to authenticate, or provide an API key",
68
89
  colorize("(see doc:", ANSIColors.GREY_DARK),
69
90
  colorize("https://intlayer.org/doc/concept/configuration", ANSIColors.GREY),
70
91
  colorize(")", ANSIColors.GREY_DARK),
@@ -1 +1 @@
1
- {"version":3,"file":"checkAccess.mjs","names":[],"sources":["../../../src/utils/checkAccess.ts"],"sourcesContent":["import type { AIOptions } from '@intlayer/api';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, getAppLogger } from '@intlayer/config/logger';\nimport { extractErrorMessage } from '@intlayer/config/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { checkConfigConsistency } from './checkConfigConsistency';\n\nexport const checkCMSAuth = async (\n configuration: IntlayerConfig,\n shouldCheckConfigConsistency: boolean = true\n): Promise<boolean> => {\n const appLogger = getAppLogger(configuration);\n\n const hasCMSAuth =\n configuration.editor.clientId && configuration.editor.clientSecret;\n if (!hasCMSAuth) {\n appLogger(\n [\n 'CMS auth not provided. You can either retreive the CMS access key on',\n colorize('https://intlayer.org/dahboard', ANSIColors.GREY),\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize('https://intlayer.org/doc/concept/cms', ANSIColors.GREY),\n colorize(')', ANSIColors.GREY_DARK),\n '.',\n ],\n {\n level: 'error',\n }\n );\n\n return false;\n }\n const intlayerAPI = getIntlayerAPIProxy(undefined, configuration);\n\n try {\n const result = await intlayerAPI.oAuth.getOAuth2AccessToken();\n\n const project = result.data?.project;\n\n if (!project) {\n appLogger('Project not found');\n\n return true;\n }\n\n if (project.configuration && shouldCheckConfigConsistency) {\n try {\n let remoteConfigToCheck = project.configuration;\n\n // Remove server-side computed flags (apiKeyConfigured)\n // We use destructuring + spread to avoid the 'delete' operator (performance)\n if (\n remoteConfigToCheck.ai &&\n 'apiKeyConfigured' in remoteConfigToCheck.ai\n ) {\n const { apiKeyConfigured, ...restAi } = remoteConfigToCheck.ai as any;\n\n remoteConfigToCheck = {\n ...remoteConfigToCheck,\n ai: restAi,\n };\n }\n\n // Recursively check if project.configuration (subset) matches configuration (superset)\n checkConfigConsistency(remoteConfigToCheck, configuration);\n } catch {\n appLogger(\n [\n 'Remote configuration is not up to date. The project configuration does not match the local configuration.',\n 'You can push the configuration by running',\n colorize('npx intlayer configuration push', ANSIColors.CYAN),\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize(\n 'https://intlayer.org/doc/concept/cli/push',\n ANSIColors.GREY\n ),\n colorize(')', ANSIColors.GREY_DARK),\n '.',\n ],\n {\n level: 'warn',\n }\n );\n }\n }\n } catch (error) {\n const message = extractErrorMessage(error);\n\n appLogger(message, {\n level: 'error',\n });\n return false;\n }\n\n return true;\n};\n\nexport const checkAIAccess = async (\n configuration: IntlayerConfig,\n aiOptions?: AIOptions,\n shouldCheckConfigConsistency: boolean = true\n): Promise<boolean> => {\n const appLogger = getAppLogger(configuration);\n\n const hasCMSAuth = Boolean(\n configuration.editor.clientId && configuration.editor.clientSecret\n );\n const isOllama =\n configuration.ai?.provider === 'ollama' || aiOptions?.provider === 'ollama';\n const hasHisOwnAIAPIKey = Boolean(\n configuration.ai?.apiKey || aiOptions?.apiKey\n );\n\n if (hasHisOwnAIAPIKey || isOllama) {\n return true;\n }\n\n // User need to provide either his own AI API key or the CMS auth\n if (!hasCMSAuth) {\n appLogger(\n [\n 'AI options or API key not provided. You can either retreive the CMS access key on',\n colorize('https://intlayer.org/dahboard', ANSIColors.GREY),\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize('https://intlayer.org/doc/concept/cms', ANSIColors.GREY),\n colorize(')', ANSIColors.GREY_DARK),\n '. Alternatively, you can add your own OpenAI API key in the settings',\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize(\n 'https://intlayer.org/doc/concept/configuration',\n ANSIColors.GREY\n ),\n colorize(')', ANSIColors.GREY_DARK),\n '.',\n ],\n {\n level: 'error',\n }\n );\n\n return false;\n }\n\n // If the user do not have his own AI API key, we need to check the CMS auth\n return await checkCMSAuth(configuration, shouldCheckConfigConsistency);\n};\n"],"mappings":";;;;;;;AAQA,MAAa,eAAe,OAC1B,eACA,+BAAwC,SACnB;CACrB,MAAM,YAAY,aAAa,cAAc;CAI7C,IAAI,EADF,cAAc,OAAO,YAAY,cAAc,OAAO,eACvC;EACf,UACE;GACE;GACA,SAAS,iCAAiC,WAAW,KAAK;GAC1D,SAAS,aAAa,WAAW,UAAU;GAC3C,SAAS,wCAAwC,WAAW,KAAK;GACjE,SAAS,KAAK,WAAW,UAAU;GACnC;GACD,EACD,EACE,OAAO,SACR,CACF;EAED,OAAO;;CAET,MAAM,cAAc,oBAAoB,QAAW,cAAc;CAEjE,IAAI;EAGF,MAAM,WAAU,MAFK,YAAY,MAAM,sBAAsB,EAEtC,MAAM;EAE7B,IAAI,CAAC,SAAS;GACZ,UAAU,oBAAoB;GAE9B,OAAO;;EAGT,IAAI,QAAQ,iBAAiB,8BAC3B,IAAI;GACF,IAAI,sBAAsB,QAAQ;GAIlC,IACE,oBAAoB,MACpB,sBAAsB,oBAAoB,IAC1C;IACA,MAAM,EAAE,kBAAkB,GAAG,WAAW,oBAAoB;IAE5D,sBAAsB;KACpB,GAAG;KACH,IAAI;KACL;;GAIH,uBAAuB,qBAAqB,cAAc;UACpD;GACN,UACE;IACE;IACA;IACA,SAAS,mCAAmC,WAAW,KAAK;IAC5D,SAAS,aAAa,WAAW,UAAU;IAC3C,SACE,6CACA,WAAW,KACZ;IACD,SAAS,KAAK,WAAW,UAAU;IACnC;IACD,EACD,EACE,OAAO,QACR,CACF;;UAGE,OAAO;EAGd,UAFgB,oBAAoB,MAEnB,EAAE,EACjB,OAAO,SACR,CAAC;EACF,OAAO;;CAGT,OAAO;;AAGT,MAAa,gBAAgB,OAC3B,eACA,WACA,+BAAwC,SACnB;CACrB,MAAM,YAAY,aAAa,cAAc;CAE7C,MAAM,aAAa,QACjB,cAAc,OAAO,YAAY,cAAc,OAAO,aACvD;CACD,MAAM,WACJ,cAAc,IAAI,aAAa,YAAY,WAAW,aAAa;CAKrE,IAJ0B,QACxB,cAAc,IAAI,UAAU,WAAW,OAGpB,IAAI,UACvB,OAAO;CAIT,IAAI,CAAC,YAAY;EACf,UACE;GACE;GACA,SAAS,iCAAiC,WAAW,KAAK;GAC1D,SAAS,aAAa,WAAW,UAAU;GAC3C,SAAS,wCAAwC,WAAW,KAAK;GACjE,SAAS,KAAK,WAAW,UAAU;GACnC;GACA,SAAS,aAAa,WAAW,UAAU;GAC3C,SACE,kDACA,WAAW,KACZ;GACD,SAAS,KAAK,WAAW,UAAU;GACnC;GACD,EACD,EACE,OAAO,SACR,CACF;EAED,OAAO;;CAIT,OAAO,MAAM,aAAa,eAAe,6BAA6B"}
1
+ {"version":3,"file":"checkAccess.mjs","names":[],"sources":["../../../src/utils/checkAccess.ts"],"sourcesContent":["import type { AIOptions } from '@intlayer/api';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, getAppLogger } from '@intlayer/config/logger';\nimport { extractErrorMessage } from '@intlayer/config/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { readCliSessionToken } from '../auth/sessionToken';\nimport { checkConfigConsistency } from './checkConfigConsistency';\n\nconst checkProjectConfigConsistency = (\n project: { configuration?: any } | null | undefined,\n configuration: IntlayerConfig,\n appLogger: ReturnType<typeof getAppLogger>\n): void => {\n if (!project?.configuration) return;\n\n try {\n let remoteConfigToCheck = project.configuration;\n\n if (\n remoteConfigToCheck.ai &&\n 'apiKeyConfigured' in remoteConfigToCheck.ai\n ) {\n const { apiKeyConfigured, ...restAi } = remoteConfigToCheck.ai as any;\n remoteConfigToCheck = { ...remoteConfigToCheck, ai: restAi };\n }\n\n checkConfigConsistency(remoteConfigToCheck, configuration);\n } catch {\n appLogger(\n [\n 'Remote configuration is not up to date. The project configuration does not match the local configuration.',\n 'You can push the configuration by running',\n colorize('npx intlayer configuration push', ANSIColors.CYAN),\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize('https://intlayer.org/doc/concept/cli/push', ANSIColors.GREY),\n colorize(')', ANSIColors.GREY_DARK),\n '.',\n ],\n { level: 'warn' }\n );\n }\n};\n\nexport const checkCMSAuth = async (\n configuration: IntlayerConfig,\n shouldCheckConfigConsistency: boolean = true\n): Promise<boolean> => {\n const appLogger = getAppLogger(configuration);\n\n // Try CLI session token first (short-lived 2h token stored in tempDir)\n const sessionData = await readCliSessionToken(configuration);\n if (sessionData) {\n const intlayerAPI = getIntlayerAPIProxy(\n undefined,\n configuration,\n sessionData.token\n );\n\n try {\n const result = await intlayerAPI.oAuth.getCliSessionMe();\n const project = result.data?.project;\n\n if (project && shouldCheckConfigConsistency) {\n checkProjectConfigConsistency(project, configuration, appLogger);\n }\n\n return true;\n } catch (error) {\n const message = extractErrorMessage(error);\n appLogger(message, { level: 'error' });\n return false;\n }\n }\n\n // Fall back to client_credentials (access key)\n const hasCMSAuth =\n configuration.editor.clientId && configuration.editor.clientSecret;\n if (!hasCMSAuth) {\n appLogger(\n [\n 'CMS auth not provided. Run',\n colorize('npx intlayer login', ANSIColors.CYAN),\n 'to authenticate, or set',\n colorize('INTLAYER_CLIENT_ID', ANSIColors.GREY_LIGHT),\n 'and',\n colorize('INTLAYER_CLIENT_SECRET', ANSIColors.GREY_LIGHT),\n 'in your .env file',\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize('https://intlayer.org/doc/concept/cms', ANSIColors.GREY),\n colorize(')', ANSIColors.GREY_DARK),\n '.',\n ],\n { level: 'error' }\n );\n\n return false;\n }\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, configuration);\n\n try {\n const result = await intlayerAPI.oAuth.getOAuth2AccessToken();\n\n const project = result.data?.project;\n\n if (!project) {\n appLogger('Project not found');\n return true;\n }\n\n if (shouldCheckConfigConsistency) {\n checkProjectConfigConsistency(project, configuration, appLogger);\n }\n } catch (error) {\n const message = extractErrorMessage(error);\n appLogger(message, { level: 'error' });\n return false;\n }\n\n return true;\n};\n\nexport const checkAIAccess = async (\n configuration: IntlayerConfig,\n aiOptions?: AIOptions,\n shouldCheckConfigConsistency: boolean = true\n): Promise<boolean> => {\n const appLogger = getAppLogger(configuration);\n\n const hasCMSAuth = Boolean(\n configuration.editor.clientId && configuration.editor.clientSecret\n );\n\n const sessionData = await readCliSessionToken(configuration);\n const hasSessionToken = Boolean(sessionData);\n const isOllama =\n configuration.ai?.provider === 'ollama' || aiOptions?.provider === 'ollama';\n const hasHisOwnAIAPIKey = Boolean(\n configuration.ai?.apiKey || aiOptions?.apiKey\n );\n\n if (hasHisOwnAIAPIKey || isOllama) {\n return true;\n }\n\n // User need to provide either his own AI API key or the CMS auth\n if (!hasCMSAuth && !hasSessionToken) {\n appLogger(\n [\n 'AI options or API key not provided. Run',\n colorize('npx intlayer login', ANSIColors.CYAN),\n 'to authenticate, or provide an API key',\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize(\n 'https://intlayer.org/doc/concept/configuration',\n ANSIColors.GREY\n ),\n colorize(')', ANSIColors.GREY_DARK),\n '.',\n ],\n { level: 'error' }\n );\n\n return false;\n }\n\n // If the user do not have his own AI API key, we need to check the CMS auth\n return await checkCMSAuth(configuration, shouldCheckConfigConsistency);\n};\n"],"mappings":";;;;;;;;AASA,MAAM,iCACJ,SACA,eACA,cACS;CACT,IAAI,CAAC,SAAS,eAAe;CAE7B,IAAI;EACF,IAAI,sBAAsB,QAAQ;EAElC,IACE,oBAAoB,MACpB,sBAAsB,oBAAoB,IAC1C;GACA,MAAM,EAAE,kBAAkB,GAAG,WAAW,oBAAoB;GAC5D,sBAAsB;IAAE,GAAG;IAAqB,IAAI;IAAQ;;EAG9D,uBAAuB,qBAAqB,cAAc;SACpD;EACN,UACE;GACE;GACA;GACA,SAAS,mCAAmC,WAAW,KAAK;GAC5D,SAAS,aAAa,WAAW,UAAU;GAC3C,SAAS,6CAA6C,WAAW,KAAK;GACtE,SAAS,KAAK,WAAW,UAAU;GACnC;GACD,EACD,EAAE,OAAO,QAAQ,CAClB;;;AAIL,MAAa,eAAe,OAC1B,eACA,+BAAwC,SACnB;CACrB,MAAM,YAAY,aAAa,cAAc;CAG7C,MAAM,cAAc,MAAM,oBAAoB,cAAc;CAC5D,IAAI,aAAa;EACf,MAAM,cAAc,oBAClB,QACA,eACA,YAAY,MACb;EAED,IAAI;GAEF,MAAM,WAAU,MADK,YAAY,MAAM,iBAAiB,EACjC,MAAM;GAE7B,IAAI,WAAW,8BACb,8BAA8B,SAAS,eAAe,UAAU;GAGlE,OAAO;WACA,OAAO;GAEd,UADgB,oBAAoB,MACnB,EAAE,EAAE,OAAO,SAAS,CAAC;GACtC,OAAO;;;CAOX,IAAI,EADF,cAAc,OAAO,YAAY,cAAc,OAAO,eACvC;EACf,UACE;GACE;GACA,SAAS,sBAAsB,WAAW,KAAK;GAC/C;GACA,SAAS,sBAAsB,WAAW,WAAW;GACrD;GACA,SAAS,0BAA0B,WAAW,WAAW;GACzD;GACA,SAAS,aAAa,WAAW,UAAU;GAC3C,SAAS,wCAAwC,WAAW,KAAK;GACjE,SAAS,KAAK,WAAW,UAAU;GACnC;GACD,EACD,EAAE,OAAO,SAAS,CACnB;EAED,OAAO;;CAGT,MAAM,cAAc,oBAAoB,QAAW,cAAc;CAEjE,IAAI;EAGF,MAAM,WAAU,MAFK,YAAY,MAAM,sBAAsB,EAEtC,MAAM;EAE7B,IAAI,CAAC,SAAS;GACZ,UAAU,oBAAoB;GAC9B,OAAO;;EAGT,IAAI,8BACF,8BAA8B,SAAS,eAAe,UAAU;UAE3D,OAAO;EAEd,UADgB,oBAAoB,MACnB,EAAE,EAAE,OAAO,SAAS,CAAC;EACtC,OAAO;;CAGT,OAAO;;AAGT,MAAa,gBAAgB,OAC3B,eACA,WACA,+BAAwC,SACnB;CACrB,MAAM,YAAY,aAAa,cAAc;CAE7C,MAAM,aAAa,QACjB,cAAc,OAAO,YAAY,cAAc,OAAO,aACvD;CAED,MAAM,cAAc,MAAM,oBAAoB,cAAc;CAC5D,MAAM,kBAAkB,QAAQ,YAAY;CAC5C,MAAM,WACJ,cAAc,IAAI,aAAa,YAAY,WAAW,aAAa;CAKrE,IAJ0B,QACxB,cAAc,IAAI,UAAU,WAAW,OAGpB,IAAI,UACvB,OAAO;CAIT,IAAI,CAAC,cAAc,CAAC,iBAAiB;EACnC,UACE;GACE;GACA,SAAS,sBAAsB,WAAW,KAAK;GAC/C;GACA,SAAS,aAAa,WAAW,UAAU;GAC3C,SACE,kDACA,WAAW,KACZ;GACD,SAAS,KAAK,WAAW,UAAU;GACnC;GACD,EACD,EAAE,OAAO,SAAS,CACnB;EAED,OAAO;;CAIT,OAAO,MAAM,aAAa,eAAe,6BAA6B"}
@@ -1 +1 @@
1
- {"version":3,"file":"login.d.ts","names":[],"sources":["../../../src/auth/login.ts"],"mappings":";;;KAWK,YAAA;EACH,MAAA;EACA,aAAA,GAAgB,uBAAA;AAAA;AAAA,cAGL,KAAA,GAAe,OAAA,EAAS,YAAA,KAAY,OAAA"}
1
+ {"version":3,"file":"login.d.ts","names":[],"sources":["../../../src/auth/login.ts"],"mappings":";;;KAkEK,YAAA;EACH,MAAA;EACA,aAAA,GAAgB,uBAAA;AAAA;AAAA,cAGL,KAAA,GAAe,OAAA,EAAS,YAAA,KAAY,OAAA"}
@@ -0,0 +1,13 @@
1
+ import { IntlayerConfig } from "@intlayer/types/config";
2
+
3
+ //#region src/auth/sessionToken.d.ts
4
+ type CliSessionData = {
5
+ token: string;
6
+ expiresAt: string;
7
+ };
8
+ declare const writeCliSessionToken: (config: IntlayerConfig, token: string, expiresAt: Date) => Promise<void>;
9
+ declare const readCliSessionToken: (config: IntlayerConfig) => Promise<CliSessionData | null>;
10
+ declare const clearCliSessionToken: (config: IntlayerConfig) => Promise<void>;
11
+ //#endregion
12
+ export { clearCliSessionToken, readCliSessionToken, writeCliSessionToken };
13
+ //# sourceMappingURL=sessionToken.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionToken.d.ts","names":[],"sources":["../../../src/auth/sessionToken.ts"],"mappings":";;;KAMK,cAAA;EACH,KAAA;EACA,SAAA;AAAA;AAAA,cAMW,oBAAA,GACX,MAAA,EAAQ,cAAA,EACR,KAAA,UACA,SAAA,EAAW,IAAA,KACV,OAAA;AAAA,cASU,mBAAA,GACX,MAAA,EAAQ,cAAA,KACP,OAAA,CAAQ,cAAA;AAAA,cAsBE,oBAAA,GACX,MAAA,EAAQ,cAAA,KACP,OAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"checkAccess.d.ts","names":[],"sources":["../../../src/utils/checkAccess.ts"],"mappings":";;;;cAQa,YAAA,GACX,aAAA,EAAe,cAAA,EACf,4BAAA,eACC,OAAA;AAAA,cAuFU,aAAA,GACX,aAAA,EAAe,cAAA,EACf,SAAA,GAAY,SAAA,EACZ,4BAAA,eACC,OAAA"}
1
+ {"version":3,"file":"checkAccess.d.ts","names":[],"sources":["../../../src/utils/checkAccess.ts"],"mappings":";;;;cA4Ca,YAAA,GACX,aAAA,EAAe,cAAA,EACf,4BAAA,eACC,OAAA;AAAA,cA4EU,aAAA,GACX,aAAA,EAAe,cAAA,EACf,SAAA,GAAY,SAAA,EACZ,4BAAA,eACC,OAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intlayer/cli",
3
- "version": "8.9.6-canary.0",
3
+ "version": "8.9.7",
4
4
  "private": false,
5
5
  "description": "Provides uniform command-line interface scripts for Intlayer, used in packages like intlayer-cli and intlayer.",
6
6
  "keywords": [
@@ -67,22 +67,22 @@
67
67
  },
68
68
  "dependencies": {
69
69
  "@clack/prompts": "0.11.0",
70
- "@intlayer/api": "8.9.6-canary.0",
71
- "@intlayer/babel": "8.9.6-canary.0",
72
- "@intlayer/chokidar": "8.9.6-canary.0",
73
- "@intlayer/config": "8.9.6-canary.0",
74
- "@intlayer/core": "8.9.6-canary.0",
75
- "@intlayer/dictionaries-entry": "8.9.6-canary.0",
76
- "@intlayer/remote-dictionaries-entry": "8.9.6-canary.0",
77
- "@intlayer/types": "8.9.6-canary.0",
78
- "@intlayer/unmerged-dictionaries-entry": "8.9.6-canary.0",
70
+ "@intlayer/api": "8.9.7",
71
+ "@intlayer/babel": "8.9.7",
72
+ "@intlayer/chokidar": "8.9.7",
73
+ "@intlayer/config": "8.9.7",
74
+ "@intlayer/core": "8.9.7",
75
+ "@intlayer/dictionaries-entry": "8.9.7",
76
+ "@intlayer/remote-dictionaries-entry": "8.9.7",
77
+ "@intlayer/types": "8.9.7",
78
+ "@intlayer/unmerged-dictionaries-entry": "8.9.7",
79
79
  "commander": "14.0.3",
80
80
  "enquirer": "^2.4.1",
81
81
  "eventsource": "4.1.0",
82
82
  "fast-glob": "3.3.3"
83
83
  },
84
84
  "devDependencies": {
85
- "@intlayer/ai": "8.9.6-canary.0",
85
+ "@intlayer/ai": "8.9.7",
86
86
  "@types/node": "25.8.0",
87
87
  "@utils/ts-config": "1.0.4",
88
88
  "@utils/ts-config-types": "1.0.4",
@@ -93,7 +93,7 @@
93
93
  "vitest": "4.1.6"
94
94
  },
95
95
  "peerDependencies": {
96
- "@intlayer/ai": "8.9.6-canary.0"
96
+ "@intlayer/ai": "8.9.7"
97
97
  },
98
98
  "peerDependenciesMeta": {
99
99
  "@intlayer/ai": {