hereya-cli 0.86.0 → 0.86.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/README.md +64 -64
- package/dist/commands/deploy/index.js +14 -36
- package/dist/commands/undeploy/index.js +14 -36
- package/dist/executor/context.js +12 -15
- package/dist/lib/active-cloud.d.ts +31 -0
- package/dist/lib/active-cloud.js +55 -0
- package/dist/lib/hereya-token.js +10 -11
- package/dist/lib/package/index.js +20 -24
- package/oclif.manifest.json +298 -298
- package/package.json +1 -1
|
@@ -2,12 +2,12 @@ import { Command, Flags } from '@oclif/core';
|
|
|
2
2
|
import { getRendererClass, Listr, ListrLogger, ListrLogLevels } from 'listr2';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { CloudBackend } from '../../backend/cloud/cloud-backend.js';
|
|
5
|
-
import { getCloudCredentials, loadBackendConfig } from '../../backend/config.js';
|
|
6
5
|
import { getBackend } from '../../backend/index.js';
|
|
7
6
|
import { getExecutor } from '../../executor/index.js';
|
|
7
|
+
import { getActiveCloudAuth } from '../../lib/active-cloud.js';
|
|
8
8
|
import { getConfigManager } from '../../lib/config/index.js';
|
|
9
9
|
import { getEnvManager } from '../../lib/env/index.js';
|
|
10
|
-
import {
|
|
10
|
+
import { withEphemeralToken } from '../../lib/ephemeral-token.js';
|
|
11
11
|
import { gitUtils } from '../../lib/git-utils.js';
|
|
12
12
|
import { getDefaultLogger, getLogger, getLogPath, isDebug, setDebug } from '../../lib/log.js';
|
|
13
13
|
import { getParameterManager } from '../../lib/parameter/index.js';
|
|
@@ -338,41 +338,19 @@ See ${getLogPath()} for more details`);
|
|
|
338
338
|
this.error(cleanCheck.reason);
|
|
339
339
|
}
|
|
340
340
|
myLogger.log(ListrLogLevels.STARTED, `Undeploying ${input.workspace} via remote executor (branch: ${input.gitBranch})`);
|
|
341
|
-
// 2.
|
|
342
|
-
//
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
const resolvedWorkspaceName = resolveWorkspaceName(input.workspace, input.project);
|
|
347
|
-
let cloudBackend;
|
|
348
|
-
if (ephemeral) {
|
|
349
|
-
const url = ephemeral.cloudUrl
|
|
350
|
-
?? process.env.HEREYA_CLOUD_URL
|
|
351
|
-
?? (await loadBackendConfig()).cloud?.url
|
|
352
|
-
?? 'https://cloud.hereya.dev';
|
|
353
|
-
cloudBackend = new CloudBackend({
|
|
354
|
-
accessToken: ephemeral.token,
|
|
355
|
-
clientId: '',
|
|
356
|
-
refreshToken: '',
|
|
357
|
-
url,
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
else {
|
|
361
|
-
const backendConfig = await loadBackendConfig();
|
|
362
|
-
if (!backendConfig.cloud) {
|
|
363
|
-
this.error('Remote undeployment requires cloud backend. Run `hereya login` first.');
|
|
364
|
-
}
|
|
365
|
-
const credentials = await getCloudCredentials(backendConfig.cloud.clientId);
|
|
366
|
-
if (!credentials) {
|
|
367
|
-
this.error('Cloud credentials not found. Run `hereya login` first.');
|
|
368
|
-
}
|
|
369
|
-
cloudBackend = new CloudBackend({
|
|
370
|
-
accessToken: credentials.accessToken,
|
|
371
|
-
clientId: backendConfig.cloud.clientId,
|
|
372
|
-
refreshToken: credentials.refreshToken,
|
|
373
|
-
url: backendConfig.cloud.url,
|
|
374
|
-
});
|
|
341
|
+
// 2. Resolve cloud auth (ephemeral --token first, then persisted creds)
|
|
342
|
+
// and build a CloudBackend for job submission + polling.
|
|
343
|
+
const auth = await getActiveCloudAuth();
|
|
344
|
+
if (!auth) {
|
|
345
|
+
this.error('Remote undeployment requires cloud backend. Run `hereya login` or pass `--token <token>`.');
|
|
375
346
|
}
|
|
347
|
+
const resolvedWorkspaceName = resolveWorkspaceName(input.workspace, input.project);
|
|
348
|
+
const cloudBackend = new CloudBackend({
|
|
349
|
+
accessToken: auth.accessToken,
|
|
350
|
+
clientId: auth.clientId ?? '',
|
|
351
|
+
refreshToken: auth.refreshToken ?? '',
|
|
352
|
+
url: auth.cloudUrl,
|
|
353
|
+
});
|
|
376
354
|
// 3. Submit undeploy job
|
|
377
355
|
const submitResult = await cloudBackend.submitExecutorJob({
|
|
378
356
|
payload: {
|
package/dist/executor/context.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { getBackend } from '../backend/index.js';
|
|
2
|
+
import { getActiveCloudAuth } from '../lib/active-cloud.js';
|
|
3
3
|
import { resolveWorkspaceName } from '../lib/workspace-utils.js';
|
|
4
4
|
import { getExecutor } from './index.js';
|
|
5
5
|
export async function getExecutorForWorkspace(workspaceName, project) {
|
|
6
6
|
if (!workspaceName) {
|
|
7
7
|
return getExecutor();
|
|
8
8
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
// Honors both `hereya login` (persisted on disk) and an in-scope
|
|
10
|
+
// ephemeral `--token`. Without this, `--token` invocations silently
|
|
11
|
+
// fell through to the local executor regardless of whether the
|
|
12
|
+
// workspace actually has a remote executor.
|
|
13
|
+
const auth = await getActiveCloudAuth();
|
|
14
|
+
if (!auth) {
|
|
14
15
|
return getExecutor();
|
|
15
16
|
}
|
|
16
17
|
const resolvedName = resolveWorkspaceName(workspaceName, project);
|
|
@@ -23,16 +24,12 @@ export async function getExecutorForWorkspace(workspaceName, project) {
|
|
|
23
24
|
if (!workspace.hasExecutor) {
|
|
24
25
|
return getExecutor();
|
|
25
26
|
}
|
|
26
|
-
const credentials = await getCloudCredentials(backendConfig.cloud.clientId);
|
|
27
|
-
if (!credentials) {
|
|
28
|
-
return getExecutor();
|
|
29
|
-
}
|
|
30
27
|
return getExecutor({
|
|
31
28
|
cloudConfig: {
|
|
32
|
-
accessToken:
|
|
33
|
-
clientId:
|
|
34
|
-
refreshToken:
|
|
35
|
-
url:
|
|
29
|
+
accessToken: auth.accessToken,
|
|
30
|
+
clientId: auth.clientId ?? '',
|
|
31
|
+
refreshToken: auth.refreshToken ?? '',
|
|
32
|
+
url: auth.cloudUrl,
|
|
36
33
|
},
|
|
37
34
|
workspace: resolvedName,
|
|
38
35
|
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The active cloud auth credentials for this CLI invocation.
|
|
3
|
+
*
|
|
4
|
+
* Either sourced from an ephemeral `--token` (in which case `clientId` and
|
|
5
|
+
* `refreshToken` are `null` because there is no persisted client identity
|
|
6
|
+
* and the token cannot be refreshed) or from the persisted `backend.yaml`
|
|
7
|
+
* + keychain credentials.
|
|
8
|
+
*/
|
|
9
|
+
export interface ActiveCloudAuth {
|
|
10
|
+
accessToken: string;
|
|
11
|
+
clientId: null | string;
|
|
12
|
+
cloudUrl: string;
|
|
13
|
+
refreshToken: null | string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Returns the active cloud auth, preferring an in-scope ephemeral `--token`
|
|
17
|
+
* over the persisted `backend.yaml` + keychain credentials. Returns `null`
|
|
18
|
+
* when neither is available.
|
|
19
|
+
*
|
|
20
|
+
* **This is the single source of truth for "are we authenticated to
|
|
21
|
+
* hereya-cloud right now?"** — every code path that previously read from
|
|
22
|
+
* `loadBackendConfig()` + `getCloudCredentials()` directly (and therefore
|
|
23
|
+
* silently ignored an ephemeral `--token`) should call this instead.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getActiveCloudAuth(): Promise<ActiveCloudAuth | null>;
|
|
26
|
+
/**
|
|
27
|
+
* Whether the cloud backend is "active" — either via `hereya login` or via
|
|
28
|
+
* an in-scope ephemeral `--token`. Used to decide whether to consult the
|
|
29
|
+
* cloud package registry, the remote executor, etc.
|
|
30
|
+
*/
|
|
31
|
+
export declare function isCloudBackendActive(): Promise<boolean>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { getCloudCredentials, loadBackendConfig } from '../backend/config.js';
|
|
2
|
+
import { BackendType } from '../backend/index.js';
|
|
3
|
+
import { getEphemeralToken } from './ephemeral-token.js';
|
|
4
|
+
const DEFAULT_CLOUD_URL = 'https://cloud.hereya.dev';
|
|
5
|
+
/**
|
|
6
|
+
* Returns the active cloud auth, preferring an in-scope ephemeral `--token`
|
|
7
|
+
* over the persisted `backend.yaml` + keychain credentials. Returns `null`
|
|
8
|
+
* when neither is available.
|
|
9
|
+
*
|
|
10
|
+
* **This is the single source of truth for "are we authenticated to
|
|
11
|
+
* hereya-cloud right now?"** — every code path that previously read from
|
|
12
|
+
* `loadBackendConfig()` + `getCloudCredentials()` directly (and therefore
|
|
13
|
+
* silently ignored an ephemeral `--token`) should call this instead.
|
|
14
|
+
*/
|
|
15
|
+
export async function getActiveCloudAuth() {
|
|
16
|
+
const ephemeral = getEphemeralToken();
|
|
17
|
+
if (ephemeral) {
|
|
18
|
+
const cloudUrl = ephemeral.cloudUrl
|
|
19
|
+
?? process.env.HEREYA_CLOUD_URL
|
|
20
|
+
?? (await loadBackendConfig()).cloud?.url
|
|
21
|
+
?? DEFAULT_CLOUD_URL;
|
|
22
|
+
return {
|
|
23
|
+
accessToken: ephemeral.token,
|
|
24
|
+
clientId: null,
|
|
25
|
+
cloudUrl,
|
|
26
|
+
refreshToken: null,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const config = await loadBackendConfig();
|
|
30
|
+
if (config.current !== BackendType.Cloud || !config.cloud) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const credentials = await getCloudCredentials(config.cloud.clientId);
|
|
34
|
+
if (!credentials) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
accessToken: credentials.accessToken,
|
|
39
|
+
clientId: config.cloud.clientId,
|
|
40
|
+
cloudUrl: config.cloud.url,
|
|
41
|
+
refreshToken: credentials.refreshToken,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Whether the cloud backend is "active" — either via `hereya login` or via
|
|
46
|
+
* an in-scope ephemeral `--token`. Used to decide whether to consult the
|
|
47
|
+
* cloud package registry, the remote executor, etc.
|
|
48
|
+
*/
|
|
49
|
+
export async function isCloudBackendActive() {
|
|
50
|
+
if (getEphemeralToken()) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
const config = await loadBackendConfig();
|
|
54
|
+
return config.current === BackendType.Cloud;
|
|
55
|
+
}
|
package/dist/lib/hereya-token.js
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getActiveCloudAuth } from './active-cloud.js';
|
|
2
2
|
export const hereyaTokenUtils = {
|
|
3
3
|
async generateHereyaToken(description) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
if (!credentials) {
|
|
4
|
+
// Honors both `hereya login` (persisted) and an in-scope ephemeral
|
|
5
|
+
// `--token`. Note: workspace-scoped CLI tokens (kind:"cli") are
|
|
6
|
+
// intentionally rejected by the cloud's /api/personal-tokens route —
|
|
7
|
+
// only user-level tokens can mint long-lived personal tokens.
|
|
8
|
+
const auth = await getActiveCloudAuth();
|
|
9
|
+
if (!auth) {
|
|
11
10
|
return null;
|
|
12
11
|
}
|
|
13
12
|
const formData = new FormData();
|
|
14
13
|
formData.append('description', description);
|
|
15
14
|
formData.append('expiresInDays', '365');
|
|
16
|
-
const response = await fetch(`${
|
|
15
|
+
const response = await fetch(`${auth.cloudUrl}/api/personal-tokens`, {
|
|
17
16
|
body: formData,
|
|
18
17
|
headers: {
|
|
19
|
-
Authorization: `Bearer ${
|
|
18
|
+
Authorization: `Bearer ${auth.accessToken}`,
|
|
20
19
|
},
|
|
21
20
|
method: 'POST',
|
|
22
21
|
});
|
|
@@ -24,6 +23,6 @@ export const hereyaTokenUtils = {
|
|
|
24
23
|
return null;
|
|
25
24
|
}
|
|
26
25
|
const result = await response.json();
|
|
27
|
-
return { cloudUrl:
|
|
26
|
+
return { cloudUrl: auth.cloudUrl, token: result.data.token };
|
|
28
27
|
},
|
|
29
28
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as yaml from 'yaml';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import {
|
|
4
|
-
import { BackendType, getBackend } from '../../backend/index.js';
|
|
3
|
+
import { getBackend } from '../../backend/index.js';
|
|
5
4
|
import { IacType } from '../../iac/common.js';
|
|
6
5
|
import { InfrastructureType } from '../../infrastructure/common.js';
|
|
6
|
+
import { isCloudBackendActive } from '../active-cloud.js';
|
|
7
7
|
import { CloudPackageManager } from './cloud.js';
|
|
8
8
|
import { GitHubPackageManager } from './github.js';
|
|
9
9
|
import { LocalPackageManager } from './local.js';
|
|
@@ -31,23 +31,21 @@ export async function resolvePackage(input) {
|
|
|
31
31
|
return { found: false, reason: 'Invalid package format. Package name cannot contain dots (.) nor double dashes (--)' };
|
|
32
32
|
}
|
|
33
33
|
const isLocal = packageName.startsWith('local/');
|
|
34
|
-
// Try cloud first if not local and backend is cloud
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return result; // Success
|
|
41
|
-
}
|
|
42
|
-
// Failed - check if we should fallback to GitHub or return the error
|
|
43
|
-
if (!packageName.includes('/')) {
|
|
44
|
-
// Simple package names can't fallback to GitHub, return the detailed error
|
|
45
|
-
return result;
|
|
46
|
-
}
|
|
47
|
-
// For owner/repo format, we can fallback to GitHub
|
|
48
|
-
input.logger?.debug(`Package ${packageName} not found in registry, trying GitHub...`);
|
|
49
|
-
input.logger?.debug(`Registry error: ${result.reason}`);
|
|
34
|
+
// Try cloud first if not local and backend is cloud (or an ephemeral
|
|
35
|
+
// --token is in scope, which acts as Cloud regardless of on-disk config).
|
|
36
|
+
if (!isLocal && await isCloudBackendActive()) {
|
|
37
|
+
const result = await tryCloudResolution(input);
|
|
38
|
+
if (result.found) {
|
|
39
|
+
return result; // Success
|
|
50
40
|
}
|
|
41
|
+
// Failed - check if we should fallback to GitHub or return the error
|
|
42
|
+
if (!packageName.includes('/')) {
|
|
43
|
+
// Simple package names can't fallback to GitHub, return the detailed error
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
// For owner/repo format, we can fallback to GitHub
|
|
47
|
+
input.logger?.debug(`Package ${packageName} not found in registry, trying GitHub...`);
|
|
48
|
+
input.logger?.debug(`Registry error: ${result.reason}`);
|
|
51
49
|
}
|
|
52
50
|
// Determine protocol for standard resolution
|
|
53
51
|
const protocol = isLocal ? 'local' : '';
|
|
@@ -63,12 +61,10 @@ export async function downloadPackage(pkgUrl, destPath) {
|
|
|
63
61
|
if (pkgUrl.startsWith('local/')) {
|
|
64
62
|
protocol = 'local';
|
|
65
63
|
}
|
|
66
|
-
else if (pkgUrl.includes('#')) {
|
|
67
|
-
// Registry package URLs contain commit SHA
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
protocol = 'cloud';
|
|
71
|
-
}
|
|
64
|
+
else if (pkgUrl.includes('#') && await isCloudBackendActive()) {
|
|
65
|
+
// Registry package URLs contain commit SHA. Cloud branch fires when
|
|
66
|
+
// backend.yaml says Cloud OR an ephemeral --token is in scope.
|
|
67
|
+
protocol = 'cloud';
|
|
72
68
|
}
|
|
73
69
|
// Otherwise defaults to GitHub
|
|
74
70
|
const packageManager = await getPackageManager(protocol);
|