@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
|
@@ -212,13 +212,6 @@ To authenticate, open the following URL in any browser:
|
|
|
212
212
|
}
|
|
213
213
|
return noBrowser ? this.promptForToken(origin) : this.startAuthServer(origin);
|
|
214
214
|
}
|
|
215
|
-
getHeaders(accessToken) {
|
|
216
|
-
return {
|
|
217
|
-
'User-Agent': buildUserAgent(this.config.version),
|
|
218
|
-
accept: 'application/json',
|
|
219
|
-
...(accessToken && { Authorization: `Bearer ${accessToken}` }),
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
215
|
async fetchBranches(accessToken, origin, workspaceId) {
|
|
223
216
|
try {
|
|
224
217
|
const response = await fetch(`${origin}/api:meta/workspace/${workspaceId}/branch`, {
|
|
@@ -288,6 +281,13 @@ To authenticate, open the following URL in any browser:
|
|
|
288
281
|
return [];
|
|
289
282
|
}
|
|
290
283
|
}
|
|
284
|
+
getHeaders(accessToken) {
|
|
285
|
+
return {
|
|
286
|
+
accept: 'application/json',
|
|
287
|
+
'User-Agent': buildUserAgent(this.config.version),
|
|
288
|
+
...(accessToken && { Authorization: `Bearer ${accessToken}` }),
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
291
|
async promptForToken(origin) {
|
|
292
292
|
// Headless flow: no local callback server. The login page, when opened
|
|
293
293
|
// without a `callback` param, renders the access token on screen for the
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import BaseCommand from '../../../base-command.js';
|
|
2
2
|
export default class BranchCreate extends BaseCommand {
|
|
3
|
-
static description: string;
|
|
4
3
|
static args: {
|
|
5
4
|
label: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
5
|
};
|
|
6
|
+
static description: string;
|
|
7
7
|
static examples: string[];
|
|
8
8
|
static flags: {
|
|
9
9
|
color: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import BaseCommand from '../../../base-command.js';
|
|
3
3
|
export default class BranchCreate extends BaseCommand {
|
|
4
|
-
static description = 'Create a new branch by cloning from an existing branch';
|
|
5
4
|
static args = {
|
|
6
5
|
label: Args.string({
|
|
7
6
|
description: 'Label for the new branch',
|
|
8
7
|
required: true,
|
|
9
8
|
}),
|
|
10
9
|
};
|
|
10
|
+
static description = 'Create a new branch by cloning from an existing branch';
|
|
11
11
|
static examples = [
|
|
12
12
|
`$ xano branch create dev
|
|
13
13
|
Created branch: dev
|
|
@@ -8,11 +8,33 @@ export default class BranchSetLive extends BaseCommand {
|
|
|
8
8
|
static flags: {
|
|
9
9
|
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
10
|
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
'policy-override': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
12
|
workspace: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
13
|
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
14
|
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
15
|
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
16
|
};
|
|
17
|
+
/** Set once the platform refuses for a weakened mandatory policy: already explained, it exits 1 as it is. */
|
|
18
|
+
private refusedByPermission;
|
|
19
|
+
/** Set once the set-live policy gate refuses, the one failure that exits 2. */
|
|
20
|
+
private refusedByPolicyGate;
|
|
21
|
+
/** Exit 2 is the set-live gate's refusal; every other failure, flag errors included, exits 1. */
|
|
22
|
+
protected catch(error: Error & {
|
|
23
|
+
oclif?: {
|
|
24
|
+
exit?: number;
|
|
25
|
+
};
|
|
26
|
+
}): Promise<void>;
|
|
16
27
|
run(): Promise<void>;
|
|
17
28
|
private confirm;
|
|
29
|
+
/**
|
|
30
|
+
* The set-live gate refused: the verdict on stdout (under `-o json`, `{set_live: false, message,
|
|
31
|
+
* policy_gate}`), then the platform's message and how to proceed, exiting 2.
|
|
32
|
+
*/
|
|
33
|
+
private refuseSetLive;
|
|
34
|
+
/**
|
|
35
|
+
* The branch weakens a mandatory policy of the live branch (or lacks one), and the credential lacks
|
|
36
|
+
* `workspace:policy` update. That is a permission refusal, not a blocking finding: it exits 1 (under
|
|
37
|
+
* `-o json` as `{error}`) with the platform's sentence and how to go on. The live branch is unchanged.
|
|
38
|
+
*/
|
|
39
|
+
private refuseWeakening;
|
|
18
40
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import BaseCommand from '../../../base-command.js';
|
|
3
|
+
import { BLANK_POLICY_OVERRIDE, blankPolicyOverride, gateOverrideHint, gateRefusal, liveGateLines, quoted, weakeningRefusal, } from '../../../utils/policy/gate.js';
|
|
4
|
+
import { policyWeakeningGuidance } from '../../../utils/policy/permission.js';
|
|
3
5
|
export default class BranchSetLive extends BaseCommand {
|
|
4
6
|
static args = {
|
|
5
7
|
branch_label: Args.string({
|
|
@@ -7,7 +9,7 @@ export default class BranchSetLive extends BaseCommand {
|
|
|
7
9
|
required: true,
|
|
8
10
|
}),
|
|
9
11
|
};
|
|
10
|
-
static description = '[IMPORTANT] ALWAYS confirm with the user before changing the live branch. Sets a branch as the live (active) branch for API requests.';
|
|
12
|
+
static description = '[IMPORTANT] ALWAYS confirm with the user before changing the live branch. Sets a branch as the live (active) branch for API requests. The set-live policy gate checks the branch against its own policies, which live will have once it is set live: when blocking findings refuse it, the command exits 2 unless --policy-override gives a reason. A branch that weakens a mandatory policy of the live branch needs workspace:policy update; without it the command exits 1.';
|
|
11
13
|
static examples = [
|
|
12
14
|
`$ xano branch set-live staging
|
|
13
15
|
Are you sure you want to set 'staging' as the live branch? (y/N) y
|
|
@@ -24,6 +26,7 @@ Branch 'v1' is now live
|
|
|
24
26
|
"live": true
|
|
25
27
|
}
|
|
26
28
|
`,
|
|
29
|
+
`$ xano branch set_live rollback --force --policy-override "Rollback approved; finding tracked in JIRA-12"`,
|
|
27
30
|
];
|
|
28
31
|
static flags = {
|
|
29
32
|
...BaseCommand.baseFlags,
|
|
@@ -40,14 +43,27 @@ Branch 'v1' is now live
|
|
|
40
43
|
options: ['summary', 'json'],
|
|
41
44
|
required: false,
|
|
42
45
|
}),
|
|
46
|
+
'policy-override': Flags.string({ description: 'Set the branch live past a blocking set-live policy gate with an audited reason (requires workspace:policy update)' }),
|
|
43
47
|
workspace: Flags.integer({
|
|
44
48
|
char: 'w',
|
|
45
49
|
description: 'Workspace ID (uses profile workspace if not provided)',
|
|
46
50
|
required: false,
|
|
47
51
|
}),
|
|
48
52
|
};
|
|
53
|
+
/** Set once the platform refuses for a weakened mandatory policy: already explained, it exits 1 as it is. */
|
|
54
|
+
refusedByPermission = false;
|
|
55
|
+
/** Set once the set-live policy gate refuses, the one failure that exits 2. */
|
|
56
|
+
refusedByPolicyGate = false;
|
|
57
|
+
/** Exit 2 is the set-live gate's refusal; every other failure, flag errors included, exits 1. */
|
|
58
|
+
async catch(error) {
|
|
59
|
+
if (this.refusedByPolicyGate)
|
|
60
|
+
return super.catch(error);
|
|
61
|
+
return this.catchAsOperational(error);
|
|
62
|
+
}
|
|
49
63
|
async run() {
|
|
50
64
|
const { args, flags } = await this.parse(BranchSetLive);
|
|
65
|
+
if (blankPolicyOverride(flags['policy-override']))
|
|
66
|
+
this.error(BLANK_POLICY_OVERRIDE);
|
|
51
67
|
const { profile } = this.resolveProfile(flags);
|
|
52
68
|
// Get workspace ID from flag or profile
|
|
53
69
|
const workspaceId = flags.workspace || profile.workspace;
|
|
@@ -66,16 +82,26 @@ Branch 'v1' is now live
|
|
|
66
82
|
}
|
|
67
83
|
// Construct the API URL
|
|
68
84
|
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/branch/${encodeURIComponent(branchLabel)}/live`;
|
|
85
|
+
// A reason from someone with workspace:policy update sets the branch live past a blocking gate.
|
|
86
|
+
const overrideReason = flags['policy-override']?.trim();
|
|
69
87
|
// Set branch as live via the API
|
|
70
88
|
try {
|
|
71
89
|
const response = await this.verboseFetch(apiUrl, {
|
|
90
|
+
...(overrideReason ? { body: JSON.stringify({ override_reason: overrideReason }) } : {}),
|
|
72
91
|
headers: {
|
|
73
92
|
'accept': 'application/json',
|
|
74
93
|
'Authorization': `Bearer ${profile.access_token}`,
|
|
94
|
+
...(overrideReason ? { 'Content-Type': 'application/json' } : {}),
|
|
75
95
|
},
|
|
76
96
|
method: 'POST',
|
|
77
97
|
}, flags.verbose, profile.access_token);
|
|
78
98
|
if (!response.ok) {
|
|
99
|
+
const refused = await gateRefusal(response);
|
|
100
|
+
if (refused)
|
|
101
|
+
this.refuseSetLive(refused, flags, branchLabel);
|
|
102
|
+
const weakening = await weakeningRefusal(response);
|
|
103
|
+
if (weakening)
|
|
104
|
+
this.refuseWeakening(weakening, flags, branchLabel);
|
|
79
105
|
const errorText = await response.text();
|
|
80
106
|
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
81
107
|
}
|
|
@@ -89,6 +115,8 @@ Branch 'v1' is now live
|
|
|
89
115
|
}
|
|
90
116
|
}
|
|
91
117
|
catch (error) {
|
|
118
|
+
if (this.refusedByPolicyGate || this.refusedByPermission)
|
|
119
|
+
throw error;
|
|
92
120
|
if (error instanceof Error) {
|
|
93
121
|
this.error(`Failed to set branch as live: ${error.message}`);
|
|
94
122
|
}
|
|
@@ -111,4 +139,35 @@ Branch 'v1' is now live
|
|
|
111
139
|
});
|
|
112
140
|
});
|
|
113
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* The set-live gate refused: the verdict on stdout (under `-o json`, `{set_live: false, message,
|
|
144
|
+
* policy_gate}`), then the platform's message and how to proceed, exiting 2.
|
|
145
|
+
*/
|
|
146
|
+
refuseSetLive(refused, flags, branchLabel) {
|
|
147
|
+
if (flags.output === 'json') {
|
|
148
|
+
this.log(JSON.stringify({ message: refused.message, policy_gate: refused.answer, set_live: false }, null, 2));
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
for (const line of liveGateLines(refused.answer, 'branch'))
|
|
152
|
+
this.log(line);
|
|
153
|
+
}
|
|
154
|
+
const workspace = flags.workspace ? ` -w ${flags.workspace}` : '';
|
|
155
|
+
const hint = gateOverrideHint(refused.answer, `xano branch set_live ${quoted(branchLabel)}${workspace} --policy-override "<why>"`);
|
|
156
|
+
this.refusedByPolicyGate = true;
|
|
157
|
+
this.error(`${refused.message}${hint}`, { exit: 2 });
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* The branch weakens a mandatory policy of the live branch (or lacks one), and the credential lacks
|
|
161
|
+
* `workspace:policy` update. That is a permission refusal, not a blocking finding: it exits 1 (under
|
|
162
|
+
* `-o json` as `{error}`) with the platform's sentence and how to go on. The live branch is unchanged.
|
|
163
|
+
*/
|
|
164
|
+
refuseWeakening(refused, flags, branchLabel) {
|
|
165
|
+
const workspace = flags.workspace ? ` -w ${flags.workspace}` : '';
|
|
166
|
+
this.refusedByPermission = true;
|
|
167
|
+
const again = `xano branch set_live ${quoted(branchLabel)}${workspace}`;
|
|
168
|
+
const way = refused.payload.unavailable === true
|
|
169
|
+
? `The check may read them next time: ${again}`
|
|
170
|
+
: `To set it live yourself, give ${quoted(branchLabel)} the live branch's version of those policies, then run: ${again}`;
|
|
171
|
+
this.error(`${refused.message}${policyWeakeningGuidance(refused.payload, [way], true)}`);
|
|
172
|
+
}
|
|
114
173
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class EphemeralCreate extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
display: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
'expires-hours': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
};
|
|
17
|
+
run(): Promise<void>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import BaseCommand from '../../../base-command.js';
|
|
3
|
+
export default class EphemeralCreate extends BaseCommand {
|
|
4
|
+
static args = {
|
|
5
|
+
display: Args.string({
|
|
6
|
+
description: 'Display name for the ephemeral tenant',
|
|
7
|
+
required: true,
|
|
8
|
+
}),
|
|
9
|
+
};
|
|
10
|
+
static description = 'Create a new ephemeral (short-lived, auto-expiring) tenant in a workspace';
|
|
11
|
+
static examples = [
|
|
12
|
+
`$ xano ephemeral create "PR preview"
|
|
13
|
+
Created ephemeral tenant: PR preview (e4f2-9ab1-...) - ID: 42
|
|
14
|
+
Expires: in 1 hour
|
|
15
|
+
`,
|
|
16
|
+
`$ xano ephemeral create "Demo" --expires-hours 24 -w 114`,
|
|
17
|
+
`$ xano ephemeral create "Load test" --description "overnight soak" --expires-hours 24 -o json`,
|
|
18
|
+
];
|
|
19
|
+
static flags = {
|
|
20
|
+
...BaseCommand.baseFlags,
|
|
21
|
+
description: Flags.string({
|
|
22
|
+
char: 'd',
|
|
23
|
+
description: 'Ephemeral tenant description',
|
|
24
|
+
required: false,
|
|
25
|
+
}),
|
|
26
|
+
'expires-hours': Flags.integer({
|
|
27
|
+
default: 1,
|
|
28
|
+
description: 'Hours until the tenant auto-expires (1-24)',
|
|
29
|
+
max: 24,
|
|
30
|
+
min: 1,
|
|
31
|
+
required: false,
|
|
32
|
+
}),
|
|
33
|
+
output: Flags.string({
|
|
34
|
+
char: 'o',
|
|
35
|
+
default: 'summary',
|
|
36
|
+
description: 'Output format',
|
|
37
|
+
options: ['summary', 'json'],
|
|
38
|
+
required: false,
|
|
39
|
+
}),
|
|
40
|
+
workspace: Flags.string({
|
|
41
|
+
char: 'w',
|
|
42
|
+
description: 'Workspace ID (uses profile workspace if not provided)',
|
|
43
|
+
required: false,
|
|
44
|
+
}),
|
|
45
|
+
};
|
|
46
|
+
async run() {
|
|
47
|
+
const { args, flags } = await this.parse(EphemeralCreate);
|
|
48
|
+
const { profile } = this.resolveProfile(flags);
|
|
49
|
+
// Ephemeral tenants REQUIRE a workspace (unlike sandbox, which is workspace-agnostic).
|
|
50
|
+
const workspaceId = flags.workspace || profile.workspace;
|
|
51
|
+
if (!workspaceId) {
|
|
52
|
+
this.error('No workspace ID provided. Ephemeral tenants require a workspace — use --workspace or set one in your profile.');
|
|
53
|
+
}
|
|
54
|
+
const body = {
|
|
55
|
+
display: args.display,
|
|
56
|
+
expires_hours: flags['expires-hours'],
|
|
57
|
+
tag: [],
|
|
58
|
+
};
|
|
59
|
+
if (flags.description)
|
|
60
|
+
body.description = flags.description;
|
|
61
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/ephemeral`;
|
|
62
|
+
try {
|
|
63
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
64
|
+
body: JSON.stringify(body),
|
|
65
|
+
headers: {
|
|
66
|
+
accept: 'application/json',
|
|
67
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
68
|
+
'Content-Type': 'application/json',
|
|
69
|
+
},
|
|
70
|
+
method: 'POST',
|
|
71
|
+
}, flags.verbose, profile.access_token);
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
const errorText = await response.text();
|
|
74
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
75
|
+
}
|
|
76
|
+
const tenant = (await response.json());
|
|
77
|
+
if (flags.output === 'json') {
|
|
78
|
+
this.log(JSON.stringify(tenant, null, 2));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
this.log(`Created ephemeral tenant: ${tenant.display || tenant.name} (${tenant.name}) - ID: ${tenant.id}`);
|
|
82
|
+
if (tenant.state) {
|
|
83
|
+
this.log(` State: ${tenant.state}`);
|
|
84
|
+
}
|
|
85
|
+
if (tenant.ephemeral_expires_at) {
|
|
86
|
+
// The API serializes the timestamp field as a date string
|
|
87
|
+
// ("2026-07-24 19:49:15+0000"); tolerate a raw unix-epoch number too.
|
|
88
|
+
const raw = tenant.ephemeral_expires_at;
|
|
89
|
+
const expiresMs = typeof raw === 'number' ? raw * 1000 : Date.parse(String(raw).replace(' ', 'T'));
|
|
90
|
+
this.log(` Expires: ${Number.isNaN(expiresMs) ? String(raw) : new Date(expiresMs).toISOString()}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
if (error instanceof Error) {
|
|
96
|
+
this.error(`Failed to create ephemeral tenant: ${error.message}`);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
this.error(`Failed to create ephemeral tenant: ${String(error)}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class EphemeralDelete extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
tenant_name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
+
};
|
|
16
|
+
run(): Promise<void>;
|
|
17
|
+
private confirm;
|
|
18
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import BaseCommand from '../../../base-command.js';
|
|
3
|
+
export default class EphemeralDelete extends BaseCommand {
|
|
4
|
+
static args = {
|
|
5
|
+
tenant_name: Args.string({
|
|
6
|
+
description: 'Ephemeral tenant name to delete',
|
|
7
|
+
required: true,
|
|
8
|
+
}),
|
|
9
|
+
};
|
|
10
|
+
static description = 'Delete an ephemeral tenant permanently. This destroys all associated infrastructure and cannot be undone.';
|
|
11
|
+
static examples = [
|
|
12
|
+
`$ xano ephemeral delete e4f2-9ab1-xyz1
|
|
13
|
+
Are you sure you want to delete tenant e4f2-9ab1-xyz1? This action cannot be undone. (y/N) y
|
|
14
|
+
Deleted tenant e4f2-9ab1-xyz1
|
|
15
|
+
`,
|
|
16
|
+
`$ xano ephemeral delete e4f2-9ab1-xyz1 --force
|
|
17
|
+
Deleted tenant e4f2-9ab1-xyz1
|
|
18
|
+
`,
|
|
19
|
+
`$ xano ephemeral delete e4f2-9ab1-xyz1 -f -o json`,
|
|
20
|
+
];
|
|
21
|
+
static flags = {
|
|
22
|
+
...BaseCommand.baseFlags,
|
|
23
|
+
force: Flags.boolean({
|
|
24
|
+
char: 'f',
|
|
25
|
+
default: false,
|
|
26
|
+
description: '[CRITICAL] NEVER run without explicit user confirmation. Skips the confirmation prompt.',
|
|
27
|
+
required: false,
|
|
28
|
+
}),
|
|
29
|
+
output: Flags.string({
|
|
30
|
+
char: 'o',
|
|
31
|
+
default: 'summary',
|
|
32
|
+
description: 'Output format',
|
|
33
|
+
options: ['summary', 'json'],
|
|
34
|
+
required: false,
|
|
35
|
+
}),
|
|
36
|
+
workspace: Flags.string({
|
|
37
|
+
char: 'w',
|
|
38
|
+
description: 'Workspace ID (uses profile workspace if not provided)',
|
|
39
|
+
required: false,
|
|
40
|
+
}),
|
|
41
|
+
};
|
|
42
|
+
async run() {
|
|
43
|
+
const { args, flags } = await this.parse(EphemeralDelete);
|
|
44
|
+
const { profile } = this.resolveProfile(flags);
|
|
45
|
+
const workspaceId = flags.workspace || profile.workspace;
|
|
46
|
+
if (!workspaceId) {
|
|
47
|
+
this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
|
|
48
|
+
}
|
|
49
|
+
const tenantName = args.tenant_name;
|
|
50
|
+
if (!flags.force) {
|
|
51
|
+
const confirmed = await this.confirm(`Are you sure you want to delete tenant ${tenantName}? This action cannot be undone.`);
|
|
52
|
+
if (!confirmed) {
|
|
53
|
+
this.log('Deletion cancelled.');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/tenant/${tenantName}`;
|
|
58
|
+
try {
|
|
59
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
60
|
+
headers: {
|
|
61
|
+
'accept': 'application/json',
|
|
62
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
63
|
+
},
|
|
64
|
+
method: 'DELETE',
|
|
65
|
+
}, flags.verbose, profile.access_token);
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
const errorText = await response.text();
|
|
68
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
69
|
+
}
|
|
70
|
+
if (flags.output === 'json') {
|
|
71
|
+
this.log(JSON.stringify({ deleted: true, tenant_name: tenantName }, null, 2));
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
this.log(`Deleted tenant ${tenantName}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
if (error instanceof Error) {
|
|
79
|
+
this.error(`Failed to delete ephemeral tenant: ${error.message}`);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
this.error(`Failed to delete ephemeral tenant: ${String(error)}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async confirm(message) {
|
|
87
|
+
const readline = await import('node:readline');
|
|
88
|
+
const rl = readline.createInterface({
|
|
89
|
+
input: process.stdin,
|
|
90
|
+
output: process.stdout,
|
|
91
|
+
});
|
|
92
|
+
return new Promise((resolve) => {
|
|
93
|
+
rl.question(`${message} (y/N) `, (answer) => {
|
|
94
|
+
rl.close();
|
|
95
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class EphemeralEdit extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
tenant_name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
display: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
};
|
|
17
|
+
run(): Promise<void>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import BaseCommand from '../../../base-command.js';
|
|
3
|
+
export default class EphemeralEdit extends BaseCommand {
|
|
4
|
+
static args = {
|
|
5
|
+
tenant_name: Args.string({
|
|
6
|
+
description: 'Ephemeral tenant name to edit',
|
|
7
|
+
required: true,
|
|
8
|
+
}),
|
|
9
|
+
};
|
|
10
|
+
static description = 'Edit an existing ephemeral tenant';
|
|
11
|
+
static examples = [
|
|
12
|
+
`$ xano ephemeral edit e4f2-9ab1-xyz1 --display "New Name" --description "Updated description"
|
|
13
|
+
Updated tenant: New Name (e4f2-9ab1-xyz1) - ID: 42
|
|
14
|
+
`,
|
|
15
|
+
`$ xano ephemeral edit e4f2-9ab1-xyz1 -o json`,
|
|
16
|
+
];
|
|
17
|
+
static flags = {
|
|
18
|
+
...BaseCommand.baseFlags,
|
|
19
|
+
description: Flags.string({
|
|
20
|
+
char: 'd',
|
|
21
|
+
description: 'New description',
|
|
22
|
+
required: false,
|
|
23
|
+
}),
|
|
24
|
+
display: Flags.string({
|
|
25
|
+
description: 'New display name',
|
|
26
|
+
required: false,
|
|
27
|
+
}),
|
|
28
|
+
output: Flags.string({
|
|
29
|
+
char: 'o',
|
|
30
|
+
default: 'summary',
|
|
31
|
+
description: 'Output format',
|
|
32
|
+
options: ['summary', 'json'],
|
|
33
|
+
required: false,
|
|
34
|
+
}),
|
|
35
|
+
workspace: Flags.string({
|
|
36
|
+
char: 'w',
|
|
37
|
+
description: 'Workspace ID (uses profile workspace if not provided)',
|
|
38
|
+
required: false,
|
|
39
|
+
}),
|
|
40
|
+
};
|
|
41
|
+
async run() {
|
|
42
|
+
const { args, flags } = await this.parse(EphemeralEdit);
|
|
43
|
+
const { profile } = this.resolveProfile(flags);
|
|
44
|
+
const workspaceId = flags.workspace || profile.workspace;
|
|
45
|
+
if (!workspaceId) {
|
|
46
|
+
this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
|
|
47
|
+
}
|
|
48
|
+
const tenantName = args.tenant_name;
|
|
49
|
+
const baseUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/tenant/${tenantName}`;
|
|
50
|
+
const headers = {
|
|
51
|
+
'accept': 'application/json',
|
|
52
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
53
|
+
'Content-Type': 'application/json',
|
|
54
|
+
};
|
|
55
|
+
try {
|
|
56
|
+
// Fetch current tenant state (PUT requires all fields)
|
|
57
|
+
const getResponse = await this.verboseFetch(baseUrl, {
|
|
58
|
+
headers: {
|
|
59
|
+
'accept': 'application/json',
|
|
60
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
61
|
+
},
|
|
62
|
+
method: 'GET',
|
|
63
|
+
}, flags.verbose, profile.access_token);
|
|
64
|
+
if (!getResponse.ok) {
|
|
65
|
+
const errorText = await getResponse.text();
|
|
66
|
+
this.error(`Failed to fetch tenant: ${getResponse.status} ${getResponse.statusText}\n${errorText}`);
|
|
67
|
+
}
|
|
68
|
+
const current = await getResponse.json();
|
|
69
|
+
// Merge in user-provided values
|
|
70
|
+
const body = {
|
|
71
|
+
description: flags.description === undefined ? (current.description ?? '') : flags.description,
|
|
72
|
+
display: flags.display === undefined ? (current.display ?? current.name) : flags.display,
|
|
73
|
+
domain: current.domain ?? '',
|
|
74
|
+
ingress: current.ingress ?? false,
|
|
75
|
+
proxy: current.proxy ?? '',
|
|
76
|
+
rbac: {
|
|
77
|
+
enabled: current.rbac?.enabled ?? false,
|
|
78
|
+
},
|
|
79
|
+
tag: current.tag ?? [],
|
|
80
|
+
tasks: current.tasks ?? false,
|
|
81
|
+
};
|
|
82
|
+
// Update tenant
|
|
83
|
+
const putResponse = await this.verboseFetch(baseUrl, {
|
|
84
|
+
body: JSON.stringify(body),
|
|
85
|
+
headers,
|
|
86
|
+
method: 'PUT',
|
|
87
|
+
}, flags.verbose, profile.access_token);
|
|
88
|
+
if (!putResponse.ok) {
|
|
89
|
+
const errorText = await putResponse.text();
|
|
90
|
+
this.error(`API request failed with status ${putResponse.status}: ${putResponse.statusText}\n${errorText}`);
|
|
91
|
+
}
|
|
92
|
+
const tenant = await putResponse.json();
|
|
93
|
+
if (flags.output === 'json') {
|
|
94
|
+
this.log(JSON.stringify(tenant, null, 2));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
this.log(`Updated tenant: ${tenant.display || tenant.name} (${tenant.name}) - ID: ${tenant.id}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
if (error instanceof Error) {
|
|
102
|
+
this.error(`Failed to edit ephemeral tenant: ${error.message}`);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
this.error(`Failed to edit ephemeral tenant: ${String(error)}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class EphemeralGet extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
tenant_name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
};
|
|
15
|
+
run(): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Render a unix-epoch-seconds expiration as a human-readable "in Xh Ym" string,
|
|
18
|
+
* falling back to an ISO timestamp if the value has already passed or is absent.
|
|
19
|
+
*/
|
|
20
|
+
private formatExpiration;
|
|
21
|
+
}
|