@backstage/cli 0.35.5-next.0 → 0.36.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/config/jest.js +2 -2
  3. package/dist/index.cjs.js +1 -0
  4. package/dist/modules/auth/commands/list.cjs.js +23 -0
  5. package/dist/modules/auth/commands/login.cjs.js +316 -0
  6. package/dist/modules/auth/commands/logout.cjs.js +55 -0
  7. package/dist/modules/auth/commands/printToken.cjs.js +41 -0
  8. package/dist/modules/auth/commands/select.cjs.js +32 -0
  9. package/dist/modules/auth/commands/show.cjs.js +59 -0
  10. package/dist/modules/auth/index.cjs.js +44 -0
  11. package/dist/modules/auth/lib/auth.cjs.js +60 -0
  12. package/dist/modules/auth/lib/http.cjs.js +26 -0
  13. package/dist/modules/auth/lib/localServer.cjs.js +80 -0
  14. package/dist/modules/auth/lib/pkce.cjs.js +23 -0
  15. package/dist/modules/auth/lib/prompt.cjs.js +44 -0
  16. package/dist/modules/auth/lib/secretStore.cjs.js +81 -0
  17. package/dist/modules/auth/lib/storage.cjs.js +152 -0
  18. package/dist/modules/build/commands/buildWorkspace.cjs.js +31 -2
  19. package/dist/modules/build/commands/package/build/command.cjs.js +62 -15
  20. package/dist/modules/build/commands/package/build/index.cjs.js +3 -1
  21. package/dist/modules/{maintenance → build}/commands/package/clean.cjs.js +4 -2
  22. package/dist/modules/build/commands/package/postpack.cjs.js +15 -0
  23. package/dist/modules/{maintenance/commands/package/pack.cjs.js → build/commands/package/prepack.cjs.js} +10 -10
  24. package/dist/modules/build/commands/package/start/command.cjs.js +68 -11
  25. package/dist/modules/build/commands/package/start/index.cjs.js +3 -1
  26. package/dist/modules/build/commands/package/start/startFrontend.cjs.js +1 -1
  27. package/dist/modules/build/commands/repo/build.cjs.js +46 -11
  28. package/dist/modules/{maintenance → build}/commands/repo/clean.cjs.js +7 -3
  29. package/dist/modules/build/commands/repo/start.cjs.js +54 -5
  30. package/dist/modules/build/index.cjs.js +32 -123
  31. package/dist/modules/build/lib/buildFrontend.cjs.js +2 -2
  32. package/dist/modules/build/lib/bundler/config.cjs.js +1 -1
  33. package/dist/modules/build/lib/bundler/moduleFederation.cjs.js +1 -1
  34. package/dist/modules/build/lib/bundler/server.cjs.js +1 -1
  35. package/dist/modules/build/lib/config.cjs.js +94 -0
  36. package/dist/modules/build/lib/optionsParser.cjs.js +22 -0
  37. package/dist/modules/build/lib/packager/createDistWorkspace.cjs.js +1 -1
  38. package/dist/modules/build/lib/packager/productionPack.cjs.js +1 -1
  39. package/dist/modules/build/lib/role.cjs.js +1 -1
  40. package/dist/modules/config/commands/docs.cjs.js +19 -2
  41. package/dist/modules/config/commands/print.cjs.js +41 -13
  42. package/dist/modules/config/commands/schema.cjs.js +25 -6
  43. package/dist/modules/config/commands/validate.cjs.js +38 -7
  44. package/dist/modules/config/index.cjs.js +6 -65
  45. package/dist/modules/config/lib/config.cjs.js +6 -22
  46. package/dist/modules/create-github-app/commands/create-github-app/index.cjs.js +14 -3
  47. package/dist/modules/create-github-app/index.cjs.js +1 -9
  48. package/dist/modules/info/commands/info.cjs.js +26 -6
  49. package/dist/modules/info/index.cjs.js +1 -23
  50. package/dist/modules/lint/commands/package/lint.cjs.js +42 -10
  51. package/dist/modules/lint/commands/repo/lint.cjs.js +99 -27
  52. package/dist/modules/lint/index.cjs.js +2 -60
  53. package/dist/modules/lint/lib/optionsParser.cjs.js +22 -0
  54. package/dist/modules/maintenance/commands/repo/fix.cjs.js +32 -9
  55. package/dist/modules/maintenance/commands/repo/list-deprecations.cjs.js +20 -4
  56. package/dist/modules/maintenance/index.cjs.js +2 -64
  57. package/dist/modules/migrate/commands/packageExports.cjs.js +9 -11
  58. package/dist/modules/migrate/commands/packageLintConfigs.cjs.js +7 -3
  59. package/dist/modules/migrate/commands/packageRole.cjs.js +3 -1
  60. package/dist/modules/migrate/commands/packageScripts.cjs.js +11 -7
  61. package/dist/modules/migrate/commands/reactRouterDeps.cjs.js +7 -3
  62. package/dist/modules/migrate/commands/versions/bump.cjs.js +48 -20
  63. package/dist/modules/migrate/commands/versions/migrate.cjs.js +24 -3
  64. package/dist/modules/migrate/index.cjs.js +12 -55
  65. package/dist/modules/new/commands/new.cjs.js +70 -15
  66. package/dist/modules/new/index.cjs.js +1 -29
  67. package/dist/modules/new/lib/execution/PortableTemplater.cjs.js +5 -9
  68. package/dist/modules/new/lib/preparation/loadPortableTemplate.cjs.js +8 -8
  69. package/dist/modules/new/lib/preparation/loadPortableTemplateConfig.cjs.js +18 -18
  70. package/dist/{lib → modules/new/lib}/version.cjs.js +22 -34
  71. package/dist/modules/test/commands/package/test.cjs.js +1 -10
  72. package/dist/modules/test/commands/repo/test.cjs.js +51 -39
  73. package/dist/modules/test/index.cjs.js +2 -32
  74. package/dist/modules/translations/commands/export.cjs.js +25 -1
  75. package/dist/modules/translations/commands/import.cjs.js +25 -1
  76. package/dist/modules/translations/index.cjs.js +2 -37
  77. package/dist/packages/backend-defaults/package.json.cjs.js +1 -1
  78. package/dist/packages/backend-plugin-api/package.json.cjs.js +1 -1
  79. package/dist/packages/backend-test-utils/package.json.cjs.js +1 -1
  80. package/dist/packages/catalog-client/package.json.cjs.js +1 -1
  81. package/dist/packages/cli/package.json.cjs.js +7 -6
  82. package/dist/packages/core-app-api/package.json.cjs.js +1 -1
  83. package/dist/packages/core-components/package.json.cjs.js +1 -1
  84. package/dist/packages/core-plugin-api/package.json.cjs.js +1 -1
  85. package/dist/packages/dev-utils/package.json.cjs.js +1 -1
  86. package/dist/packages/frontend-defaults/package.json.cjs.js +1 -1
  87. package/dist/packages/frontend-plugin-api/package.json.cjs.js +1 -1
  88. package/dist/packages/frontend-test-utils/package.json.cjs.js +1 -1
  89. package/dist/plugins/auth-backend/package.json.cjs.js +1 -1
  90. package/dist/plugins/auth-backend-module-guest-provider/package.json.cjs.js +1 -1
  91. package/dist/plugins/catalog-node/package.json.cjs.js +1 -1
  92. package/dist/plugins/scaffolder-node/package.json.cjs.js +1 -1
  93. package/dist/plugins/scaffolder-node-test-utils/package.json.cjs.js +1 -1
  94. package/dist/wiring/CliInitializer.cjs.js +24 -7
  95. package/dist/wiring/version.cjs.js +20 -0
  96. package/package.json +26 -22
  97. package/dist/lib/cache/SuccessCache.cjs.js +0 -79
  98. package/dist/lib/lazy.cjs.js +0 -22
  99. package/dist/lib/optionsParser.cjs.js +0 -37
  100. package/dist/lib/versioning/Lockfile.cjs.js +0 -89
  101. package/dist/lib/yarnPlugin.cjs.js +0 -46
  102. /package/dist/modules/{maintenance → build}/lib/publishing.cjs.js +0 -0
  103. /package/dist/{lib → modules/build/lib}/typeDistProject.cjs.js +0 -0
  104. /package/dist/{lib → modules/migrate/lib}/versioning/packages.cjs.js +0 -0
  105. /package/dist/{lib → modules/migrate/lib}/versioning/yarn.cjs.js +0 -0
  106. /package/dist/{lib → wiring}/errors.cjs.js +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,47 @@
