@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
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import BaseCommand from '../../../base-command.js';
|
|
5
|
+
import { isAwaited, waitForMicroservices, } from '../../../utils/microservice-wait.js';
|
|
6
|
+
import { executePush } from '../../../utils/multidoc-push.js';
|
|
7
|
+
import { policiesSkippedNotice } from '../../../utils/policy/feedback.js';
|
|
8
|
+
/** Status glyph for one microservice row in the `--wait` progress block. */
|
|
9
|
+
const icon = (e) => {
|
|
10
|
+
if (!isAwaited(e))
|
|
11
|
+
return '➖';
|
|
12
|
+
switch (e.status) {
|
|
13
|
+
case 'error': {
|
|
14
|
+
return '❌';
|
|
15
|
+
}
|
|
16
|
+
case 'ok': {
|
|
17
|
+
return '✅';
|
|
18
|
+
}
|
|
19
|
+
default: {
|
|
20
|
+
return '⏳';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
/** One rendered microservice row: glyph, padded name, status detail. */
|
|
25
|
+
const line = (e) => {
|
|
26
|
+
const detail = isAwaited(e) ? e.detail || e.status : `skipped (${e.tenant_deploy ?? 'manual'})`;
|
|
27
|
+
return ` ${icon(e)} ${e.name.padEnd(24)} ${detail}`;
|
|
28
|
+
};
|
|
29
|
+
export default class EphemeralPush extends BaseCommand {
|
|
30
|
+
static args = {
|
|
31
|
+
tenant_name: Args.string({
|
|
32
|
+
description: 'Ephemeral tenant name to push to',
|
|
33
|
+
required: true,
|
|
34
|
+
}),
|
|
35
|
+
};
|
|
36
|
+
static description = '[IMPORTANT] ALWAYS run --dry-run first and show the user the output before pushing. Push local documents to an ephemeral tenant via multidoc import. By default, only changed files are pushed (partial mode). Use --sync to push all files. Shows a preview of changes before pushing unless --force is specified. Use --dry-run to preview only.';
|
|
37
|
+
static examples = [
|
|
38
|
+
`$ xano ephemeral push e4f2-9ab1-xyz1
|
|
39
|
+
Push from current directory (default partial mode)
|
|
40
|
+
`,
|
|
41
|
+
`$ xano ephemeral push e4f2-9ab1-xyz1 -d ./my-workspace
|
|
42
|
+
Push from a specific directory
|
|
43
|
+
`,
|
|
44
|
+
`$ xano ephemeral push e4f2-9ab1-xyz1 --sync
|
|
45
|
+
Push all files to the ephemeral tenant
|
|
46
|
+
`,
|
|
47
|
+
`$ xano ephemeral push e4f2-9ab1-xyz1 --sync --delete
|
|
48
|
+
Push all files and delete remote objects not included
|
|
49
|
+
`,
|
|
50
|
+
`$ xano ephemeral push e4f2-9ab1-xyz1 --dry-run
|
|
51
|
+
Preview changes without pushing
|
|
52
|
+
`,
|
|
53
|
+
`$ xano ephemeral push e4f2-9ab1-xyz1 --force
|
|
54
|
+
Skip preview and push immediately
|
|
55
|
+
`,
|
|
56
|
+
`$ xano ephemeral push e4f2-9ab1-xyz1 --records --env`,
|
|
57
|
+
`$ xano ephemeral push e4f2-9ab1-xyz1 --truncate`,
|
|
58
|
+
];
|
|
59
|
+
static flags = {
|
|
60
|
+
...BaseCommand.baseFlags,
|
|
61
|
+
delete: Flags.boolean({
|
|
62
|
+
default: false,
|
|
63
|
+
description: 'Delete remote objects not included in the push (requires --sync)',
|
|
64
|
+
required: false,
|
|
65
|
+
}),
|
|
66
|
+
directory: Flags.string({
|
|
67
|
+
char: 'd',
|
|
68
|
+
default: '.',
|
|
69
|
+
description: 'Directory containing documents to push (defaults to current directory)',
|
|
70
|
+
required: false,
|
|
71
|
+
}),
|
|
72
|
+
'dry-run': Flags.boolean({
|
|
73
|
+
default: false,
|
|
74
|
+
description: 'Show preview of changes without pushing (exit after preview)',
|
|
75
|
+
required: false,
|
|
76
|
+
}),
|
|
77
|
+
env: Flags.boolean({
|
|
78
|
+
default: false,
|
|
79
|
+
description: 'Include environment variables in import',
|
|
80
|
+
required: false,
|
|
81
|
+
}),
|
|
82
|
+
force: Flags.boolean({
|
|
83
|
+
default: false,
|
|
84
|
+
description: 'Skip preview and confirmation prompt (for CI/CD pipelines)',
|
|
85
|
+
required: false,
|
|
86
|
+
}),
|
|
87
|
+
guids: Flags.boolean({
|
|
88
|
+
allowNo: true,
|
|
89
|
+
default: true,
|
|
90
|
+
description: 'Write server-assigned GUIDs back to local files (use --no-guids to skip)',
|
|
91
|
+
required: false,
|
|
92
|
+
}),
|
|
93
|
+
records: Flags.boolean({
|
|
94
|
+
default: false,
|
|
95
|
+
description: 'Include records in import',
|
|
96
|
+
required: false,
|
|
97
|
+
}),
|
|
98
|
+
sync: Flags.boolean({
|
|
99
|
+
default: false,
|
|
100
|
+
description: 'Full push — send all files, not just changed ones. Required for --delete.',
|
|
101
|
+
required: false,
|
|
102
|
+
}),
|
|
103
|
+
transaction: Flags.boolean({
|
|
104
|
+
allowNo: true,
|
|
105
|
+
default: true,
|
|
106
|
+
description: '[CRITICAL] DO NOT run with --no-transaction without explicit user confirmation; this disables rollback. Wraps import in a database transaction (use --no-transaction for debugging purposes).',
|
|
107
|
+
required: false,
|
|
108
|
+
}),
|
|
109
|
+
truncate: Flags.boolean({
|
|
110
|
+
default: false,
|
|
111
|
+
description: '[CRITICAL] STOP and confirm with the user; this truncates live tables before importing.',
|
|
112
|
+
required: false,
|
|
113
|
+
}),
|
|
114
|
+
wait: Flags.boolean({
|
|
115
|
+
default: false,
|
|
116
|
+
description: 'After pushing, wait for auto-deployed microservices (tenant_deploy="auto") to become ready. Exits non-zero if any fails to deploy or the wait times out. Ignored with --dry-run.',
|
|
117
|
+
required: false,
|
|
118
|
+
}),
|
|
119
|
+
'wait-timeout': Flags.integer({
|
|
120
|
+
default: 300,
|
|
121
|
+
description: 'Seconds to wait for microservices to become ready when --wait is set (default 300).',
|
|
122
|
+
min: 1,
|
|
123
|
+
required: false,
|
|
124
|
+
}),
|
|
125
|
+
workspace: Flags.string({
|
|
126
|
+
char: 'w',
|
|
127
|
+
description: 'Workspace ID (uses profile workspace if not provided)',
|
|
128
|
+
required: false,
|
|
129
|
+
}),
|
|
130
|
+
};
|
|
131
|
+
/** Number of lines the last progress render wrote (for in-place TTY updates). */
|
|
132
|
+
lastRenderLines = 0;
|
|
133
|
+
async run() {
|
|
134
|
+
const { args, flags } = await this.parse(EphemeralPush);
|
|
135
|
+
const { profile } = this.resolveProfile(flags);
|
|
136
|
+
const workspaceId = flags.workspace || profile.workspace;
|
|
137
|
+
if (!workspaceId) {
|
|
138
|
+
this.error('No workspace ID provided. Ephemeral tenants require a workspace — use --workspace or set one in your profile.');
|
|
139
|
+
}
|
|
140
|
+
const inputDir = resolve(flags.directory);
|
|
141
|
+
if (!fs.existsSync(inputDir)) {
|
|
142
|
+
this.error(`Directory not found: ${inputDir}`);
|
|
143
|
+
}
|
|
144
|
+
if (!fs.statSync(inputDir).isDirectory()) {
|
|
145
|
+
this.error(`Not a directory: ${inputDir}`);
|
|
146
|
+
}
|
|
147
|
+
const tenantName = args.tenant_name;
|
|
148
|
+
const baseUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/tenant/${tenantName}`;
|
|
149
|
+
const target = {
|
|
150
|
+
buildDryRunUrl: (params) => `${baseUrl}/multidoc/dry-run?${params.toString()}`,
|
|
151
|
+
buildPushUrl: (params) => `${baseUrl}/multidoc?${params.toString()}`,
|
|
152
|
+
cliVersion: this.config.version,
|
|
153
|
+
instanceOrigin: profile.instance_origin,
|
|
154
|
+
label: `ephemeral tenant ${tenantName}`,
|
|
155
|
+
sourceWorkspaceId: profile.workspace,
|
|
156
|
+
supportsBranches: false,
|
|
157
|
+
supportsPartial: true,
|
|
158
|
+
warnOnWorkspaceMismatch: false,
|
|
159
|
+
};
|
|
160
|
+
const pushFlags = {
|
|
161
|
+
delete: flags.delete,
|
|
162
|
+
'dry-run': flags['dry-run'],
|
|
163
|
+
env: flags.env,
|
|
164
|
+
force: flags.force,
|
|
165
|
+
guids: flags.guids,
|
|
166
|
+
records: flags.records,
|
|
167
|
+
sync: flags.sync,
|
|
168
|
+
transaction: flags.transaction,
|
|
169
|
+
truncate: flags.truncate,
|
|
170
|
+
verbose: flags.verbose,
|
|
171
|
+
};
|
|
172
|
+
const result = await executePush({
|
|
173
|
+
accessToken: profile.access_token,
|
|
174
|
+
branch: '',
|
|
175
|
+
command: this,
|
|
176
|
+
inputDir,
|
|
177
|
+
verboseFetch: this.verboseFetch.bind(this),
|
|
178
|
+
}, target, pushFlags);
|
|
179
|
+
// When the platform leaves policy files out, it says so once.
|
|
180
|
+
const skipped = policiesSkippedNotice(result.response ?? result.preview);
|
|
181
|
+
if (skipped)
|
|
182
|
+
this.warn(skipped);
|
|
183
|
+
// --wait: after a real push, poll the tenant's live microservice status until
|
|
184
|
+
// every auto-deployed microservice (tenant_deploy="auto") is ready or the
|
|
185
|
+
// wait times out. Skipped on --dry-run (nothing was deployed).
|
|
186
|
+
if (flags.wait && !flags['dry-run']) {
|
|
187
|
+
await this.waitForDeploy(baseUrl, profile.access_token, tenantName, flags['wait-timeout'], flags.verbose);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Poll GET .../tenant/{name}/microservice until auto microservices settle.
|
|
192
|
+
* Renders a one-line-per-microservice progress view that updates in place, then
|
|
193
|
+
* exits non-zero if any auto microservice failed or the wait timed out.
|
|
194
|
+
*/
|
|
195
|
+
async waitForDeploy(baseUrl, accessToken, tenantName, timeoutSeconds, verbose) {
|
|
196
|
+
this.log('');
|
|
197
|
+
this.log(`Waiting for microservices to deploy (timeout ${timeoutSeconds}s)…`);
|
|
198
|
+
const result = await waitForMicroservices({
|
|
199
|
+
accessToken,
|
|
200
|
+
onPoll: (entries) => {
|
|
201
|
+
// Re-render the block: move the cursor up over the previous render (if any)
|
|
202
|
+
// and rewrite. Falls back to plain appends when not a TTY.
|
|
203
|
+
if (process.stdout.isTTY && this.lastRenderLines > 0) {
|
|
204
|
+
process.stdout.write(`[${this.lastRenderLines}A[0J`);
|
|
205
|
+
}
|
|
206
|
+
const lines = entries.map((e) => line(e));
|
|
207
|
+
for (const l of lines)
|
|
208
|
+
this.log(l);
|
|
209
|
+
this.lastRenderLines = process.stdout.isTTY ? lines.length : 0;
|
|
210
|
+
},
|
|
211
|
+
statusUrl: `${baseUrl}/microservice`,
|
|
212
|
+
timeoutMs: timeoutSeconds * 1000,
|
|
213
|
+
verbose,
|
|
214
|
+
verboseFetch: this.verboseFetch.bind(this),
|
|
215
|
+
});
|
|
216
|
+
const awaited = result.entries.filter((e) => isAwaited(e));
|
|
217
|
+
const ready = awaited.filter((e) => e.status === 'ok').length;
|
|
218
|
+
if (result.timedOut) {
|
|
219
|
+
this.error(`Timed out after ${timeoutSeconds}s waiting for microservices (${ready}/${awaited.length} ready). ` +
|
|
220
|
+
`Check status with the Xano dashboard or re-run with a larger --wait-timeout.`);
|
|
221
|
+
}
|
|
222
|
+
if (result.hadError) {
|
|
223
|
+
const failed = awaited.filter((e) => e.status === 'error').map((e) => `${e.name} (${e.detail || 'error'})`);
|
|
224
|
+
this.error(`Microservice deploy failed: ${failed.join(', ')}`);
|
|
225
|
+
}
|
|
226
|
+
this.log(`All microservices ready (${ready}/${awaited.length}).`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import BaseCommand from '../../../../../base-command.js';
|
|
2
|
+
export default class EphemeralStaticHostBuildDelete 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
|
+
build_id: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
'static-host': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
|
+
};
|
|
18
|
+
run(): Promise<void>;
|
|
19
|
+
private confirm;
|
|
20
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import BaseCommand from '../../../../../base-command.js';
|
|
3
|
+
export default class EphemeralStaticHostBuildDelete extends BaseCommand {
|
|
4
|
+
static args = {
|
|
5
|
+
tenant_name: Args.string({
|
|
6
|
+
description: 'Ephemeral tenant name',
|
|
7
|
+
required: true,
|
|
8
|
+
}),
|
|
9
|
+
};
|
|
10
|
+
static description = 'Delete a static host build for an ephemeral tenant permanently. This action cannot be undone.';
|
|
11
|
+
static examples = [
|
|
12
|
+
`$ xano ephemeral static_host build delete e4f2-9ab1-xyz1 --static-host default --build_id 52
|
|
13
|
+
Are you sure you want to delete build 52 from static host 'default'? This action cannot be undone. (y/N) y
|
|
14
|
+
Deleted build 52 from static host 'default'
|
|
15
|
+
`,
|
|
16
|
+
`$ xano ephemeral static_host build delete e4f2-9ab1-xyz1 -H default --build_id 52 --force
|
|
17
|
+
Deleted build 52 from static host 'default'
|
|
18
|
+
`,
|
|
19
|
+
`$ xano ephemeral static_host build delete e4f2-9ab1-xyz1 -H myhost --build_id 123 -w 40 -f
|
|
20
|
+
Deleted build 123 from static host 'myhost'
|
|
21
|
+
`,
|
|
22
|
+
`$ xano ephemeral static_host build delete e4f2-9ab1-xyz1 -H default --build_id 52 -f -o json`,
|
|
23
|
+
];
|
|
24
|
+
static flags = {
|
|
25
|
+
...BaseCommand.baseFlags,
|
|
26
|
+
build_id: Flags.string({
|
|
27
|
+
description: 'Build ID to delete',
|
|
28
|
+
required: true,
|
|
29
|
+
}),
|
|
30
|
+
force: Flags.boolean({
|
|
31
|
+
char: 'f',
|
|
32
|
+
default: false,
|
|
33
|
+
description: '[CRITICAL] NEVER run without explicit user confirmation. Skips the confirmation prompt.',
|
|
34
|
+
required: false,
|
|
35
|
+
}),
|
|
36
|
+
output: Flags.string({
|
|
37
|
+
char: 'o',
|
|
38
|
+
default: 'summary',
|
|
39
|
+
description: 'Output format',
|
|
40
|
+
options: ['summary', 'json'],
|
|
41
|
+
required: false,
|
|
42
|
+
}),
|
|
43
|
+
'static-host': Flags.string({
|
|
44
|
+
char: 'H',
|
|
45
|
+
description: 'Static host name',
|
|
46
|
+
required: true,
|
|
47
|
+
}),
|
|
48
|
+
workspace: Flags.string({
|
|
49
|
+
char: 'w',
|
|
50
|
+
description: 'Workspace ID (optional if set in profile)',
|
|
51
|
+
required: false,
|
|
52
|
+
}),
|
|
53
|
+
};
|
|
54
|
+
async run() {
|
|
55
|
+
const { args, flags } = await this.parse(EphemeralStaticHostBuildDelete);
|
|
56
|
+
const { profile, profileName } = this.resolveProfile(flags);
|
|
57
|
+
const tenantName = args.tenant_name;
|
|
58
|
+
const staticHost = flags['static-host'];
|
|
59
|
+
// Determine workspace_id from flag or profile
|
|
60
|
+
let workspaceId;
|
|
61
|
+
if (flags.workspace) {
|
|
62
|
+
workspaceId = flags.workspace;
|
|
63
|
+
}
|
|
64
|
+
else if (profile.workspace) {
|
|
65
|
+
workspaceId = profile.workspace;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
this.error(`Workspace ID is required. Either:\n` +
|
|
69
|
+
` 1. Provide it as a flag: xano ephemeral static_host build delete <tenant_name> --static-host <static_host> --build_id <id> -w <workspace_id>\n` +
|
|
70
|
+
` 2. Set it in your profile using: xano profile edit ${profileName} -w <workspace_id>`);
|
|
71
|
+
}
|
|
72
|
+
if (!flags.force) {
|
|
73
|
+
const confirmed = await this.confirm(`Are you sure you want to delete build ${flags.build_id} from static host '${staticHost}'? This action cannot be undone.`);
|
|
74
|
+
if (!confirmed) {
|
|
75
|
+
this.log('Deletion cancelled.');
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/tenant/${tenantName}/static_host/${staticHost}/build/${flags.build_id}`;
|
|
80
|
+
try {
|
|
81
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
82
|
+
headers: {
|
|
83
|
+
'accept': 'application/json',
|
|
84
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
85
|
+
},
|
|
86
|
+
method: 'DELETE',
|
|
87
|
+
}, flags.verbose, profile.access_token);
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
const errorText = await response.text();
|
|
90
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
91
|
+
}
|
|
92
|
+
if (flags.output === 'json') {
|
|
93
|
+
this.log(JSON.stringify({ build_id: flags.build_id, deleted: true, static_host: staticHost }, null, 2));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
this.log(`Deleted build ${flags.build_id} from static host '${staticHost}'`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
if (error instanceof Error) {
|
|
101
|
+
this.error(`Failed to delete build: ${error.message}`);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
this.error(`Failed to delete build: ${String(error)}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async confirm(message) {
|
|
109
|
+
const readline = await import('node:readline');
|
|
110
|
+
const rl = readline.createInterface({
|
|
111
|
+
input: process.stdin,
|
|
112
|
+
output: process.stdout,
|
|
113
|
+
});
|
|
114
|
+
return new Promise((resolve) => {
|
|
115
|
+
rl.question(`${message} (y/N) `, (answer) => {
|
|
116
|
+
rl.close();
|
|
117
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import BaseCommand from '../../../../../base-command.js';
|
|
2
|
+
export default class EphemeralStaticHostBuildGet 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
|
+
build_id: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
'static-host': 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,137 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import BaseCommand from '../../../../../base-command.js';
|
|
3
|
+
export default class EphemeralStaticHostBuildGet extends BaseCommand {
|
|
4
|
+
static args = {
|
|
5
|
+
tenant_name: Args.string({
|
|
6
|
+
description: 'Ephemeral tenant name',
|
|
7
|
+
required: true,
|
|
8
|
+
}),
|
|
9
|
+
};
|
|
10
|
+
static description = 'Get details of a specific build for a static host of an ephemeral tenant';
|
|
11
|
+
static examples = [
|
|
12
|
+
`$ xano ephemeral static_host build get e4f2-9ab1-xyz1 --static-host default --build_id 52
|
|
13
|
+
Build Details:
|
|
14
|
+
ID: 52
|
|
15
|
+
Name: v1.0.0
|
|
16
|
+
Status: completed
|
|
17
|
+
`,
|
|
18
|
+
`$ xano ephemeral static_host build get e4f2-9ab1-xyz1 -H default --build_id 52 -w 40
|
|
19
|
+
Build Details:
|
|
20
|
+
ID: 52
|
|
21
|
+
Name: v1.0.0
|
|
22
|
+
Status: completed
|
|
23
|
+
`,
|
|
24
|
+
`$ xano ephemeral static_host build get e4f2-9ab1-xyz1 -H myhost --build_id 123 --profile production
|
|
25
|
+
Build Details:
|
|
26
|
+
ID: 123
|
|
27
|
+
Name: production-build
|
|
28
|
+
`,
|
|
29
|
+
`$ xano ephemeral static_host build get e4f2-9ab1-xyz1 -H default --build_id 52 -o json
|
|
30
|
+
{
|
|
31
|
+
"id": 52,
|
|
32
|
+
"name": "v1.0.0",
|
|
33
|
+
"status": "completed"
|
|
34
|
+
}
|
|
35
|
+
`,
|
|
36
|
+
];
|
|
37
|
+
static flags = {
|
|
38
|
+
...BaseCommand.baseFlags,
|
|
39
|
+
build_id: Flags.string({
|
|
40
|
+
description: 'Build ID',
|
|
41
|
+
required: true,
|
|
42
|
+
}),
|
|
43
|
+
output: Flags.string({
|
|
44
|
+
char: 'o',
|
|
45
|
+
default: 'summary',
|
|
46
|
+
description: 'Output format',
|
|
47
|
+
options: ['summary', 'json'],
|
|
48
|
+
required: false,
|
|
49
|
+
}),
|
|
50
|
+
'static-host': Flags.string({
|
|
51
|
+
char: 'H',
|
|
52
|
+
description: 'Static host name',
|
|
53
|
+
required: true,
|
|
54
|
+
}),
|
|
55
|
+
workspace: Flags.string({
|
|
56
|
+
char: 'w',
|
|
57
|
+
description: 'Workspace ID (optional if set in profile)',
|
|
58
|
+
required: false,
|
|
59
|
+
}),
|
|
60
|
+
};
|
|
61
|
+
async run() {
|
|
62
|
+
const { args, flags } = await this.parse(EphemeralStaticHostBuildGet);
|
|
63
|
+
const { profile, profileName } = this.resolveProfile(flags);
|
|
64
|
+
const tenantName = args.tenant_name;
|
|
65
|
+
const staticHost = flags['static-host'];
|
|
66
|
+
// Determine workspace_id from flag or profile
|
|
67
|
+
let workspaceId;
|
|
68
|
+
if (flags.workspace) {
|
|
69
|
+
workspaceId = flags.workspace;
|
|
70
|
+
}
|
|
71
|
+
else if (profile.workspace) {
|
|
72
|
+
workspaceId = profile.workspace;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
this.error(`Workspace ID is required. Either:\n` +
|
|
76
|
+
` 1. Provide it as a flag: xano ephemeral static_host build get <tenant_name> --static-host <static_host> --build_id <id> -w <workspace_id>\n` +
|
|
77
|
+
` 2. Set it in your profile using: xano profile:edit ${profileName} -w <workspace_id>`);
|
|
78
|
+
}
|
|
79
|
+
// Construct the API URL
|
|
80
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/tenant/${tenantName}/static_host/${staticHost}/build/${flags.build_id}`;
|
|
81
|
+
// Fetch build from the API
|
|
82
|
+
try {
|
|
83
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
84
|
+
headers: {
|
|
85
|
+
'accept': 'application/json',
|
|
86
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
87
|
+
},
|
|
88
|
+
method: 'GET',
|
|
89
|
+
}, flags.verbose, profile.access_token);
|
|
90
|
+
if (!response.ok) {
|
|
91
|
+
const errorText = await response.text();
|
|
92
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
93
|
+
}
|
|
94
|
+
const build = await response.json();
|
|
95
|
+
// Validate response
|
|
96
|
+
if (!build || typeof build !== 'object') {
|
|
97
|
+
this.error('Unexpected API response format');
|
|
98
|
+
}
|
|
99
|
+
// Output results
|
|
100
|
+
if (flags.output === 'json') {
|
|
101
|
+
this.log(JSON.stringify(build, null, 2));
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// summary format
|
|
105
|
+
this.log('Build Details:');
|
|
106
|
+
this.log(`ID: ${build.id}`);
|
|
107
|
+
this.log(`Name: ${build.name}`);
|
|
108
|
+
if (build.description) {
|
|
109
|
+
this.log(`Description: ${build.description}`);
|
|
110
|
+
}
|
|
111
|
+
if (build.status) {
|
|
112
|
+
this.log(`Status: ${build.status}`);
|
|
113
|
+
}
|
|
114
|
+
if (typeof build.file_count === 'number') {
|
|
115
|
+
this.log(`Files: ${build.file_count}`);
|
|
116
|
+
}
|
|
117
|
+
if (typeof build.file_bytes === 'number') {
|
|
118
|
+
this.log(`Size: ${build.file_bytes} bytes`);
|
|
119
|
+
}
|
|
120
|
+
if (build.created_at) {
|
|
121
|
+
this.log(`Created: ${build.created_at}`);
|
|
122
|
+
}
|
|
123
|
+
if (build.updated_at) {
|
|
124
|
+
this.log(`Updated: ${build.updated_at}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
if (error instanceof Error) {
|
|
130
|
+
this.error(`Failed to fetch build: ${error.message}`);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
this.error(`Failed to fetch build: ${String(error)}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import BaseCommand from '../../../../../base-command.js';
|
|
2
|
+
export default class EphemeralStaticHostBuildList 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
|
+
'static-host': 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
|
+
page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, 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
|
+
}
|