@lpm-registry/cli 0.46.0 → 0.48.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,10 @@
1
- import fs from "node:fs"
2
- import path from "node:path"
3
- import * as p from "@clack/prompts"
4
- import chalk from "chalk"
5
- import { getRegistryUrl } from "../config.js"
6
- import { exchangeOidcInstallToken } from "../oidc.js"
7
- import { log, printHeader } from "../ui.js"
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import * as p from "@clack/prompts";
4
+ import chalk from "chalk";
5
+ import { getRegistryUrl } from "../config.js";
6
+ import { exchangeOidcInstallToken } from "../oidc.js";
7
+ import { log, printHeader } from "../ui.js";
8
8
 
9
9
  /**
10
10
  * Remove existing LPM config lines from .npmrc content.
@@ -13,21 +13,21 @@ import { log, printHeader } from "../ui.js"
13
13
  * @returns {string}
14
14
  */
15
15
  function removeLpmLines(content) {
16
- return content
17
- .split("\n")
18
- .filter(line => {
19
- return (
20
- !line.includes("@lpm.dev:registry") &&
21
- !line.match(/^registry=.*lpm\.dev/) &&
22
- !line.includes("lpm.dev/api/registry/:_authToken") &&
23
- !line.includes("_authToken=lpm_") &&
24
- // biome-ignore lint/suspicious/noTemplateCurlyInString: matching literal npm token placeholder
25
- !line.includes("_authToken=${LPM_TOKEN}") &&
26
- !line.includes("# LPM Registry")
27
- )
28
- })
29
- .join("\n")
30
- .trim()
16
+ return content
17
+ .split("\n")
18
+ .filter((line) => {
19
+ return (
20
+ !line.includes("@lpm.dev:registry") &&
21
+ !line.match(/^registry=.*lpm\.dev/) &&
22
+ !line.includes("lpm.dev/api/registry/:_authToken") &&
23
+ !line.includes("_authToken=lpm_") &&
24
+ // biome-ignore lint/suspicious/noTemplateCurlyInString: matching literal npm token placeholder
25
+ !line.includes("_authToken=${LPM_TOKEN}") &&
26
+ !line.includes("# LPM Registry")
27
+ );
28
+ })
29
+ .join("\n")
30
+ .trim();
31
31
  }
32
32
 
33
33
  /**
@@ -36,182 +36,186 @@ function removeLpmLines(content) {
36
36
  * @returns {string|null} The custom registry line, or null if none found
37
37
  */
38
38
  function detectCustomRegistry(content) {
39
- return (
40
- content
41
- .split("\n")
42
- .find(
43
- line =>
44
- line.match(/^registry=/) &&
45
- !line.includes("registry.npmjs.org") &&
46
- !line.includes("lpm.dev"),
47
- ) || null
48
- )
39
+ return (
40
+ content
41
+ .split("\n")
42
+ .find(
43
+ (line) =>
44
+ line.match(/^registry=/) &&
45
+ !line.includes("registry.npmjs.org") &&
46
+ !line.includes("lpm.dev"),
47
+ ) || null
48
+ );
49
49
  }
50
50
 
51
51
  /**
52
- * Configure .npmrc for LPM packages.
52
+ * Configure CI/CD .npmrc for LPM packages.
53
53
  *
54
54
  * Default mode: writes ${LPM_TOKEN} placeholder (for CI with env var).
55
55
  * --oidc mode: exchanges CI OIDC token for a real short-lived token (no secrets needed).
56
56
  */