1
1
  # @backstage/cli
2
2
 
3
+ ## 0.36.0-next.2
4
+
5
+ ### Minor Changes
6
+
7
+ - d0f4cd2: Added new `auth` command group for authenticating the CLI with Backstage instances using OAuth 2.0 with a pre-registered client metadata document. Commands include `login`, `logout`, `list`, `show`, `print-token`, and `select` for managing multiple authenticated instances.
8
+
9
+ ### Patch Changes
10
+
11
+ - a4e5902: Internal refactor of the CLI command registration
12
+ - ff4a45a: Migrated remaining CLI command handlers from `commander` to `cleye` for argument parsing. Several camelCase CLI flags have been deprecated in favor of their kebab-case equivalents (e.g. `--successCache` → `--success-cache`). The old camelCase forms still work but will now log a deprecation warning. Please update any scripts or CI configurations to use the kebab-case versions.
13
+ - 4a75544: Updated dependency `react-refresh` to `^0.18.0`.
14
+ - Updated dependencies
15
+ - @backstage/cli-common@0.2.0-next.2
16
+ - @backstage/integration@2.0.0-next.2
17
+
18
+ ## 0.36.0-next.1
19
+
20
+ ### Minor Changes
21
+
22
+ - b36a60d: **BREAKING**: The `migrate package-exports` command has been removed. Use `repo fix` instead.
23
+
24
+ ### Patch Changes
25
+
26
+ - 0d2d0f2: Internal refactor of CLI modularization, moving individual commands to be implemented with cleye.
27
+ - 2fcba39: Internal refactor to move shared utilities into their consuming modules, reducing cross-module dependencies.
28
+ - c85ac86: Internal refactor to split `loadCliConfig` into separate implementations for the build and config CLI modules, removing a cross-module dependency.
29
+ - 61cb976: Migrated internal versioning utilities to use `@backstage/cli-node` instead of a local implementation.
30
+ - 825c81d: Internal refactor of CLI command modules.
31
+ - a9d23c4: Properly support `package.json` `workspaces` field
32
+ - Updated dependencies
33
+ - @backstage/cli-common@0.2.0-next.1
34
+ - @backstage/cli-node@0.2.19-next.1
35
+ - @backstage/module-federation-common@0.1.2-next.0
36
+ - @backstage/integration@2.0.0-next.1
37
+ - @backstage/catalog-model@1.7.6
38
+ - @backstage/config@1.3.6
39
+ - @backstage/config-loader@1.10.9-next.0
40
+ - @backstage/errors@1.2.7
41
+ - @backstage/eslint-plugin@0.2.2-next.0
42
+ - @backstage/release-manifests@0.0.13
43
+ - @backstage/types@1.2.2
44
+
3
45
  ## 0.35.5-next.0
