@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.
- package/CHANGELOG.md +42 -0
- package/config/jest.js +2 -2
- package/dist/index.cjs.js +1 -0
- package/dist/modules/auth/commands/list.cjs.js +23 -0
- package/dist/modules/auth/commands/login.cjs.js +316 -0
- package/dist/modules/auth/commands/logout.cjs.js +55 -0
- package/dist/modules/auth/commands/printToken.cjs.js +41 -0
- package/dist/modules/auth/commands/select.cjs.js +32 -0
- package/dist/modules/auth/commands/show.cjs.js +59 -0
- package/dist/modules/auth/index.cjs.js +44 -0
- package/dist/modules/auth/lib/auth.cjs.js +60 -0
- package/dist/modules/auth/lib/http.cjs.js +26 -0
- package/dist/modules/auth/lib/localServer.cjs.js +80 -0
- package/dist/modules/auth/lib/pkce.cjs.js +23 -0
- package/dist/modules/auth/lib/prompt.cjs.js +44 -0
- package/dist/modules/auth/lib/secretStore.cjs.js +81 -0
- package/dist/modules/auth/lib/storage.cjs.js +152 -0
- package/dist/modules/build/commands/buildWorkspace.cjs.js +31 -2
- package/dist/modules/build/commands/package/build/command.cjs.js +62 -15
- package/dist/modules/build/commands/package/build/index.cjs.js +3 -1
- package/dist/modules/{maintenance → build}/commands/package/clean.cjs.js +4 -2
- package/dist/modules/build/commands/package/postpack.cjs.js +15 -0
- package/dist/modules/{maintenance/commands/package/pack.cjs.js → build/commands/package/prepack.cjs.js} +10 -10
- package/dist/modules/build/commands/package/start/command.cjs.js +68 -11
- package/dist/modules/build/commands/package/start/index.cjs.js +3 -1
- package/dist/modules/build/commands/package/start/startFrontend.cjs.js +1 -1
- package/dist/modules/build/commands/repo/build.cjs.js +46 -11
- package/dist/modules/{maintenance → build}/commands/repo/clean.cjs.js +7 -3
- package/dist/modules/build/commands/repo/start.cjs.js +54 -5
- package/dist/modules/build/index.cjs.js +32 -123
- package/dist/modules/build/lib/buildFrontend.cjs.js +2 -2
- package/dist/modules/build/lib/bundler/config.cjs.js +1 -1
- package/dist/modules/build/lib/bundler/moduleFederation.cjs.js +1 -1
- package/dist/modules/build/lib/bundler/server.cjs.js +1 -1
- package/dist/modules/build/lib/config.cjs.js +94 -0
- package/dist/modules/build/lib/optionsParser.cjs.js +22 -0
- package/dist/modules/build/lib/packager/createDistWorkspace.cjs.js +1 -1
- package/dist/modules/build/lib/packager/productionPack.cjs.js +1 -1
- package/dist/modules/build/lib/role.cjs.js +1 -1
- package/dist/modules/config/commands/docs.cjs.js +19 -2
- package/dist/modules/config/commands/print.cjs.js +41 -13
- package/dist/modules/config/commands/schema.cjs.js +25 -6
- package/dist/modules/config/commands/validate.cjs.js +38 -7
- package/dist/modules/config/index.cjs.js +6 -65
- package/dist/modules/config/lib/config.cjs.js +6 -22
- package/dist/modules/create-github-app/commands/create-github-app/index.cjs.js +14 -3
- package/dist/modules/create-github-app/index.cjs.js +1 -9
- package/dist/modules/info/commands/info.cjs.js +26 -6
- package/dist/modules/info/index.cjs.js +1 -23
- package/dist/modules/lint/commands/package/lint.cjs.js +42 -10
- package/dist/modules/lint/commands/repo/lint.cjs.js +99 -27
- package/dist/modules/lint/index.cjs.js +2 -60
- package/dist/modules/lint/lib/optionsParser.cjs.js +22 -0
- package/dist/modules/maintenance/commands/repo/fix.cjs.js +32 -9
- package/dist/modules/maintenance/commands/repo/list-deprecations.cjs.js +20 -4
- package/dist/modules/maintenance/index.cjs.js +2 -64
- package/dist/modules/migrate/commands/packageExports.cjs.js +9 -11
- package/dist/modules/migrate/commands/packageLintConfigs.cjs.js +7 -3
- package/dist/modules/migrate/commands/packageRole.cjs.js +3 -1
- package/dist/modules/migrate/commands/packageScripts.cjs.js +11 -7
- package/dist/modules/migrate/commands/reactRouterDeps.cjs.js +7 -3
- package/dist/modules/migrate/commands/versions/bump.cjs.js +48 -20
- package/dist/modules/migrate/commands/versions/migrate.cjs.js +24 -3
- package/dist/modules/migrate/index.cjs.js +12 -55
- package/dist/modules/new/commands/new.cjs.js +70 -15
- package/dist/modules/new/index.cjs.js +1 -29
- package/dist/modules/new/lib/execution/PortableTemplater.cjs.js +5 -9
- package/dist/modules/new/lib/preparation/loadPortableTemplate.cjs.js +8 -8
- package/dist/modules/new/lib/preparation/loadPortableTemplateConfig.cjs.js +18 -18
- package/dist/{lib → modules/new/lib}/version.cjs.js +22 -34
- package/dist/modules/test/commands/package/test.cjs.js +1 -10
- package/dist/modules/test/commands/repo/test.cjs.js +51 -39
- package/dist/modules/test/index.cjs.js +2 -32
- package/dist/modules/translations/commands/export.cjs.js +25 -1
- package/dist/modules/translations/commands/import.cjs.js +25 -1
- package/dist/modules/translations/index.cjs.js +2 -37
- package/dist/packages/backend-defaults/package.json.cjs.js +1 -1
- package/dist/packages/backend-plugin-api/package.json.cjs.js +1 -1
- package/dist/packages/backend-test-utils/package.json.cjs.js +1 -1
- package/dist/packages/catalog-client/package.json.cjs.js +1 -1
- package/dist/packages/cli/package.json.cjs.js +7 -6
- package/dist/packages/core-app-api/package.json.cjs.js +1 -1
- package/dist/packages/core-components/package.json.cjs.js +1 -1
- package/dist/packages/core-plugin-api/package.json.cjs.js +1 -1
- package/dist/packages/dev-utils/package.json.cjs.js +1 -1
- package/dist/packages/frontend-defaults/package.json.cjs.js +1 -1
- package/dist/packages/frontend-plugin-api/package.json.cjs.js +1 -1
- package/dist/packages/frontend-test-utils/package.json.cjs.js +1 -1
- package/dist/plugins/auth-backend/package.json.cjs.js +1 -1
- package/dist/plugins/auth-backend-module-guest-provider/package.json.cjs.js +1 -1
- package/dist/plugins/catalog-node/package.json.cjs.js +1 -1
- package/dist/plugins/scaffolder-node/package.json.cjs.js +1 -1
- package/dist/plugins/scaffolder-node-test-utils/package.json.cjs.js +1 -1
- package/dist/wiring/CliInitializer.cjs.js +24 -7
- package/dist/wiring/version.cjs.js +20 -0
- package/package.json +26 -22
- package/dist/lib/cache/SuccessCache.cjs.js +0 -79
- package/dist/lib/lazy.cjs.js +0 -22
- package/dist/lib/optionsParser.cjs.js +0 -37
- package/dist/lib/versioning/Lockfile.cjs.js +0 -89
- package/dist/lib/yarnPlugin.cjs.js +0 -46
- /package/dist/modules/{maintenance → build}/lib/publishing.cjs.js +0 -0
- /package/dist/{lib → modules/build/lib}/typeDistProject.cjs.js +0 -0
- /package/dist/{lib → modules/migrate/lib}/versioning/packages.cjs.js +0 -0
- /package/dist/{lib → modules/migrate/lib}/versioning/yarn.cjs.js +0 -0
- /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
|
|
340
|
-
|
|
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
|