57
57
  export async function setup(options) {
58
- printHeader()
59
-
60
- p.intro(chalk.bgCyan(chalk.black(" lpm setup ")))
61
-
62
- const registryUrl = options?.registry || getRegistryUrl()
63
- const projectRoot = process.cwd()
64
- const npmrcPath = path.join(projectRoot, ".npmrc")
65
-
66
- // Check for package.json
67
- const pkgJsonPath = path.join(projectRoot, "package.json")
68
- if (!fs.existsSync(pkgJsonPath)) {
69
- log.warn("No package.json found. Run this command in your project root.")
70
- }
71
-
72
- // Build registry URL
73
- const fullRegistryUrl = registryUrl.endsWith("/api/registry")
74
- ? registryUrl
75
- : `${registryUrl}/api/registry`
76
- const registryHost = fullRegistryUrl.replace(/^https?:/, "")
77
-
78
- // Default: scoped (only @lpm.dev packages through lpm.dev).
79
- // --proxy: route all npm traffic through lpm.dev (Pro/Org feature).
80
- let useScoped = !options?.proxy
81
-
82
- // ── OIDC mode: exchange CI token for a real short-lived install token ──
83
- if (options?.oidc) {
84
- let oidcToken
85
- try {
86
- oidcToken = await exchangeOidcInstallToken()
87
- } catch (err) {
88
- log.warn(`OIDC exchange failed: ${err.message}`)
89
- // biome-ignore lint/suspicious/noTemplateCurlyInString: literal placeholder name in user-facing message
90
- log.warn("Falling back to ${LPM_TOKEN} environment variable placeholder.")
91
- // Fall through to default mode
92
- }
93
-
94
- if (oidcToken) {
95
- let npmrcContent = ""
96
- if (fs.existsSync(npmrcPath)) {
97
- const existingContent = fs.readFileSync(npmrcPath, "utf8")
98
-
99
- // Detect custom default registry
100
- if (!useScoped) {
101
- const customRegistry = detectCustomRegistry(existingContent)
102
- if (customRegistry) {
103
- log.warn(
104
- `Found existing default registry: ${customRegistry.trim()}`,
105
- )
106
- log.warn("Using --scoped mode to avoid overriding it.")
107
- useScoped = true
108
- }
109
- }
110
-
111
- npmrcContent = removeLpmLines(existingContent)
112
- }
113
-
114
- const registryLine = useScoped
115
- ? `@lpm.dev:registry=${fullRegistryUrl}`
116
- : `registry=${fullRegistryUrl}`
117
-
118
- const lpmConfig = `# LPM Registry (OIDC — expires in 30 minutes)
58
+ printHeader();
59
+
60
+ p.intro(chalk.bgCyan(chalk.black(" lpm setup ci ")));
61
+
62
+ const registryUrl = options?.registry || getRegistryUrl();
63
+ const projectRoot = process.cwd();
64
+ const npmrcPath = path.join(projectRoot, ".npmrc");
65
+
66
+ // Check for package.json
67
+ const pkgJsonPath = path.join(projectRoot, "package.json");
68
+ if (!fs.existsSync(pkgJsonPath)) {
69
+ log.warn("No package.json found. Run this command in your project root.");
70
+ }
71
+
72
+ // Build registry URL
73
+ const fullRegistryUrl = registryUrl.endsWith("/api/registry")
74
+ ? registryUrl
75
+ : `${registryUrl}/api/registry`;
76
+ const registryHost = fullRegistryUrl.replace(/^https?:/, "");
77
+
78
+ // Default: scoped (only @lpm.dev packages through lpm.dev).
79
+ // --proxy: route all npm traffic through lpm.dev (Pro/Org feature).
80
+ let useScoped = !options?.proxy;
81
+
82
+ // ── OIDC mode: exchange CI token for a real short-lived install token ──
83
+ if (options?.oidc) {
84
+ let oidcToken;
85
+ try {
86
+ oidcToken = await exchangeOidcInstallToken();
87
+ } catch (err) {
88
+ log.warn(`OIDC exchange failed: ${err.message}`);
89
+ // biome-ignore lint/suspicious/noTemplateCurlyInString: literal placeholder name in user-facing message
90
+ log.warn(
91
+ "Falling back to ${LPM_TOKEN} environment variable placeholder.",
92
+ );
93
+ // Fall through to default mode
94
+ }
95
+
96
+ if (oidcToken) {
97
+ let npmrcContent = "";
98
+ if (fs.existsSync(npmrcPath)) {
99
+ const existingContent = fs.readFileSync(npmrcPath, "utf8");
100
+
101
+ // Detect custom default registry
102
+ if (!useScoped) {
103
+ const customRegistry = detectCustomRegistry(existingContent);
104
+ if (customRegistry) {
105
+ log.warn(
106
+ `Found existing default registry: ${customRegistry.trim()}`,
107
+ );
108
+ log.warn("Using --scoped mode to avoid overriding it.");
109
+ useScoped = true;
110
+ }
111
+ }
112
+
113
+ npmrcContent = removeLpmLines(existingContent);
114
+ }
115
+
116
+ const registryLine = useScoped
117
+ ? `@lpm.dev:registry=${fullRegistryUrl}`
118
+ : `registry=${fullRegistryUrl}`;
119
+
120
+ const lpmConfig = `# LPM Registry (OIDC — expires in 30 minutes)
119
121
  ${registryLine}
120
- ${registryHost}/:_authToken=${oidcToken}`
121
-
122
- npmrcContent = npmrcContent
123
- ? `${npmrcContent}\n\n${lpmConfig}`
124
- : lpmConfig
125
- fs.writeFileSync(npmrcPath, `${npmrcContent}\n`)
126
-
127
- p.note(
128
- `${registryLine}\nToken: OIDC read-only (30 min)`,
129
- ".npmrc configuration",
130
- )
131
-
132
- console.log("")
133
- log.success(".npmrc configured with OIDC read-only token.")
134
- log.info("Token expires in 30 minutes. No LPM_TOKEN secret needed.")
135
- if (!useScoped) {
136
- log.info("All packages (LPM + npm) will route through lpm.dev.")
137
- }
138
- console.log("")
139
-
140
- p.outro("Setup complete!")
141
- return
142
- }
143
- }
144
-
145
- // ── Default mode: ${LPM_TOKEN} placeholder ──
146
-
147
- // Check for existing .npmrc
148
- let npmrcContent = ""
149
- if (fs.existsSync(npmrcPath)) {
150
- npmrcContent = fs.readFileSync(npmrcPath, "utf8")
151
-
152
- // Detect custom default registry
153
- if (!useScoped) {
154
- const customRegistry = detectCustomRegistry(npmrcContent)
155
- if (customRegistry) {
156
- log.warn(`Found existing default registry: ${customRegistry.trim()}`)
157
- log.warn("Using --scoped mode to avoid overriding it.")
158
- log.warn(
159
- "To use LPM as your default registry, remove the existing registry= line first.",
160
- )
161
- useScoped = true
162
- }
163
- }
164
-
165
- // Check if already configured (both scoped and unscoped formats)
166
- if (
167
- npmrcContent.includes("@lpm.dev:registry") ||
168
- npmrcContent.match(/^registry=.*lpm\.dev/m)
169
- ) {
170
- const overwrite = await p.confirm({
171
- message: ".npmrc already has LPM configuration. Overwrite?",
172
- initialValue: false,
173
- })
174
-
175
- if (p.isCancel(overwrite) || !overwrite) {
176
- p.outro("Setup cancelled.")
177
- return
178
- }
179
-
180
- npmrcContent = removeLpmLines(npmrcContent)
181
- }
182
- }
183
-
184
- const registryLine = useScoped
185
- ? `@lpm.dev:registry=${fullRegistryUrl}`
186
- : `registry=${fullRegistryUrl}`
187
-
188
- // Add LPM registry config with placeholder
189
- const lpmConfig = `
122
+ ${registryHost}/:_authToken=${oidcToken}`;
123
+
124
+ npmrcContent = npmrcContent
125
+ ? `${npmrcContent}\n\n${lpmConfig}`
126
+ : lpmConfig;
127
+ fs.writeFileSync(npmrcPath, `${npmrcContent}\n`);
128
+
129
+ p.note(
130
+ `${registryLine}\nToken: OIDC read-only (30 min)`,
131
+ ".npmrc configuration",
132
+ );
133
+
134
+ console.log("");
135
+ log.success(".npmrc configured with OIDC read-only token.");
136
+ log.info("Token expires in 30 minutes. No LPM_TOKEN secret needed.");
137
+ if (!useScoped) {
138
+ log.info("All packages (LPM + npm) will route through lpm.dev.");
139
+ }
140
+ console.log("");
141
+
142
+ p.outro("Setup complete!");
143
+ return;
144
+ }
145
+ }
146
+
147
+ // ── Default mode: ${LPM_TOKEN} placeholder ──
148
+
149
+ // Check for existing .npmrc
150
+ let npmrcContent = "";
151
+ if (fs.existsSync(npmrcPath)) {
152
+ npmrcContent = fs.readFileSync(npmrcPath, "utf8");
153
+
154
+ // Detect custom default registry
155
+ if (!useScoped) {
156
+ const customRegistry = detectCustomRegistry(npmrcContent);
157
+ if (customRegistry) {
158
+ log.warn(`Found existing default registry: ${customRegistry.trim()}`);
159
+ log.warn("Using --scoped mode to avoid overriding it.");
160
+ log.warn(
161
+ "To use LPM as your default registry, remove the existing registry= line first.",
162
+ );
163
+ useScoped = true;
164
+ }
165
+ }
166
+
167
+ // Check if already configured (both scoped and unscoped formats)
168
+ if (
169
+ npmrcContent.includes("@lpm.dev:registry") ||
170
+ npmrcContent.match(/^registry=.*lpm\.dev/m)
171
+ ) {
172
+ const overwrite = await p.confirm({
173
+ message: ".npmrc already has LPM configuration. Overwrite?",
174
+ initialValue: false,
175
+ });
176
+
177
+ if (p.isCancel(overwrite) || !overwrite) {
178
+ p.outro("Setup cancelled.");
179
+ return;
180
+ }
181
+
182
+ npmrcContent = removeLpmLines(npmrcContent);
183
+ }
184
+ }
185
+
186
+ const registryLine = useScoped
187
+ ? `@lpm.dev:registry=${fullRegistryUrl}`
188
+ : `registry=${fullRegistryUrl}`;
189
+
190
+ // Add LPM registry config with placeholder
191
+ const lpmConfig = `
190
192
  # LPM Registry
191
193
  ${registryLine}
192
194
  ${registryHost}/:_authToken=\${LPM_TOKEN}
193
- `.trim()
194
-
195
- npmrcContent = npmrcContent ? `${npmrcContent}\n\n${lpmConfig}` : lpmConfig
196
- fs.writeFileSync(npmrcPath, `${npmrcContent}\n`)
197
-
198
- p.note(
199
- `${registryLine}\n${registryHost}/:_authToken=\${LPM_TOKEN}`,
200
- ".npmrc configuration",
201
- )
202
-
203
- console.log("")
204
- log.success(".npmrc configured for LPM registry.")
205
- if (useScoped) {
206
- log.info("Only @lpm.dev packages will route through lpm.dev.")
207
- } else {
208
- log.info("All packages (LPM + npm) will route through lpm.dev.")
209
- log.info("Note: First install may update resolved URLs in your lockfile.")
210
- }
211
- log.info("For local development: Run `lpm npmrc` to generate a token.")
212
- log.info("For CI/CD: Set the LPM_TOKEN environment variable.")
213
- log.info("For CI/CD with OIDC: Run `lpm setup --oidc` (no secrets needed).")
214
- console.log("")
215
-
216
- p.outro("Setup complete!")
195
+ `.trim();
196
+
197
+ npmrcContent = npmrcContent ? `${npmrcContent}\n\n${lpmConfig}` : lpmConfig;
198
+ fs.writeFileSync(npmrcPath, `${npmrcContent}\n`);
199
+
200
+ p.note(
201
+ `${registryLine}\n${registryHost}/:_authToken=\${LPM_TOKEN}`,
202
+ ".npmrc configuration",
203
+ );
204
+
205
+ console.log("");
206
+ log.success(".npmrc configured for LPM registry.");
207
+ if (useScoped) {
208
+ log.info("Only @lpm.dev packages will route through lpm.dev.");
209
+ } else {
210
+ log.info("All packages (LPM + npm) will route through lpm.dev.");
211
+ log.info("Note: First install may update resolved URLs in your lockfile.");
212
+ }
213
+ log.info("For local development: Run `lpm setup local` to generate a token.");
214
+ log.info("For CI/CD: Set the LPM_TOKEN environment variable.");
215
+ log.info(
216
+ "For CI/CD with OIDC: Run `lpm setup ci --oidc` (no secrets needed).",
217
+ );
218
+ console.log("");
219
+
220
+ p.outro("Setup complete!");
217
221
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lpm-registry/cli",
3
- "version": "0.46.0",
3
+ "version": "0.48.2",
4
4
  "description": "CLI for Licensed Package Manager",
5
5
  "author": "LPM <hello@lpm.dev> (https://lpm.dev)",
6
6
  "repository": {
@@ -62,11 +62,11 @@
62
62
  "xcode": "^3.0.1"
63
63
  },
64
64
  "optionalDependencies": {
65
- "@lpm-registry/cli-darwin-arm64": "0.46.0",
66
- "@lpm-registry/cli-darwin-x64": "0.46.0",
67
- "@lpm-registry/cli-linux-x64": "0.46.0",
68
- "@lpm-registry/cli-linux-arm64": "0.46.0",
69
- "@lpm-registry/cli-win32-x64": "0.46.0",
65
+ "@lpm-registry/cli-darwin-arm64": "0.48.2",
66
+ "@lpm-registry/cli-darwin-x64": "0.48.2",
67
+ "@lpm-registry/cli-linux-x64": "0.48.2",
68
+ "@lpm-registry/cli-linux-arm64": "0.48.2",
69
+ "@lpm-registry/cli-win32-x64": "0.48.2",
70
70
  "keytar": "^7.9.0",
71
71
  "sigstore": "^3.0.0"
72
72
  },