4
46
 
5
47
  ### Patch Changes
package/config/jest.js CHANGED
@@ -336,8 +336,8 @@ async function getRootConfig() {
336
336
  rejectFrontendNetworkRequests,
337
337
  };
338
338
 
339
- const workspacePatterns =
340
- rootPkgJson.workspaces && rootPkgJson.workspaces.packages;
339
+ const ws = rootPkgJson.workspaces;
340
+ const workspacePatterns = Array.isArray(ws) ? ws : ws?.packages;
341
341
 
342
342
  // Check if we're running within a specific monorepo package. In that case just get the single project config.
343
343
  if (!workspacePatterns || paths.targetRoot !== paths.targetDir) {
package/dist/index.cjs.js CHANGED
@@ -14,6 +14,7 @@ var CliInitializer = require('./wiring/CliInitializer.cjs.js');
14
14
  initializer.add(import('./modules/new/index.cjs.js'));
15
15
  initializer.add(import('./modules/test/index.cjs.js'));
16
16
  initializer.add(import('./modules/translations/index.cjs.js'));
17
+ initializer.add(import('./modules/auth/index.cjs.js'));
17
18
  await initializer.run();
18
19
  })();
19
20
  //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1,23 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var cleye = require('cleye');
6
+ var storage = require('../lib/storage.cjs.js');
7
+
8
+ var list = async ({ args, info }) => {
9
+ cleye.cli({ help: info }, void 0, args);
10
+ const { instances, selected } = await storage.getAllInstances();
11
+ if (!instances.length) {
12
+ process.stderr.write("No instances found\n");
13
+ return;
14
+ }
15
+ for (const inst of instances) {
16
+ const mark = inst.name === selected?.name ? "* " : " ";
17
+ process.stdout.write(`${mark}${inst.name} - ${inst.baseUrl}
18
+ `);
19
+ }
20
+ };
21
+
22
+ exports.default = list;
23
+ //# sourceMappingURL=list.cjs.js.map
@@ -0,0 +1,316 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var cleye = require('cleye');
6
+ var localServer = require('../lib/localServer.cjs.js');
7
+ var node_child_process = require('node:child_process');
8
+ var pkce = require('../lib/pkce.cjs.js');
9
+ var http = require('../lib/http.cjs.js');
10
+ var storage = require('../lib/storage.cjs.js');
11
+ var secretStore = require('../lib/secretStore.cjs.js');
12
+ var crypto = require('node:crypto');
13
+ var fs = require('fs-extra');
14
+ var path = require('node:path');
15
+ var glob = require('glob');
16
+ var YAML = require('yaml');
17
+ var inquirer = require('inquirer');
18
+
19
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
20
+
21
+ var crypto__default = /*#__PURE__*/_interopDefaultCompat(crypto);
22
+ var fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
23
+ var path__default = /*#__PURE__*/_interopDefaultCompat(path);
24
+ var glob__default = /*#__PURE__*/_interopDefaultCompat(glob);
25
+ var YAML__default = /*#__PURE__*/_interopDefaultCompat(YAML);
26
+ var inquirer__default = /*#__PURE__*/_interopDefaultCompat(inquirer);
27
+
28
+ const TOKEN_EXCHANGE_TIMEOUT_MS = 3e4;
29
+ var login = async ({ args, info }) => {
30
+ const {
31
+ flags: { backendUrl, noBrowser, instance: instanceFlag }
32
+ } = cleye.cli(
33
+ {
34
+ help: info,
35
+ flags: {
36
+ backendUrl: { type: String, description: "Backend base URL" },
37
+ noBrowser: {
38
+ type: Boolean,
39
+ description: "Do not open browser automatically"
40
+ },
41
+ instance: {
42
+ type: String,
43
+ description: "Name for this instance (used by other auth commands)"
44
+ }
45
+ }
46
+ },
47
+ void 0,
48
+ args
49
+ );
50
+ const { instances, selected } = await storage.getAllInstances();
51
+ let backendBaseUrl;
52
+ let instanceName;
53
+ if (instanceFlag) {
54
+ instanceName = instanceFlag;
55
+ const targetInstance = instances.find((i) => i.name === instanceFlag);
56
+ if (targetInstance) {
57
+ backendBaseUrl = normalizeUrl(backendUrl) ?? targetInstance.baseUrl;
58
+ } else {
59
+ backendBaseUrl = normalizeUrl(backendUrl) ?? await pickBaseUrl();
60
+ }
61
+ } else if (backendUrl) {
62
+ backendBaseUrl = normalizeUrl(backendUrl);
63
+ instanceName = deriveInstanceName(backendBaseUrl);
64
+ } else if (instances.length > 0) {
65
+ const choice = await promptForInstance(instances, selected);
66
+ if (choice === "__new__") {
67
+ backendBaseUrl = await pickBaseUrl();
68
+ instanceName = deriveInstanceName(backendBaseUrl);
69
+ } else {
70
+ const targetInstance = instances.find((i) => i.name === choice);
71
+ if (!targetInstance) {
72
+ throw new Error("Instance not found");
73
+ }
74
+ backendBaseUrl = targetInstance.baseUrl;
75
+ instanceName = targetInstance.name;
76
+ }
77
+ } else {
78
+ backendBaseUrl = await pickBaseUrl();
79
+ instanceName = deriveInstanceName(backendBaseUrl);
80
+ }
81
+ const authBaseUrl = `${backendBaseUrl}/api/auth`;
82
+ const clientId = `${authBaseUrl}/.well-known/oauth-client/cli.json`;
83
+ const metadataResponse = await fetch(clientId, {
84
+ signal: AbortSignal.timeout(3e4)
85
+ });
86
+ if (!metadataResponse.ok) {
87
+ throw new Error(
88
+ `Server does not support CLI authentication. Ensure CIMD is enabled on the backend.`
89
+ );
90
+ }
91
+ const { verifier, challenge, state } = createPkceState();
92
+ const callback = await localServer.startCallbackServer({ state });
93
+ try {
94
+ const authorizeUrl = buildAuthorizeUrl({
95
+ authBaseUrl,
96
+ clientId,
97
+ redirectUri: callback.url,
98
+ state,
99
+ challenge
100
+ });
101
+ await openBrowserOrPrint(authorizeUrl, noBrowser);
102
+ const code = await waitForAuthorizationCode(callback, state);
103
+ const token = await exchangeAuthorizationCode({
104
+ authBaseUrl,
105
+ code,
106
+ redirectUri: callback.url,
107
+ verifier
108
+ });
109
+ await persistInstance({
110
+ instanceName,
111
+ backendBaseUrl,
112
+ clientId,
113
+ token
114
+ });
115
+ process.stdout.write("Login successful\n");
116
+ } finally {
117
+ await callback.close();
118
+ }
119
+ };
120
+ async function promptForInstance(instances, selected) {
121
+ const choices = instances.map((i) => ({
122
+ name: `${i.name === selected?.name ? "* " : " "}${i.name} (${i.baseUrl})`,
123
+ value: i.name
124
+ }));
125
+ choices.push({
126
+ name: "Add new instance...",
127
+ value: "__new__"
128
+ });
129
+ const { choice } = await inquirer__default.default.prompt([
130
+ {
131
+ type: "list",
132
+ name: "choice",
133
+ message: "Select instance to authenticate:",
134
+ choices,
135
+ default: selected?.name ?? "__new__"
136
+ }
137
+ ]);
138
+ return choice;
139
+ }
140
+ async function pickBaseUrl() {
141
+ const cwd = process.cwd();
142
+ const candidates = [];
143
+ const patterns = [
144
+ "app-config.yaml",
145
+ "app-config.*.yaml",
146
+ "packages/*/app-config.yaml",
147
+ "packages/*/app-config.*.yaml"
148
+ ];
149
+ const files = patterns.flatMap((p) => glob__default.default.sync(p, { cwd, nodir: true }));
150
+ for (const file of files) {
151
+ try {
152
+ const content = await fs__default.default.readFile(path__default.default.resolve(cwd, file), "utf8");
153
+ const doc = YAML__default.default.parse(content);
154
+ const url = doc?.backend?.baseUrl;
155
+ if (url) {
156
+ candidates.push({ url: normalizeUrl(url), file });
157
+ }
158
+ } catch {
159
+ }
160
+ }
161
+ const list = [...new Map(candidates.map((c) => [c.url, c])).values()];
162
+ if (list.length === 0) {
163
+ const { manual } = await inquirer__default.default.prompt([
164
+ { type: "input", name: "manual", message: "Enter backend base URL" }
165
+ ]);
166
+ return normalizeUrl(manual);
167
+ }
168
+ if (list.length === 1) {
169
+ return list[0].url;
170
+ }
171
+ const { picked } = await inquirer__default.default.prompt([
172
+ {
173
+ type: "list",
174
+ name: "picked",
175
+ message: "Select backend base URL",
176
+ choices: [
177
+ ...list.map((e) => ({ name: `${e.url} (${e.file})`, value: e.url })),
178
+ { name: "Enter manually", value: "__manual__" }
179
+ ]
180
+ }
181
+ ]);
182
+ if (picked === "__manual__") {
183
+ const { manual } = await inquirer__default.default.prompt([
184
+ { type: "input", name: "manual", message: "Enter backend base URL" }
185
+ ]);
186
+ return normalizeUrl(manual);
187
+ }
188
+ return picked;
189
+ }
190
+ function normalizeUrl(u) {
191
+ if (u === void 0) {
192
+ return void 0;
193
+ }
194
+ try {
195
+ const url = new URL(u);
196
+ return url.toString().replace(/\/$/, "");
197
+ } catch {
198
+ throw new Error(`'${u}' is not a valid URL`);
199
+ }
200
+ }
201
+ function deriveInstanceName(url) {
202
+ return new URL(url).host;
203
+ }
204
+ function createPkceState() {
205
+ const verifier = pkce.generateVerifier();
206
+ const challenge = pkce.challengeFromVerifier(verifier);
207
+ const state = cryptoRandom();
208
+ return { verifier, challenge, state };
209
+ }
210
+ function buildAuthorizeUrl(options) {
211
+ const { authBaseUrl, clientId, redirectUri, state, challenge } = options;
212
+ const authorize = new URL(`${authBaseUrl}/v1/authorize`);
213
+ authorize.searchParams.set("client_id", clientId);
214
+ authorize.searchParams.set("redirect_uri", redirectUri);
215
+ authorize.searchParams.set("response_type", "code");
216
+ authorize.searchParams.set("scope", "openid offline_access");
217
+ authorize.searchParams.set("state", state);
218
+ authorize.searchParams.set("code_challenge", challenge);
219
+ authorize.searchParams.set("code_challenge_method", "S256");
220
+ return authorize.toString();
221
+ }
222
+ async function openBrowserOrPrint(url, noBrowser) {
223
+ if (noBrowser) {
224
+ process.stdout.write(`Open this URL to continue: ${url}
225
+ `);
226
+ } else {
227
+ process.stdout.write(`Opening the following URL: ${url}
228
+ `);
229
+ openInBrowser(url);
230
+ }
231
+ }
232
+ async function waitForAuthorizationCode(callback, expectedState) {
233
+ const { code, state } = await callback.waitForCode();
234
+ if (state !== expectedState) {
235
+ throw new Error("State mismatch");
236
+ }
237
+ return code;
238
+ }
239
+ async function exchangeAuthorizationCode(options) {
240
+ const { authBaseUrl, code, redirectUri, verifier } = options;
241
+ return await http.httpJson(`${authBaseUrl}/v1/token`, {
242
+ method: "POST",
243
+ body: {
244
+ grant_type: "authorization_code",
245
+ code,
246
+ redirect_uri: redirectUri,
247
+ code_verifier: verifier
248
+ },
249
+ signal: AbortSignal.timeout(TOKEN_EXCHANGE_TIMEOUT_MS)
250
+ });
251
+ }
252
+ async function persistInstance(options) {
253
+ const { instanceName, backendBaseUrl, clientId, token } = options;
254
+ const secretStore$1 = await secretStore.getSecretStore();
255
+ await storage.withMetadataLock(async () => {
256
+ const service = `backstage-cli:auth-instance:${instanceName}`;
257
+ await secretStore$1.set(service, "accessToken", token.access_token);
258
+ if (token.refresh_token) {
259
+ await secretStore$1.set(service, "refreshToken", token.refresh_token);
260
+ } else {
261
+ process.stderr.write(
262
+ "Warning: No refresh token received. You will need to re-authenticate when the access token expires.\n"
263
+ );
264
+ }
265
+ let existing;
266
+ try {
267
+ existing = await storage.getInstanceByName(instanceName);
268
+ } catch {
269
+ }
270
+ await storage.upsertInstance({
271
+ name: instanceName,
272
+ baseUrl: backendBaseUrl,
273
+ clientId,
274
+ issuedAt: Date.now(),
275
+ accessTokenExpiresAt: Date.now() + token.expires_in * 1e3,
276
+ selected: existing?.selected
277
+ });
278
+ });
279
+ }
280
+ function cryptoRandom() {
281
+ return crypto__default.default.randomBytes(32).toString("hex");
282
+ }
283
+ function openInBrowser(url) {
284
+ const handleError = (error) => {
285
+ const message = error instanceof Error ? error.message : "Unknown error";
286
+ process.stderr.write(
287
+ `Warning: Failed to open browser automatically: ${message}
288
+ `
289
+ );
290
+ process.stderr.write(`Please open this URL manually: ${url}
291
+ `);
292
+ };
293
+ const spawnOpts = { detached: true, stdio: "ignore" };
294
+ let child;
295
+ try {
296
+ if (process.platform === "darwin") {
297
+ child = node_child_process.spawn("open", [url], spawnOpts);
298
+ } else if (process.platform === "win32") {
299
+ child = node_child_process.spawn(
300
+ "powershell",
301
+ ["-Command", `Start-Process '${url.replace(/'/g, "''")}'`],
302
+ spawnOpts
303
+ );
304
+ } else {
305
+ child = node_child_process.spawn("xdg-open", [url], spawnOpts);
306
+ }
307
+ child.unref();
308
+ child.on("error", handleError);
309
+ } catch (error) {
310
+ handleError(error);
311
+ }
312
+ }
313
+
314
+ exports.default = login;
315
+ exports.openInBrowser = openInBrowser;
316
+ //# sourceMappingURL=login.cjs.js.map
@@ -0,0 +1,55 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var cleye = require('cleye');
6
+ var secretStore = require('../lib/secretStore.cjs.js');
7
+ var storage = require('../lib/storage.cjs.js');
8
+ var http = require('../lib/http.cjs.js');
9
+ var prompt = require('../lib/prompt.cjs.js');
10
+
11
+ var logout = async ({ args, info }) => {
12
+ const {
13
+ flags: { instance: instanceFlag }
14
+ } = cleye.cli(
15
+ {
16
+ help: info,
17
+ flags: {
18
+ instance: {
19
+ type: String,
20
+ description: "Name of the instance to log out"
21
+ }
22
+ }
23
+ },
24
+ void 0,
25
+ args
26
+ );
27
+ const { name: instanceName } = await prompt.pickInstance(instanceFlag);
28
+ await storage.withMetadataLock(async () => {
29
+ const instance = await storage.getInstanceByName(instanceName);
30
+ const secretStore$1 = await secretStore.getSecretStore();
31
+ const service = `backstage-cli:auth-instance:${instanceName}`;
32
+ const refreshToken = await secretStore$1.get(service, "refreshToken") ?? "";
33
+ if (refreshToken) {
34
+ try {
35
+ const authBaseUrl = new URL("/api/auth", instance.baseUrl).toString().replace(/\/$/, "");
36
+ await http.httpJson(`${authBaseUrl}/v1/revoke`, {
37
+ method: "POST",
38
+ body: {
39
+ token: refreshToken,
40
+ token_type_hint: "refresh_token"
41
+ },
42
+ signal: AbortSignal.timeout(3e4)
43
+ });
44
+ } catch {
45
+ }
46
+ }
47
+ await secretStore$1.delete(service, "accessToken");
48
+ await secretStore$1.delete(service, "refreshToken");
49
+ await storage.removeInstance(instance.name);
50
+ });
51
+ process.stdout.write("Logged out\n");
52
+ };
53
+
54
+ exports.default = logout;
55
+ //# sourceMappingURL=logout.cjs.js.map
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var cleye = require('cleye');
6
+ var auth = require('../lib/auth.cjs.js');
7
+ var storage = require('../lib/storage.cjs.js');
8
+ var secretStore = require('../lib/secretStore.cjs.js');
9
+
10
+ var printToken = async ({ args, info }) => {
11
+ const {
12
+ flags: { instance: instanceFlag }
13
+ } = cleye.cli(
14
+ {
15
+ help: info,
16
+ flags: {
17
+ instance: {
18
+ type: String,
19
+ description: "Name of the instance to use"
20
+ }
21
+ }
22
+ },
23
+ void 0,
24
+ args
25
+ );
26
+ let instance = await storage.getSelectedInstance(instanceFlag);
27
+ if (auth.accessTokenNeedsRefresh(instance)) {
28
+ instance = await auth.refreshAccessToken(instance.name);
29
+ }
30
+ const secretStore$1 = await secretStore.getSecretStore();
31
+ const service = `backstage-cli:auth-instance:${instance.name}`;
32
+ const accessToken = await secretStore$1.get(service, "accessToken");
33
+ if (!accessToken) {
34
+ throw new Error('No access token found. Run "auth login" to authenticate.');
35
+ }
36
+ process.stdout.write(`${accessToken}
37
+ `);
38
+ };
39
+
40
+ exports.default = printToken;
41
+ //# sourceMappingURL=printToken.cjs.js.map
@@ -0,0 +1,32 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var cleye = require('cleye');
6
+ var storage = require('../lib/storage.cjs.js');
7
+ var prompt = require('../lib/prompt.cjs.js');
8
+
9
+ var select = async ({ args, info }) => {
10
+ const {
11
+ flags: { instance: instanceFlag }
12
+ } = cleye.cli(
13
+ {
14
+ help: info,
15
+ flags: {
16
+ instance: {
17
+ type: String,
18
+ description: "Name of the instance to select"
19
+ }
20
+ }
21
+ },
22
+ void 0,
23
+ args
24
+ );
25
+ const instance = await prompt.pickInstance(instanceFlag);
26
+ await storage.setSelectedInstance(instance.name);
27
+ process.stderr.write(`Selected instance '${instance.name}'
28
+ `);
29
+ };
30
+
31
+ exports.default = select;
32
+ //# sourceMappingURL=select.cjs.js.map
@@ -0,0 +1,59 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var cleye = require('cleye');
6
+ var http = require('../lib/http.cjs.js');
7
+ var storage = require('../lib/storage.cjs.js');
8
+ var auth = require('../lib/auth.cjs.js');
9
+ var secretStore = require('../lib/secretStore.cjs.js');
10
+
11
+ var show = async ({ args, info }) => {
12
+ const {
13
+ flags: { instance: instanceFlag }
14
+ } = cleye.cli(
15
+ {
16
+ help: info,
17
+ flags: {
18
+ instance: {
19
+ type: String,
20
+ description: "Name of the instance to show"
21
+ }
22
+ }
23
+ },
24
+ void 0,
25
+ args
26
+ );
27
+ let instance = await storage.getSelectedInstance(instanceFlag);
28
+ if (auth.accessTokenNeedsRefresh(instance)) {
29
+ process.stdout.write("Refreshing access token...\n");
30
+ instance = await auth.refreshAccessToken(instance.name);
31
+ }
32
+ const authBase = new URL("/api/auth", instance.baseUrl).toString().replace(/\/$/, "");
33
+ const secretStore$1 = await secretStore.getSecretStore();
34
+ const service = `backstage-cli:auth-instance:${instance.name}`;
35
+ const accessToken = await secretStore$1.get(service, "accessToken");
36
+ if (!accessToken) {
37
+ throw new Error('No access token found. Run "auth login" to authenticate.');
38
+ }
39
+ const userinfo = await http.httpJson(
40
+ `${authBase}/v1/userinfo`,
41
+ {
42
+ headers: { Authorization: `Bearer ${accessToken}` },
43
+ signal: AbortSignal.timeout(3e4)
44
+ }
45
+ );
46
+ process.stdout.write(`User: ${userinfo.claims.sub}
47
+ `);
48
+ process.stdout.write(`
49
+ `);
50
+ process.stdout.write(`Ownership:
51
+ `);
52
+ for (const ent of userinfo.claims.ent ?? []) {
53
+ process.stdout.write(` - ${ent}
54
+ `);
55
+ }
56
+ };
57
+
58
+ exports.default = show;
59
+ //# sourceMappingURL=show.cjs.js.map
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var factory = require('../../wiring/factory.cjs.js');
6
+
7
+ var index = factory.createCliPlugin({
8
+ pluginId: "auth",
9
+ init: async (reg) => {
10
+ reg.addCommand({
11
+ path: ["auth", "login"],
12
+ description: "Log in the CLI to a Backstage instance",
13
+ execute: { loader: () => import('./commands/login.cjs.js') }
14
+ });
15
+ reg.addCommand({
16
+ path: ["auth", "logout"],
17
+ description: "Log out the CLI and clear stored credentials",
18
+ execute: { loader: () => import('./commands/logout.cjs.js') }
19
+ });
20
+ reg.addCommand({
21
+ path: ["auth", "show"],
22
+ description: "Show details of an authenticated instance",
23
+ execute: { loader: () => import('./commands/show.cjs.js') }
24
+ });
25
+ reg.addCommand({
26
+ path: ["auth", "list"],
27
+ description: "List authenticated instances",
28
+ execute: { loader: () => import('./commands/list.cjs.js') }
29
+ });
30
+ reg.addCommand({
31
+ path: ["auth", "print-token"],
32
+ description: "Print an access token to stdout (auto-refresh if needed)",
33
+ execute: { loader: () => import('./commands/printToken.cjs.js') }
34
+ });
35
+ reg.addCommand({
36
+ path: ["auth", "select"],
37
+ description: "Select the default instance",
38
+ execute: { loader: () => import('./commands/select.cjs.js') }
39
+ });
40
+ }
41
+ });
42
+
43
+ exports.default = index;
44
+ //# sourceMappingURL=index.cjs.js.map