@xano/cli 1.2.0 → 1.3.1-beta.0
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 +381 -4
- package/dist/base-command.d.ts +54 -36
- package/dist/base-command.js +246 -169
- package/dist/commands/auth/index.d.ts +1 -1
- package/dist/commands/auth/index.js +7 -7
- package/dist/commands/branch/create/index.d.ts +1 -1
- package/dist/commands/branch/create/index.js +1 -1
- package/dist/commands/branch/set_live/index.d.ts +22 -0
- package/dist/commands/branch/set_live/index.js +60 -1
- package/dist/commands/ephemeral/create/index.d.ts +18 -0
- package/dist/commands/ephemeral/create/index.js +103 -0
- package/dist/commands/ephemeral/delete/index.d.ts +18 -0
- package/dist/commands/ephemeral/delete/index.js +99 -0
- package/dist/commands/ephemeral/edit/index.d.ts +18 -0
- package/dist/commands/ephemeral/edit/index.js +109 -0
- package/dist/commands/ephemeral/get/index.d.ts +21 -0
- package/dist/commands/ephemeral/get/index.js +125 -0
- package/dist/commands/ephemeral/impersonate/index.d.ts +20 -0
- package/dist/commands/ephemeral/impersonate/index.js +120 -0
- package/dist/commands/ephemeral/list/index.d.ts +19 -0
- package/dist/commands/ephemeral/list/index.js +123 -0
- package/dist/commands/ephemeral/pull/index.d.ts +19 -0
- package/dist/commands/ephemeral/pull/index.js +163 -0
- package/dist/commands/ephemeral/push/index.d.ts +35 -0
- package/dist/commands/ephemeral/push/index.js +228 -0
- package/dist/commands/ephemeral/static_host/build/delete/index.d.ts +20 -0
- package/dist/commands/ephemeral/static_host/build/delete/index.js +121 -0
- package/dist/commands/ephemeral/static_host/build/get/index.d.ts +18 -0
- package/dist/commands/ephemeral/static_host/build/get/index.js +137 -0
- package/dist/commands/ephemeral/static_host/build/list/index.d.ts +18 -0
- package/dist/commands/ephemeral/static_host/build/list/index.js +137 -0
- package/dist/commands/ephemeral/static_host/build/pull/index.d.ts +53 -0
- package/dist/commands/ephemeral/static_host/build/pull/index.js +308 -0
- package/dist/commands/ephemeral/static_host/build/push/index.d.ts +24 -0
- package/dist/commands/ephemeral/static_host/build/push/index.js +250 -0
- package/dist/commands/ephemeral/static_host/create/index.d.ts +18 -0
- package/dist/commands/ephemeral/static_host/create/index.js +92 -0
- package/dist/commands/ephemeral/static_host/deploy/index.d.ts +19 -0
- package/dist/commands/ephemeral/static_host/deploy/index.js +112 -0
- package/dist/commands/ephemeral/static_host/edit/index.d.ts +24 -0
- package/dist/commands/ephemeral/static_host/edit/index.js +158 -0
- package/dist/commands/ephemeral/static_host/get/index.d.ts +19 -0
- package/dist/commands/ephemeral/static_host/get/index.js +101 -0
- package/dist/commands/ephemeral/static_host/list/index.d.ts +17 -0
- package/dist/commands/ephemeral/static_host/list/index.js +132 -0
- package/dist/commands/flatten/index.d.ts +34 -0
- package/dist/commands/flatten/index.js +140 -0
- package/dist/commands/function/create/index.d.ts +15 -0
- package/dist/commands/function/create/index.js +41 -1
- package/dist/commands/function/edit/index.d.ts +15 -0
- package/dist/commands/function/edit/index.js +66 -32
- package/dist/commands/function/get/index.js +2 -3
- package/dist/commands/function/run/index.d.ts +1 -0
- package/dist/commands/function/run/index.js +19 -6
- package/dist/commands/policy/catalogue/index.d.ts +15 -0
- package/dist/commands/policy/catalogue/index.js +29 -0
- package/dist/commands/policy/create/index.d.ts +18 -0
- package/dist/commands/policy/create/index.js +39 -0
- package/dist/commands/policy/delete/index.d.ts +19 -0
- package/dist/commands/policy/delete/index.js +61 -0
- package/dist/commands/policy/evaluate/index.d.ts +32 -0
- package/dist/commands/policy/evaluate/index.js +71 -0
- package/dist/commands/policy/list/index.d.ts +14 -0
- package/dist/commands/policy/list/index.js +21 -0
- package/dist/commands/policy/parse/index.d.ts +19 -0
- package/dist/commands/policy/parse/index.js +18 -0
- package/dist/commands/policy/publish/index.d.ts +20 -0
- package/dist/commands/policy/publish/index.js +38 -0
- package/dist/commands/policy/runs/index.d.ts +54 -0
- package/dist/commands/policy/runs/index.js +273 -0
- package/dist/commands/policy/status/index.d.ts +21 -0
- package/dist/commands/policy/status/index.js +54 -0
- package/dist/commands/profile/create/index.d.ts +1 -1
- package/dist/commands/profile/create/index.js +7 -7
- package/dist/commands/profile/me/index.js +1 -1
- package/dist/commands/profile/use/index.js +0 -4
- package/dist/commands/profile/wizard/index.d.ts +1 -1
- package/dist/commands/profile/wizard/index.js +12 -10
- package/dist/commands/profile/workspace/set/index.js +1 -1
- package/dist/commands/release/create/index.js +7 -0
- package/dist/commands/release/deploy/index.d.ts +28 -0
- package/dist/commands/release/deploy/index.js +90 -6
- package/dist/commands/release/edit/index.js +2 -2
- package/dist/commands/release/export/index.js +1 -2
- package/dist/commands/release/import/index.js +6 -2
- package/dist/commands/release/list/index.d.ts +7 -0
- package/dist/commands/release/list/index.js +54 -10
- package/dist/commands/release/pull/index.d.ts +0 -6
- package/dist/commands/release/pull/index.js +13 -99
- package/dist/commands/release/push/index.js +21 -3
- package/dist/commands/sandbox/env/get_all/index.js +1 -1
- package/dist/commands/sandbox/env/set_all/index.js +1 -1
- package/dist/commands/sandbox/get/index.js +1 -1
- package/dist/commands/sandbox/license/get/index.js +1 -1
- package/dist/commands/sandbox/license/set/index.js +1 -1
- package/dist/commands/sandbox/pull/index.d.ts +0 -1
- package/dist/commands/sandbox/pull/index.js +13 -81
- package/dist/commands/sandbox/push/index.d.ts +1 -1
- package/dist/commands/sandbox/push/index.js +11 -6
- package/dist/commands/skills/pull/index.d.ts +30 -0
- package/dist/commands/skills/pull/index.js +118 -0
- package/dist/commands/static_host/build/create/index.d.ts +1 -1
- package/dist/commands/static_host/build/create/index.js +2 -2
- package/dist/commands/static_host/build/pull/index.js +1 -1
- package/dist/commands/static_host/build/push/index.js +1 -1
- package/dist/commands/static_host/edit/index.js +1 -1
- package/dist/commands/tenant/backup/export/index.js +1 -2
- package/dist/commands/tenant/backup/import/index.js +1 -1
- package/dist/commands/tenant/cluster/create/index.js +1 -1
- package/dist/commands/tenant/cluster/license/get/index.js +1 -1
- package/dist/commands/tenant/cluster/license/set/index.js +1 -1
- package/dist/commands/tenant/create/index.d.ts +4 -1
- package/dist/commands/tenant/create/index.js +26 -5
- package/dist/commands/tenant/deploy_platform/index.js +1 -1
- package/dist/commands/tenant/deploy_release/index.d.ts +40 -0
- package/dist/commands/tenant/deploy_release/index.js +186 -4
- package/dist/commands/tenant/edit/index.d.ts +2 -0
- package/dist/commands/tenant/edit/index.js +46 -11
- package/dist/commands/tenant/env/get_all/index.js +1 -1
- package/dist/commands/tenant/env/set_all/index.js +1 -1
- package/dist/commands/tenant/impersonate/index.d.ts +1 -1
- package/dist/commands/tenant/impersonate/index.js +14 -14
- package/dist/commands/tenant/license/get/index.js +1 -1
- package/dist/commands/tenant/license/set/index.js +1 -1
- package/dist/commands/tenant/pull/index.d.ts +0 -6
- package/dist/commands/tenant/pull/index.js +14 -100
- package/dist/commands/tenant_deploy_request/bypass/index.d.ts +31 -0
- package/dist/commands/tenant_deploy_request/bypass/index.js +109 -0
- package/dist/commands/tenant_deploy_request/create/index.d.ts +21 -0
- package/dist/commands/tenant_deploy_request/create/index.js +108 -0
- package/dist/commands/tenant_deploy_request/edit/index.d.ts +22 -0
- package/dist/commands/tenant_deploy_request/edit/index.js +103 -0
- package/dist/commands/tenant_deploy_request/get/index.d.ts +25 -0
- package/dist/commands/tenant_deploy_request/get/index.js +116 -0
- package/dist/commands/tenant_deploy_request/list/index.d.ts +20 -0
- package/dist/commands/tenant_deploy_request/list/index.js +122 -0
- package/dist/commands/tenant_deploy_request/revision/index.d.ts +21 -0
- package/dist/commands/tenant_deploy_request/revision/index.js +83 -0
- package/dist/commands/tenant_deploy_request/set_status/index.d.ts +38 -0
- package/dist/commands/tenant_deploy_request/set_status/index.js +159 -0
- package/dist/commands/workspace/git/pull/index.d.ts +4 -4
- package/dist/commands/workspace/git/pull/index.js +158 -97
- package/dist/commands/workspace/pull/index.d.ts +9 -5
- package/dist/commands/workspace/pull/index.js +83 -106
- package/dist/commands/workspace/push/index.d.ts +19 -0
- package/dist/commands/workspace/push/index.js +124 -5
- package/dist/help.js +4 -2
- package/dist/policy-command.d.ts +90 -0
- package/dist/policy-command.js +94 -0
- package/dist/update-check.d.ts +1 -1
- package/dist/update-check.js +1 -1
- package/dist/utils/document-parser.d.ts +126 -0
- package/dist/utils/document-parser.js +332 -4
- package/dist/utils/flatten.d.ts +36 -0
- package/dist/utils/flatten.js +68 -0
- package/dist/utils/knowledge-sync.d.ts +1 -0
- package/dist/utils/knowledge-sync.js +11 -6
- package/dist/utils/microservice-wait.d.ts +53 -0
- package/dist/utils/microservice-wait.js +84 -0
- package/dist/utils/multidoc-push.d.ts +143 -2
- package/dist/utils/multidoc-push.js +479 -40
- package/dist/utils/paging.d.ts +2 -1
- package/dist/utils/paging.js +0 -1
- package/dist/utils/policy/catalogue.d.ts +7 -0
- package/dist/utils/policy/catalogue.js +72 -0
- package/dist/utils/policy/errors.d.ts +12 -0
- package/dist/utils/policy/errors.js +61 -0
- package/dist/utils/policy/feedback.d.ts +68 -0
- package/dist/utils/policy/feedback.js +165 -0
- package/dist/utils/policy/findings.d.ts +23 -0
- package/dist/utils/policy/findings.js +65 -0
- package/dist/utils/policy/gate.d.ts +161 -0
- package/dist/utils/policy/gate.js +201 -0
- package/dist/utils/policy/goal.d.ts +46 -0
- package/dist/utils/policy/goal.js +95 -0
- package/dist/utils/policy/permission.d.ts +23 -0
- package/dist/utils/policy/permission.js +84 -0
- package/dist/utils/policy/push-gate.d.ts +5 -0
- package/dist/utils/policy/push-gate.js +29 -0
- package/dist/utils/policy/release.d.ts +85 -0
- package/dist/utils/policy/release.js +115 -0
- package/dist/utils/policy/request.d.ts +55 -0
- package/dist/utils/policy/request.js +115 -0
- package/dist/utils/policy/runs.d.ts +30 -0
- package/dist/utils/policy/runs.js +101 -0
- package/dist/utils/policy/status.d.ts +47 -0
- package/dist/utils/policy/status.js +117 -0
- package/dist/utils/policy/types.d.ts +185 -0
- package/dist/utils/policy/types.js +1 -0
- package/dist/utils/reference-checker.js +14 -3
- package/oclif.manifest.json +8538 -3445
- package/package.json +16 -1
package/dist/base-command.d.ts
CHANGED
|
@@ -15,6 +15,11 @@ export interface CredentialsFile {
|
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
17
|
export declare function buildUserAgent(version: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* True when `error` is the "foreign dispatcher rejected by built-in fetch"
|
|
20
|
+
* failure (Node 26+). The real code lives on `error.cause.code`.
|
|
21
|
+
*/
|
|
22
|
+
export declare function isInvalidDispatcherError(error: unknown): boolean;
|
|
18
23
|
export interface SandboxTenant {
|
|
19
24
|
created_at?: string;
|
|
20
25
|
description?: string;
|
|
@@ -22,7 +27,7 @@ export interface SandboxTenant {
|
|
|
22
27
|
ephemeral?: boolean;
|
|
23
28
|
id: number;
|
|
24
29
|
name: string;
|
|
25
|
-
sandbox_expires_at?:
|
|
30
|
+
sandbox_expires_at?: number | string;
|
|
26
31
|
state?: string;
|
|
27
32
|
xano_domain?: string;
|
|
28
33
|
}
|
|
@@ -52,57 +57,40 @@ export default abstract class BaseCommand extends Command {
|
|
|
52
57
|
config: LocalProfileConfig;
|
|
53
58
|
path: string;
|
|
54
59
|
};
|
|
55
|
-
protected updateNotice:
|
|
56
|
-
init(): Promise<void>;
|
|
57
|
-
/**
|
|
58
|
-
* Find and parse the nearest project-local profile.yaml, unless an explicit
|
|
59
|
-
* -p/XANO_PROFILE was given (in which case the local file is ignored).
|
|
60
|
-
*/
|
|
61
|
-
private loadLocalProfile;
|
|
62
|
-
/** Print the one-line target banner when a local profile.yaml is in effect. */
|
|
63
|
-
private maybePrintLocalProfileBanner;
|
|
64
|
-
finally(_: Error | undefined): Promise<void>;
|
|
65
|
-
private isJsonOutput;
|
|
60
|
+
protected updateNotice: null | string;
|
|
66
61
|
/**
|
|
67
62
|
* Apply insecure TLS mode if the active profile has insecure: true.
|
|
68
63
|
* Sets NODE_TLS_REJECT_UNAUTHORIZED=0 so all fetch() calls skip cert verification.
|
|
69
64
|
*/
|
|
70
65
|
protected applyInsecureFromProfile(): void;
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Report a failure as exit 1 for commands that reserve exit 2 for findings: operational errors,
|
|
68
|
+
* flag errors and oclif's own default exit 2 all become 1, and under `-o json` the failure is also
|
|
69
|
+
* written to stdout as `{"error": {"exit": 1, "message"}}`. A deliberate exit 0 passes through.
|
|
70
|
+
*/
|
|
71
|
+
protected catchAsOperational(error: Error & {
|
|
72
|
+
oclif?: {
|
|
73
|
+
exit?: number;
|
|
74
|
+
};
|
|
75
|
+
}): Promise<void>;
|
|
76
|
+
finally(_: Error | undefined): Promise<void>;
|
|
73
77
|
/**
|
|
74
78
|
* Get the resolved credentials file path, respecting --config flag and XANO_CONFIG env var.
|
|
75
79
|
* Reads -c/--config from process.argv directly because oclif doesn't set this.flags
|
|
76
80
|
* from parsed results — the static flags property is the flag definition, not parsed values.
|
|
77
81
|
*/
|
|
78
82
|
protected getCredentialsPath(): string;
|
|
79
|
-
protected
|
|
83
|
+
protected getDefaultProfile(): string;
|
|
80
84
|
/**
|
|
81
85
|
* Get or create the singleton sandbox environment for the authenticated user.
|
|
82
86
|
* Returns the sandbox object (existing or newly created).
|
|
83
87
|
*/
|
|
84
88
|
protected getOrCreateSandbox(profile: ProfileConfig, verbose: boolean): Promise<SandboxTenant>;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
protected resolveProfile(flags: {
|
|
91
|
-
profile?: string;
|
|
92
|
-
}): {
|
|
93
|
-
profile: ProfileConfig;
|
|
94
|
-
profileName: string;
|
|
95
|
-
};
|
|
96
|
-
/**
|
|
97
|
-
* Parse an API error response and return a clean error message.
|
|
98
|
-
* Extracts the message from JSON responses and adds context for common errors.
|
|
99
|
-
*/
|
|
100
|
-
protected parseApiError(response: Response, fallbackPrefix: string): Promise<string>;
|
|
101
|
-
/**
|
|
102
|
-
* Make an HTTP request with optional verbose logging.
|
|
103
|
-
* Use this for all Metadata API calls to support the --verbose flag.
|
|
104
|
-
*/
|
|
105
|
-
protected verboseFetch(url: string, options: RequestInit, verbose: boolean, authToken?: string): Promise<Response>;
|
|
89
|
+
protected getProfile(): string | undefined;
|
|
90
|
+
init(): Promise<void>;
|
|
91
|
+
/** Raw-argv check: the parsed flags are not available yet when the banner prints. */
|
|
92
|
+
protected isJsonOutput(): boolean;
|
|
93
|
+
protected loadCredentialsFile(): CredentialsFile | null;
|
|
106
94
|
/**
|
|
107
95
|
* Poll a static-host build until it reaches a terminal status (ok | error),
|
|
108
96
|
* showing a live, ticking spinner with the current stage and elapsed time —
|
|
@@ -117,17 +105,47 @@ export default abstract class BaseCommand extends Command {
|
|
|
117
105
|
protected logStaticHostUrls(opts: {
|
|
118
106
|
profile: ProfileConfig;
|
|
119
107
|
staticHost: string;
|
|
108
|
+
tenantName?: string;
|
|
120
109
|
verbose: boolean;
|
|
121
110
|
workspaceId: string;
|
|
122
111
|
}): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* Parse an API error response and return a clean error message.
|
|
114
|
+
* Extracts the message from JSON responses and adds context for common errors.
|
|
115
|
+
*/
|
|
116
|
+
protected parseApiError(response: Response, fallbackPrefix: string): Promise<string>;
|
|
117
|
+
/**
|
|
118
|
+
* Resolve the profile from flags and any project-local profile.yaml,
|
|
119
|
+
* validating instance_origin and access_token exist.
|
|
120
|
+
* Precedence: -p/XANO_PROFILE > profile.yaml > credentials default.
|
|
121
|
+
*/
|
|
122
|
+
protected resolveProfile(flags: {
|
|
123
|
+
profile?: string;
|
|
124
|
+
}): {
|
|
125
|
+
profile: ProfileConfig;
|
|
126
|
+
profileName: string;
|
|
127
|
+
};
|
|
128
|
+
/**
|
|
129
|
+
* Make an HTTP request with optional verbose logging.
|
|
130
|
+
* Use this for all Metadata API calls to support the --verbose flag.
|
|
131
|
+
*/
|
|
132
|
+
protected verboseFetch(url: string, options: RequestInit, verbose: boolean, authToken?: string): Promise<Response>;
|
|
123
133
|
protected waitForBuild(opts: {
|
|
124
134
|
buildId: number | string;
|
|
125
135
|
intervalMs?: number;
|
|
126
136
|
profile: ProfileConfig;
|
|
127
137
|
quiet?: boolean;
|
|
128
138
|
staticHost: string;
|
|
139
|
+
tenantName?: string;
|
|
129
140
|
timeoutMs?: number;
|
|
130
141
|
verbose: boolean;
|
|
131
142
|
workspaceId: string;
|
|
132
143
|
}): Promise<string>;
|
|
144
|
+
/**
|
|
145
|
+
* Find and parse the nearest project-local profile.yaml, unless an explicit
|
|
146
|
+
* -p/XANO_PROFILE was given (in which case the local file is ignored).
|
|
147
|
+
*/
|
|
148
|
+
private loadLocalProfile;
|
|
149
|
+
/** Print the one-line target banner when a local profile.yaml is in effect. */
|
|
150
|
+
private maybePrintLocalProfileBanner;
|
|
133
151
|
}
|
package/dist/base-command.js
CHANGED
|
@@ -2,7 +2,7 @@ import { Command, Flags, ux } from '@oclif/core';
|
|
|
2
2
|
import * as yaml from 'js-yaml';
|
|
3
3
|
import * as fs from 'node:fs';
|
|
4
4
|
import * as os from 'node:os';
|
|
5
|
-
import
|
|
5
|
+
import path from 'node:path';
|
|
6
6
|
import { Agent } from 'undici';
|
|
7
7
|
import { checkForUpdate } from './update-check.js';
|
|
8
8
|
import { applyLocalOverrides, findLocalProfilePath, formatLocalProfileBanner, parseLocalProfile, resolveProfileSelection, } from './utils/local-config.js';
|
|
@@ -29,13 +29,46 @@ function resolveRequestTimeoutMs(env = process.env) {
|
|
|
29
29
|
return DEFAULT_REQUEST_TIMEOUT_MS;
|
|
30
30
|
return parsed;
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Whether the built-in `fetch` on this runtime accepts a dispatcher built from
|
|
34
|
+
* our bundled userland `undici`. Node <= 24 accepts it; Node 26 rejects a
|
|
35
|
+
* foreign dispatcher with UND_ERR_INVALID_ARG, which `fetch` surfaces only as
|
|
36
|
+
* the generic "fetch failed" — breaking *every* command (DEV-7773).
|
|
37
|
+
*
|
|
38
|
+
* Starts optimistic (`true`); flipped to `false` the first time a request fails
|
|
39
|
+
* with UND_ERR_INVALID_ARG (see `verboseFetch`, which then retries without the
|
|
40
|
+
* dispatcher). Once flipped, every later request skips the dispatcher outright.
|
|
41
|
+
*/
|
|
42
|
+
let dispatcherSupported = true;
|
|
43
|
+
/**
|
|
44
|
+
* True when `error` is the "foreign dispatcher rejected by built-in fetch"
|
|
45
|
+
* failure (Node 26+). The real code lives on `error.cause.code`.
|
|
46
|
+
*/
|
|
47
|
+
export function isInvalidDispatcherError(error) {
|
|
48
|
+
if (!(error instanceof Error))
|
|
49
|
+
return false;
|
|
50
|
+
const { cause } = error;
|
|
51
|
+
const code = cause && typeof cause === 'object' && 'code' in cause
|
|
52
|
+
? cause.code
|
|
53
|
+
: error.code;
|
|
54
|
+
return code === 'UND_ERR_INVALID_ARG';
|
|
55
|
+
}
|
|
32
56
|
/**
|
|
33
57
|
* Lazily-built undici dispatcher whose header/body inactivity timeouts match our
|
|
34
58
|
* request timeout, so undici's internal 300s default never fires first. Built
|
|
35
59
|
* once and reused across requests. `timeout === 0` means "no bound".
|
|
60
|
+
*
|
|
61
|
+
* Returns `undefined` when this runtime has been found to reject a foreign
|
|
62
|
+
* dispatcher (Node 26+, DEV-7773). Dropping the dispatcher does NOT regress the
|
|
63
|
+
* large-push timeout: the per-request `AbortSignal.timeout(timeoutMs)` in
|
|
64
|
+
* `verboseFetch` already bounds the whole request (headers included) to the same
|
|
65
|
+
* 15-min ceiling the dispatcher enforced, so undici's hidden 300s
|
|
66
|
+
* `headersTimeout` is subsumed by it.
|
|
36
67
|
*/
|
|
37
68
|
let sharedDispatcher;
|
|
38
69
|
function getRequestDispatcher(timeoutMs) {
|
|
70
|
+
if (!dispatcherSupported)
|
|
71
|
+
return undefined;
|
|
39
72
|
if (timeoutMs === 0) {
|
|
40
73
|
sharedDispatcher ??= new Agent({ bodyTimeout: 0, headersTimeout: 0 });
|
|
41
74
|
return sharedDispatcher;
|
|
@@ -103,76 +136,6 @@ export default class BaseCommand extends Command {
|
|
|
103
136
|
// Null when none was found or when an explicit -p/XANO_PROFILE overrides it.
|
|
104
137
|
localProfile = null;
|
|
105
138
|
updateNotice = null;
|
|
106
|
-
async init() {
|
|
107
|
-
await super.init();
|
|
108
|
-
this.localProfile = this.loadLocalProfile();
|
|
109
|
-
this.applyInsecureFromProfile();
|
|
110
|
-
this.maybePrintLocalProfileBanner();
|
|
111
|
-
const forceUpdateCheck = process.env.XANO_FORCE_UPDATE_CHECK === '1';
|
|
112
|
-
this.updateNotice = checkForUpdate(this.config.version, forceUpdateCheck);
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Find and parse the nearest project-local profile.yaml, unless an explicit
|
|
116
|
-
* -p/XANO_PROFILE was given (in which case the local file is ignored).
|
|
117
|
-
*/
|
|
118
|
-
loadLocalProfile() {
|
|
119
|
-
if (argvHasProfileFlag(process.argv, process.env)) {
|
|
120
|
-
return null;
|
|
121
|
-
}
|
|
122
|
-
// Walks up to the filesystem root (git-style). parseLocalProfile returns
|
|
123
|
-
// null for a profile.yaml with no recognized keys, so an unrelated file
|
|
124
|
-
// belonging to another tool is ignored rather than hijacked.
|
|
125
|
-
const filePath = findLocalProfilePath(process.cwd());
|
|
126
|
-
if (!filePath) {
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
129
|
-
let config;
|
|
130
|
-
try {
|
|
131
|
-
config = parseLocalProfile(fs.readFileSync(filePath, 'utf8'));
|
|
132
|
-
}
|
|
133
|
-
catch (error) {
|
|
134
|
-
this.error(`${filePath}: ${error.message}`);
|
|
135
|
-
}
|
|
136
|
-
if (!config) {
|
|
137
|
-
this.warn(`Ignoring ${filePath}: no recognized profile keys found.`);
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
return { config, path: filePath };
|
|
141
|
-
}
|
|
142
|
-
/** Print the one-line target banner when a local profile.yaml is in effect. */
|
|
143
|
-
maybePrintLocalProfileBanner() {
|
|
144
|
-
if (!this.localProfile || this.isJsonOutput()) {
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
// Credential-management commands (the `profile` topic) operate on the
|
|
148
|
-
// credentials store directly and intentionally ignore the project-local
|
|
149
|
-
// pin, so the banner would be misleading for them.
|
|
150
|
-
if (this.id?.startsWith('profile')) {
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
const { config, path: filePath } = this.localProfile;
|
|
154
|
-
const profileName = config.profile ?? this.getDefaultProfile();
|
|
155
|
-
const relativePath = path.relative(process.cwd(), filePath) || path.basename(filePath);
|
|
156
|
-
this.log(formatLocalProfileBanner(profileName, config.workspace, relativePath));
|
|
157
|
-
}
|
|
158
|
-
async finally(_) {
|
|
159
|
-
if (this.updateNotice && !this.isJsonOutput()) {
|
|
160
|
-
this.log(this.updateNotice);
|
|
161
|
-
}
|
|
162
|
-
await super.finally(_);
|
|
163
|
-
}
|
|
164
|
-
isJsonOutput() {
|
|
165
|
-
const args = process.argv;
|
|
166
|
-
for (let i = 0; i < args.length; i++) {
|
|
167
|
-
if (args[i] === '--output' && args[i + 1] === 'json')
|
|
168
|
-
return true;
|
|
169
|
-
if (args[i] === '-o' && args[i + 1] === 'json')
|
|
170
|
-
return true;
|
|
171
|
-
if (args[i] === '--output=json' || args[i] === '-o=json')
|
|
172
|
-
return true;
|
|
173
|
-
}
|
|
174
|
-
return false;
|
|
175
|
-
}
|
|
176
139
|
/**
|
|
177
140
|
* Apply insecure TLS mode if the active profile has insecure: true.
|
|
178
141
|
* Sets NODE_TLS_REJECT_UNAUTHORIZED=0 so all fetch() calls skip cert verification.
|
|
@@ -193,22 +156,23 @@ export default class BaseCommand extends Command {
|
|
|
193
156
|
// Don't fail the command if we can't read the profile for insecure check
|
|
194
157
|
}
|
|
195
158
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
return
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
159
|
+
/**
|
|
160
|
+
* Report a failure as exit 1 for commands that reserve exit 2 for findings: operational errors,
|
|
161
|
+
* flag errors and oclif's own default exit 2 all become 1, and under `-o json` the failure is also
|
|
162
|
+
* written to stdout as `{"error": {"exit": 1, "message"}}`. A deliberate exit 0 passes through.
|
|
163
|
+
*/
|
|
164
|
+
async catchAsOperational(error) {
|
|
165
|
+
if (error.oclif?.exit === 0)
|
|
166
|
+
return super.catch(error);
|
|
167
|
+
if (this.isJsonOutput())
|
|
168
|
+
this.log(JSON.stringify({ error: { exit: 1, message: error.message } }, null, 2));
|
|
169
|
+
this.error(error, { exit: 1 });
|
|
208
170
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
171
|
+
async finally(_) {
|
|
172
|
+
if (this.updateNotice && !this.isJsonOutput()) {
|
|
173
|
+
this.log(this.updateNotice);
|
|
174
|
+
}
|
|
175
|
+
await super.finally(_);
|
|
212
176
|
}
|
|
213
177
|
/**
|
|
214
178
|
* Get the resolved credentials file path, respecting --config flag and XANO_CONFIG env var.
|
|
@@ -225,17 +189,18 @@ export default class BaseCommand extends Command {
|
|
|
225
189
|
}
|
|
226
190
|
return resolveCredentialsPath();
|
|
227
191
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
192
|
+
// Helper method to get the default profile from credentials file
|
|
193
|
+
getDefaultProfile() {
|
|
194
|
+
try {
|
|
195
|
+
const credentials = this.loadCredentialsFile();
|
|
196
|
+
if (credentials?.default) {
|
|
197
|
+
return credentials.default;
|
|
198
|
+
}
|
|
199
|
+
return 'default';
|
|
232
200
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
if (parsed && typeof parsed === 'object' && 'profiles' in parsed) {
|
|
236
|
-
return parsed;
|
|
201
|
+
catch {
|
|
202
|
+
return 'default';
|
|
237
203
|
}
|
|
238
|
-
return null;
|
|
239
204
|
}
|
|
240
205
|
/**
|
|
241
206
|
* Get or create the singleton sandbox environment for the authenticated user.
|
|
@@ -256,6 +221,105 @@ export default class BaseCommand extends Command {
|
|
|
256
221
|
}
|
|
257
222
|
return (await response.json());
|
|
258
223
|
}
|
|
224
|
+
// Helper method to get the profile flag value
|
|
225
|
+
getProfile() {
|
|
226
|
+
return this.flags?.profile;
|
|
227
|
+
}
|
|
228
|
+
async init() {
|
|
229
|
+
await super.init();
|
|
230
|
+
this.localProfile = this.loadLocalProfile();
|
|
231
|
+
this.applyInsecureFromProfile();
|
|
232
|
+
this.maybePrintLocalProfileBanner();
|
|
233
|
+
const forceUpdateCheck = process.env.XANO_FORCE_UPDATE_CHECK === '1';
|
|
234
|
+
this.updateNotice = checkForUpdate(this.config.version, forceUpdateCheck);
|
|
235
|
+
}
|
|
236
|
+
/** Raw-argv check: the parsed flags are not available yet when the banner prints. */
|
|
237
|
+
isJsonOutput() {
|
|
238
|
+
const args = this.argv;
|
|
239
|
+
for (let i = 0; i < args.length; i++) {
|
|
240
|
+
if (args[i] === '--output' && args[i + 1] === 'json')
|
|
241
|
+
return true;
|
|
242
|
+
if (args[i] === '-o' && args[i + 1] === 'json')
|
|
243
|
+
return true;
|
|
244
|
+
if (args[i] === '--output=json' || args[i] === '-o=json')
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
loadCredentialsFile() {
|
|
250
|
+
const credentialsPath = this.getCredentialsPath();
|
|
251
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
255
|
+
const parsed = yaml.load(fileContent);
|
|
256
|
+
if (parsed && typeof parsed === 'object' && 'profiles' in parsed) {
|
|
257
|
+
return parsed;
|
|
258
|
+
}
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Poll a static-host build until it reaches a terminal status (ok | error),
|
|
263
|
+
* showing a live, ticking spinner with the current stage and elapsed time —
|
|
264
|
+
* mirroring the UI's build progress for async (package.json) builds, which
|
|
265
|
+
* keep running after the upload returns.
|
|
266
|
+
*
|
|
267
|
+
* On a TTY it renders an animated spinner via ux.action; when quiet (JSON
|
|
268
|
+
* output) or non-interactive it falls back to plain one-line status updates.
|
|
269
|
+
*
|
|
270
|
+
* Returns the final status. Resolves to the last-seen status on timeout.
|
|
271
|
+
*/
|
|
272
|
+
async logStaticHostUrls(opts) {
|
|
273
|
+
const { profile, staticHost, tenantName, verbose, workspaceId } = opts;
|
|
274
|
+
// When a tenant is scoped, target the tenant's static hosting; otherwise the
|
|
275
|
+
// workspace-level static host (default, unchanged behavior).
|
|
276
|
+
const scope = tenantName ? `/tenant/${tenantName}` : '';
|
|
277
|
+
const url = `${profile.instance_origin}/api:meta/workspace/${workspaceId}${scope}/static_host/${staticHost}`;
|
|
278
|
+
try {
|
|
279
|
+
const response = await this.verboseFetch(url, {
|
|
280
|
+
headers: { accept: 'application/json', Authorization: `Bearer ${profile.access_token}` },
|
|
281
|
+
method: 'GET',
|
|
282
|
+
}, verbose, profile.access_token);
|
|
283
|
+
if (!response.ok)
|
|
284
|
+
return;
|
|
285
|
+
const host = (await response.json());
|
|
286
|
+
if (host.dev?.default_url)
|
|
287
|
+
this.log(`Dev URL: ${host.dev.default_url}`);
|
|
288
|
+
if (host.dev?.custom_url)
|
|
289
|
+
this.log(`Dev Custom URL: ${host.dev.custom_url}`);
|
|
290
|
+
if (host.prod?.default_url)
|
|
291
|
+
this.log(`Prod URL: ${host.prod.default_url}`);
|
|
292
|
+
if (host.prod?.custom_url)
|
|
293
|
+
this.log(`Prod Custom URL: ${host.prod.custom_url}`);
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
// Non-fatal — the build succeeded, we just can't show the URL.
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Parse an API error response and return a clean error message.
|
|
301
|
+
* Extracts the message from JSON responses and adds context for common errors.
|
|
302
|
+
*/
|
|
303
|
+
async parseApiError(response, fallbackPrefix) {
|
|
304
|
+
const errorText = await response.text();
|
|
305
|
+
let message = `${fallbackPrefix} (${response.status})`;
|
|
306
|
+
try {
|
|
307
|
+
const errorJson = JSON.parse(errorText);
|
|
308
|
+
if (errorJson.message) {
|
|
309
|
+
message = errorJson.message;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
if (errorText) {
|
|
314
|
+
message += `\n${errorText}`;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// Provide guidance when sandbox access is denied (free plan restriction)
|
|
318
|
+
if (response.status === 500 && message === 'Access Denied.') {
|
|
319
|
+
message = 'Sandbox is not available on the Free plan. Upgrade your plan to use sandbox features.';
|
|
320
|
+
}
|
|
321
|
+
return message;
|
|
322
|
+
}
|
|
259
323
|
/**
|
|
260
324
|
* Resolve the profile from flags and any project-local profile.yaml,
|
|
261
325
|
* validating instance_origin and access_token exist.
|
|
@@ -289,30 +353,6 @@ export default class BaseCommand extends Command {
|
|
|
289
353
|
}
|
|
290
354
|
return { profile, profileName };
|
|
291
355
|
}
|
|
292
|
-
/**
|
|
293
|
-
* Parse an API error response and return a clean error message.
|
|
294
|
-
* Extracts the message from JSON responses and adds context for common errors.
|
|
295
|
-
*/
|
|
296
|
-
async parseApiError(response, fallbackPrefix) {
|
|
297
|
-
const errorText = await response.text();
|
|
298
|
-
let message = `${fallbackPrefix} (${response.status})`;
|
|
299
|
-
try {
|
|
300
|
-
const errorJson = JSON.parse(errorText);
|
|
301
|
-
if (errorJson.message) {
|
|
302
|
-
message = errorJson.message;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
catch {
|
|
306
|
-
if (errorText) {
|
|
307
|
-
message += `\n${errorText}`;
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
// Provide guidance when sandbox access is denied (free plan restriction)
|
|
311
|
-
if (response.status === 500 && message === 'Access Denied.') {
|
|
312
|
-
message = 'Sandbox is not available on the Free plan. Upgrade your plan to use sandbox features.';
|
|
313
|
-
}
|
|
314
|
-
return message;
|
|
315
|
-
}
|
|
316
356
|
/**
|
|
317
357
|
* Make an HTTP request with optional verbose logging.
|
|
318
358
|
* Use this for all Metadata API calls to support the --verbose flag.
|
|
@@ -324,81 +364,74 @@ export default class BaseCommand extends Command {
|
|
|
324
364
|
...options.headers,
|
|
325
365
|
};
|
|
326
366
|
const timeoutMs = resolveRequestTimeoutMs();
|
|
327
|
-
|
|
367
|
+
// Build the fetch options fresh each attempt: a retry needs a new
|
|
368
|
+
// AbortSignal (the first attempt's may already be aborted/consumed) and,
|
|
369
|
+
// when the dispatcher is the culprit, no dispatcher at all.
|
|
370
|
+
const buildFetchOptions = (withDispatcher) => ({
|
|
328
371
|
...options,
|
|
329
372
|
// undici dispatcher: lifts the hidden 300s headers/body timeout to our bound
|
|
330
|
-
dispatcher: getRequestDispatcher(timeoutMs),
|
|
373
|
+
...(withDispatcher ? { dispatcher: getRequestDispatcher(timeoutMs) } : {}),
|
|
331
374
|
headers,
|
|
332
375
|
// belt-and-suspenders hard ceiling that also covers DNS/connect stalls;
|
|
333
376
|
// surfaces as a clean AbortError (see describeNetworkError). 0 = no bound.
|
|
377
|
+
// This alone bounds the whole request, so dropping the dispatcher (Node 26)
|
|
378
|
+
// does not lose the large-push timeout protection.
|
|
334
379
|
...(timeoutMs > 0 && !options.signal ? { signal: AbortSignal.timeout(timeoutMs) } : {}),
|
|
335
|
-
};
|
|
380
|
+
});
|
|
336
381
|
const contentType = headers['Content-Type'] || 'application/json';
|
|
382
|
+
const logDiagnostic = this.isJsonOutput() ? this.logToStderr.bind(this) : this.log.bind(this);
|
|
337
383
|
if (verbose) {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
384
|
+
logDiagnostic('');
|
|
385
|
+
logDiagnostic('─'.repeat(60));
|
|
386
|
+
logDiagnostic(`→ ${method} ${url}`);
|
|
387
|
+
logDiagnostic(` Content-Type: ${contentType}`);
|
|
342
388
|
if (authToken) {
|
|
343
|
-
|
|
389
|
+
logDiagnostic(` Authorization: Bearer ${authToken.slice(0, 8)}...${authToken.slice(-4)}`);
|
|
344
390
|
}
|
|
345
391
|
if (options.body) {
|
|
346
392
|
const bodyStr = typeof options.body === 'string' ? options.body : String(options.body);
|
|
347
393
|
const bodyPreview = bodyStr.length > 500 ? bodyStr.slice(0, 500) + '...' : bodyStr;
|
|
348
|
-
|
|
394
|
+
logDiagnostic(` Body: ${bodyPreview}`);
|
|
349
395
|
}
|
|
350
396
|
}
|
|
351
397
|
const startTime = Date.now();
|
|
352
|
-
|
|
398
|
+
let response;
|
|
399
|
+
try {
|
|
400
|
+
response = await fetch(url, buildFetchOptions(dispatcherSupported));
|
|
401
|
+
}
|
|
402
|
+
catch (error) {
|
|
403
|
+
// Node 26+ rejects our userland-undici dispatcher with UND_ERR_INVALID_ARG,
|
|
404
|
+
// surfaced only as "fetch failed" (DEV-7773). Disable it for the rest of the
|
|
405
|
+
// process and retry once without it; the AbortSignal.timeout ceiling above
|
|
406
|
+
// still bounds the request, so the large-push protection is preserved.
|
|
407
|
+
if (dispatcherSupported && isInvalidDispatcherError(error)) {
|
|
408
|
+
dispatcherSupported = false;
|
|
409
|
+
if (verbose) {
|
|
410
|
+
logDiagnostic(' (this Node runtime rejected undici dispatcher; retrying without it — see DEV-7773)');
|
|
411
|
+
}
|
|
412
|
+
response = await fetch(url, buildFetchOptions(false));
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
throw error;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
353
418
|
const elapsed = Date.now() - startTime;
|
|
354
419
|
if (verbose) {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
420
|
+
logDiagnostic(`← ${response.status} ${response.statusText} (${elapsed}ms)`);
|
|
421
|
+
logDiagnostic('─'.repeat(60));
|
|
422
|
+
logDiagnostic('');
|
|
358
423
|
}
|
|
359
424
|
return response;
|
|
360
425
|
}
|
|
361
|
-
/**
|
|
362
|
-
* Poll a static-host build until it reaches a terminal status (ok | error),
|
|
363
|
-
* showing a live, ticking spinner with the current stage and elapsed time —
|
|
364
|
-
* mirroring the UI's build progress for async (package.json) builds, which
|
|
365
|
-
* keep running after the upload returns.
|
|
366
|
-
*
|
|
367
|
-
* On a TTY it renders an animated spinner via ux.action; when quiet (JSON
|
|
368
|
-
* output) or non-interactive it falls back to plain one-line status updates.
|
|
369
|
-
*
|
|
370
|
-
* Returns the final status. Resolves to the last-seen status on timeout.
|
|
371
|
-
*/
|
|
372
|
-
async logStaticHostUrls(opts) {
|
|
373
|
-
const { profile, staticHost, verbose, workspaceId } = opts;
|
|
374
|
-
const url = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/static_host/${staticHost}`;
|
|
375
|
-
try {
|
|
376
|
-
const response = await this.verboseFetch(url, {
|
|
377
|
-
headers: { accept: 'application/json', Authorization: `Bearer ${profile.access_token}` },
|
|
378
|
-
method: 'GET',
|
|
379
|
-
}, verbose, profile.access_token);
|
|
380
|
-
if (!response.ok)
|
|
381
|
-
return;
|
|
382
|
-
const host = (await response.json());
|
|
383
|
-
if (host.dev?.default_url)
|
|
384
|
-
this.log(`Dev URL: ${host.dev.default_url}`);
|
|
385
|
-
if (host.dev?.custom_url)
|
|
386
|
-
this.log(`Dev Custom URL: ${host.dev.custom_url}`);
|
|
387
|
-
if (host.prod?.default_url)
|
|
388
|
-
this.log(`Prod URL: ${host.prod.default_url}`);
|
|
389
|
-
if (host.prod?.custom_url)
|
|
390
|
-
this.log(`Prod Custom URL: ${host.prod.custom_url}`);
|
|
391
|
-
}
|
|
392
|
-
catch {
|
|
393
|
-
// Non-fatal — the build succeeded, we just can't show the URL.
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
426
|
async waitForBuild(opts) {
|
|
397
|
-
const { buildId, profile, quiet, staticHost, verbose, workspaceId } = opts;
|
|
427
|
+
const { buildId, profile, quiet, staticHost, tenantName, verbose, workspaceId } = opts;
|
|
398
428
|
const intervalMs = opts.intervalMs ?? 2000;
|
|
399
429
|
const timeoutMs = opts.timeoutMs ?? 600_000; // 10 min
|
|
400
430
|
const terminal = new Set(['error', 'ok']);
|
|
401
|
-
|
|
431
|
+
// When a tenant is scoped, target the tenant's static hosting; otherwise the
|
|
432
|
+
// workspace-level static host (default, unchanged behavior).
|
|
433
|
+
const scope = tenantName ? `/tenant/${tenantName}` : '';
|
|
434
|
+
const url = `${profile.instance_origin}/api:meta/workspace/${workspaceId}${scope}/static_host/${staticHost}/build/${buildId}`;
|
|
402
435
|
// Spinner only on an interactive TTY and when not emitting JSON. Verbose mode
|
|
403
436
|
// also disables it (the spinner would interleave with request/response logs).
|
|
404
437
|
const animate = Boolean(process.stdout.isTTY) && !quiet && !verbose;
|
|
@@ -461,6 +494,50 @@ export default class BaseCommand extends Command {
|
|
|
461
494
|
conclude(`stopped waiting after ${Math.round(timeoutMs / 1000)}s (last status: ${stage || 'unknown'})`);
|
|
462
495
|
return stage;
|
|
463
496
|
}
|
|
497
|
+
/**
|
|
498
|
+
* Find and parse the nearest project-local profile.yaml, unless an explicit
|
|
499
|
+
* -p/XANO_PROFILE was given (in which case the local file is ignored).
|
|
500
|
+
*/
|
|
501
|
+
loadLocalProfile() {
|
|
502
|
+
if (argvHasProfileFlag(process.argv, process.env)) {
|
|
503
|
+
return null;
|
|
504
|
+
}
|
|
505
|
+
// Walks up to the filesystem root (git-style). parseLocalProfile returns
|
|
506
|
+
// null for a profile.yaml with no recognized keys, so an unrelated file
|
|
507
|
+
// belonging to another tool is ignored rather than hijacked.
|
|
508
|
+
const filePath = findLocalProfilePath(process.cwd());
|
|
509
|
+
if (!filePath) {
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
let config;
|
|
513
|
+
try {
|
|
514
|
+
config = parseLocalProfile(fs.readFileSync(filePath, 'utf8'));
|
|
515
|
+
}
|
|
516
|
+
catch (error) {
|
|
517
|
+
this.error(`${filePath}: ${error.message}`);
|
|
518
|
+
}
|
|
519
|
+
if (!config) {
|
|
520
|
+
this.warn(`Ignoring ${filePath}: no recognized profile keys found.`);
|
|
521
|
+
return null;
|
|
522
|
+
}
|
|
523
|
+
return { config, path: filePath };
|
|
524
|
+
}
|
|
525
|
+
/** Print the one-line target banner when a local profile.yaml is in effect. */
|
|
526
|
+
maybePrintLocalProfileBanner() {
|
|
527
|
+
if (!this.localProfile || this.isJsonOutput()) {
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
// Credential-management commands (the `profile` topic) operate on the
|
|
531
|
+
// credentials store directly and intentionally ignore the project-local
|
|
532
|
+
// pin, so the banner would be misleading for them.
|
|
533
|
+
if (this.id?.startsWith('profile')) {
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
const { config, path: filePath } = this.localProfile;
|
|
537
|
+
const profileName = config.profile ?? this.getDefaultProfile();
|
|
538
|
+
const relativePath = path.relative(process.cwd(), filePath) || path.basename(filePath);
|
|
539
|
+
this.log(formatLocalProfileBanner(profileName, config.workspace, relativePath));
|
|
540
|
+
}
|
|
464
541
|
}
|
|
465
542
|
/** Human-friendly label for a build status stage. */
|
|
466
543
|
function stageLabel(status) {
|
|
@@ -48,10 +48,10 @@ export default class Auth extends Command {
|
|
|
48
48
|
run(): Promise<AuthResult>;
|
|
49
49
|
protected toErrorJson(err: unknown): unknown;
|
|
50
50
|
private acquireToken;
|
|
51
|
-
private getHeaders;
|
|
52
51
|
private fetchBranches;
|
|
53
52
|
private fetchInstances;
|
|
54
53
|
private fetchWorkspaces;
|
|
54
|
+
private getHeaders;
|
|
55
55
|
private promptForToken;
|
|
56
56
|
private promptProfileName;
|
|
57
57
|
private readTokenFromStdin;
|