@xano/cli 1.0.4-beta.3 → 1.0.4
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 +4 -51
- package/dist/base-command.d.ts +0 -27
- package/dist/base-command.js +1 -124
- package/dist/commands/auth/index.d.ts +1 -45
- package/dist/commands/auth/index.js +29 -197
- package/dist/commands/{tenant/snapshot/list → knowledge/pull}/index.d.ts +4 -6
- package/dist/commands/knowledge/pull/index.js +86 -0
- package/dist/commands/knowledge/push/index.d.ts +20 -0
- package/dist/commands/knowledge/push/index.js +126 -0
- package/dist/commands/static_host/build/create/index.d.ts +1 -9
- package/dist/commands/static_host/build/create/index.js +4 -54
- package/dist/commands/static_host/build/get/index.d.ts +1 -1
- package/dist/commands/static_host/build/get/index.js +10 -16
- package/dist/utils/knowledge-sync.d.ts +108 -0
- package/dist/utils/knowledge-sync.js +380 -0
- package/dist/utils/multidoc-push.js +17 -21
- package/dist/utils/reference-checker.js +2 -2
- package/oclif.manifest.json +2951 -4086
- package/package.json +1 -3
- package/dist/commands/static_host/build/delete/index.d.ts +0 -19
- package/dist/commands/static_host/build/delete/index.js +0 -114
- package/dist/commands/static_host/build/pull/index.d.ts +0 -52
- package/dist/commands/static_host/build/pull/index.js +0 -300
- package/dist/commands/static_host/build/push/index.d.ts +0 -23
- package/dist/commands/static_host/build/push/index.js +0 -225
- package/dist/commands/static_host/create/index.d.ts +0 -17
- package/dist/commands/static_host/create/index.js +0 -86
- package/dist/commands/static_host/deploy/index.d.ts +0 -18
- package/dist/commands/static_host/deploy/index.js +0 -105
- package/dist/commands/static_host/edit/index.d.ts +0 -23
- package/dist/commands/static_host/edit/index.js +0 -151
- package/dist/commands/static_host/get/index.d.ts +0 -18
- package/dist/commands/static_host/get/index.js +0 -94
- package/dist/commands/static_host/migrate/index.d.ts +0 -44
- package/dist/commands/static_host/migrate/index.js +0 -205
- package/dist/commands/tenant/snapshot/create/index.d.ts +0 -17
- package/dist/commands/tenant/snapshot/create/index.js +0 -78
- package/dist/commands/tenant/snapshot/delete/index.d.ts +0 -19
- package/dist/commands/tenant/snapshot/delete/index.js +0 -102
- package/dist/commands/tenant/snapshot/list/index.js +0 -96
- package/dist/commands/tenant/snapshot/swap/index.d.ts +0 -19
- package/dist/commands/tenant/snapshot/swap/index.js +0 -103
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import { dirname, resolve } from 'node:path';
|
|
4
|
+
import BaseCommand from '../../../base-command.js';
|
|
5
|
+
import { fetchKnowledgeList, safeKnowledgePath } from '../../../utils/knowledge-sync.js';
|
|
6
|
+
export default class KnowledgePull extends BaseCommand {
|
|
7
|
+
static description = 'Pull knowledge files from a workspace. Each knowledge item name is a path (e.g. "some/thing/CLAUDE.md") and its content is written to that path under the output directory.';
|
|
8
|
+
static examples = [
|
|
9
|
+
`$ xano knowledge pull
|
|
10
|
+
Pulled 4 knowledge files to current directory
|
|
11
|
+
`,
|
|
12
|
+
`$ xano knowledge pull -d ./knowledge
|
|
13
|
+
Pulled 4 knowledge files to ./knowledge
|
|
14
|
+
`,
|
|
15
|
+
`$ xano knowledge pull -d ./knowledge -w 40`,
|
|
16
|
+
`$ xano knowledge pull -b dev`,
|
|
17
|
+
];
|
|
18
|
+
static flags = {
|
|
19
|
+
...BaseCommand.baseFlags,
|
|
20
|
+
branch: Flags.string({
|
|
21
|
+
char: 'b',
|
|
22
|
+
description: 'Branch name (optional if set in profile, defaults to live)',
|
|
23
|
+
required: false,
|
|
24
|
+
}),
|
|
25
|
+
directory: Flags.string({
|
|
26
|
+
char: 'd',
|
|
27
|
+
default: '.',
|
|
28
|
+
description: 'Output directory for pulled knowledge files (defaults to current directory)',
|
|
29
|
+
required: false,
|
|
30
|
+
}),
|
|
31
|
+
workspace: Flags.string({
|
|
32
|
+
char: 'w',
|
|
33
|
+
description: 'Workspace ID (optional if set in profile)',
|
|
34
|
+
required: false,
|
|
35
|
+
}),
|
|
36
|
+
};
|
|
37
|
+
async run() {
|
|
38
|
+
const { flags } = await this.parse(KnowledgePull);
|
|
39
|
+
const { profile, profileName } = this.resolveProfile(flags);
|
|
40
|
+
// Determine workspace_id from flag or profile
|
|
41
|
+
let workspaceId;
|
|
42
|
+
if (flags.workspace) {
|
|
43
|
+
workspaceId = flags.workspace;
|
|
44
|
+
}
|
|
45
|
+
else if (profile.workspace) {
|
|
46
|
+
workspaceId = profile.workspace;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
this.error(`Workspace ID is required. Either:\n` +
|
|
50
|
+
` 1. Provide it as a flag: xano knowledge pull -w <workspace_id>\n` +
|
|
51
|
+
` 2. Set it in your profile using: xano profile:edit ${profileName} -w <workspace_id>`);
|
|
52
|
+
}
|
|
53
|
+
const branch = flags.branch || profile.branch || '';
|
|
54
|
+
let items;
|
|
55
|
+
try {
|
|
56
|
+
items = await fetchKnowledgeList({
|
|
57
|
+
accessToken: profile.access_token,
|
|
58
|
+
branch,
|
|
59
|
+
instanceOrigin: profile.instance_origin,
|
|
60
|
+
verboseFetch: this.verboseFetch.bind(this),
|
|
61
|
+
workspaceId,
|
|
62
|
+
}, flags.verbose);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
this.error(`Failed to fetch knowledge: ${error.message}`);
|
|
66
|
+
}
|
|
67
|
+
if (items.length === 0) {
|
|
68
|
+
this.log('No knowledge found in workspace');
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const outputDir = resolve(flags.directory);
|
|
72
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
73
|
+
let writtenCount = 0;
|
|
74
|
+
for (const item of items) {
|
|
75
|
+
const filePath = safeKnowledgePath(outputDir, item.name);
|
|
76
|
+
if (!filePath) {
|
|
77
|
+
this.warn(`Skipping knowledge "${item.name}": name is not a safe relative path`);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
fs.mkdirSync(dirname(filePath), { recursive: true });
|
|
81
|
+
fs.writeFileSync(filePath, item.content ?? '', 'utf8');
|
|
82
|
+
writtenCount++;
|
|
83
|
+
}
|
|
84
|
+
this.log(`Pulled ${writtenCount} knowledge files to ${flags.directory}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class KnowledgePush extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
delete: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
directory: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
exclude: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
include: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
sync: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
18
|
+
};
|
|
19
|
+
run(): Promise<void>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { 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 { executeKnowledgePush } from '../../../utils/knowledge-sync.js';
|
|
6
|
+
export default class KnowledgePush extends BaseCommand {
|
|
7
|
+
static description = '[IMPORTANT] ALWAYS run --dry-run first and show the user the output before pushing. Push local knowledge files to a workspace. Each file becomes a knowledge item named by its relative path (e.g. "some/thing/CLAUDE.md"). By default, only changed files are pushed (partial mode). Use --sync to compare all files (required for --delete). Shows a preview of changes before pushing unless --force is specified. Use --dry-run to preview only.';
|
|
8
|
+
static examples = [
|
|
9
|
+
`$ xano knowledge push
|
|
10
|
+
Push from current directory (default partial mode)
|
|
11
|
+
`,
|
|
12
|
+
`$ xano knowledge push -d ./knowledge
|
|
13
|
+
Push from a specific directory
|
|
14
|
+
`,
|
|
15
|
+
`$ xano knowledge push --dry-run
|
|
16
|
+
Preview changes without pushing
|
|
17
|
+
`,
|
|
18
|
+
`$ xano knowledge push --sync --delete
|
|
19
|
+
Push all files and delete remote knowledge not included
|
|
20
|
+
`,
|
|
21
|
+
`$ xano knowledge push --force
|
|
22
|
+
Skip preview and push immediately (for CI/CD)
|
|
23
|
+
`,
|
|
24
|
+
`$ xano knowledge push -i "guides/*"
|
|
25
|
+
Push only files matching the glob pattern
|
|
26
|
+
`,
|
|
27
|
+
`$ xano knowledge push -e "**/README.md"
|
|
28
|
+
Push all files except READMEs
|
|
29
|
+
`,
|
|
30
|
+
];
|
|
31
|
+
static flags = {
|
|
32
|
+
...BaseCommand.baseFlags,
|
|
33
|
+
branch: Flags.string({
|
|
34
|
+
char: 'b',
|
|
35
|
+
description: 'Branch name (optional if set in profile, defaults to live)',
|
|
36
|
+
required: false,
|
|
37
|
+
}),
|
|
38
|
+
delete: Flags.boolean({
|
|
39
|
+
default: false,
|
|
40
|
+
description: '[CRITICAL] STOP and confirm with the user before running. Delete remote knowledge not included in the push (requires --sync).',
|
|
41
|
+
required: false,
|
|
42
|
+
}),
|
|
43
|
+
directory: Flags.string({
|
|
44
|
+
char: 'd',
|
|
45
|
+
default: '.',
|
|
46
|
+
description: 'Directory containing knowledge files to push (defaults to current directory)',
|
|
47
|
+
required: false,
|
|
48
|
+
}),
|
|
49
|
+
'dry-run': Flags.boolean({
|
|
50
|
+
default: false,
|
|
51
|
+
description: 'Show preview of changes without pushing (exit after preview)',
|
|
52
|
+
required: false,
|
|
53
|
+
}),
|
|
54
|
+
exclude: Flags.string({
|
|
55
|
+
char: 'e',
|
|
56
|
+
description: 'Glob pattern to exclude files (e.g. "**/README.md"). Matched against relative paths from the push directory.',
|
|
57
|
+
multiple: true,
|
|
58
|
+
required: false,
|
|
59
|
+
}),
|
|
60
|
+
force: Flags.boolean({
|
|
61
|
+
default: false,
|
|
62
|
+
description: '[CRITICAL] NEVER run without explicit user confirmation. Skips preview and confirmation prompt (for CI/CD pipelines).',
|
|
63
|
+
required: false,
|
|
64
|
+
}),
|
|
65
|
+
include: Flags.string({
|
|
66
|
+
char: 'i',
|
|
67
|
+
description: 'Glob pattern to include files (e.g. "guides/*"). Matched against relative paths from the push directory.',
|
|
68
|
+
multiple: true,
|
|
69
|
+
required: false,
|
|
70
|
+
}),
|
|
71
|
+
sync: Flags.boolean({
|
|
72
|
+
default: false,
|
|
73
|
+
description: 'Full push — compare all files, not just changed ones. Required for --delete.',
|
|
74
|
+
required: false,
|
|
75
|
+
}),
|
|
76
|
+
workspace: Flags.string({
|
|
77
|
+
char: 'w',
|
|
78
|
+
description: 'Workspace ID (optional if set in profile)',
|
|
79
|
+
required: false,
|
|
80
|
+
}),
|
|
81
|
+
};
|
|
82
|
+
async run() {
|
|
83
|
+
const { flags } = await this.parse(KnowledgePush);
|
|
84
|
+
const { profile, profileName } = this.resolveProfile(flags);
|
|
85
|
+
// Determine workspace_id from flag or profile
|
|
86
|
+
let workspaceId;
|
|
87
|
+
if (flags.workspace) {
|
|
88
|
+
workspaceId = flags.workspace;
|
|
89
|
+
}
|
|
90
|
+
else if (profile.workspace) {
|
|
91
|
+
workspaceId = profile.workspace;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
this.error(`Workspace ID is required. Either:\n` +
|
|
95
|
+
` 1. Provide it as a flag: xano knowledge push -w <workspace_id>\n` +
|
|
96
|
+
` 2. Set it in your profile using: xano profile:edit ${profileName} -w <workspace_id>`);
|
|
97
|
+
}
|
|
98
|
+
const inputDir = resolve(flags.directory);
|
|
99
|
+
if (!fs.existsSync(inputDir)) {
|
|
100
|
+
this.error(`Directory not found: ${inputDir}`);
|
|
101
|
+
}
|
|
102
|
+
if (!fs.statSync(inputDir).isDirectory()) {
|
|
103
|
+
this.error(`Not a directory: ${inputDir}`);
|
|
104
|
+
}
|
|
105
|
+
const branch = flags.branch || profile.branch || '';
|
|
106
|
+
const pushFlags = {
|
|
107
|
+
delete: flags.delete,
|
|
108
|
+
'dry-run': flags['dry-run'],
|
|
109
|
+
exclude: flags.exclude,
|
|
110
|
+
force: flags.force,
|
|
111
|
+
include: flags.include,
|
|
112
|
+
sync: flags.sync,
|
|
113
|
+
verbose: flags.verbose,
|
|
114
|
+
};
|
|
115
|
+
await executeKnowledgePush({
|
|
116
|
+
accessToken: profile.access_token,
|
|
117
|
+
branch,
|
|
118
|
+
cliVersion: this.config.version,
|
|
119
|
+
command: this,
|
|
120
|
+
inputDir,
|
|
121
|
+
instanceOrigin: profile.instance_origin,
|
|
122
|
+
verboseFetch: this.verboseFetch.bind(this),
|
|
123
|
+
workspaceId,
|
|
124
|
+
}, pushFlags);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
import BaseCommand from '../../../../base-command.js';
|
|
2
|
-
/**
|
|
3
|
-
* Generate a default build name from a compact timestamp: `YYYYMMDD-HHmmss`
|
|
4
|
-
* (e.g. `20260531-143022`). Sortable, distinct down to the second, and uses
|
|
5
|
-
* local time so it lines up with when the user ran the command.
|
|
6
|
-
*/
|
|
7
|
-
export declare function generateBuildName(date?: Date): string;
|
|
8
2
|
export default class StaticHostBuildCreate extends BaseCommand {
|
|
9
|
-
static hidden: boolean;
|
|
10
3
|
static args: {
|
|
11
4
|
static_host: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
12
5
|
};
|
|
@@ -15,8 +8,7 @@ export default class StaticHostBuildCreate extends BaseCommand {
|
|
|
15
8
|
static flags: {
|
|
16
9
|
description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
10
|
file: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
|
-
name: import("@oclif/core/interfaces").OptionFlag<string
|
|
19
|
-
'no-wait': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
12
|
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
13
|
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
22
14
|
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -2,42 +2,20 @@ import { Args, Flags } from '@oclif/core';
|
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
3
|
import * as path from 'node:path';
|
|
4
4
|
import BaseCommand from '../../../../base-command.js';
|
|
5
|
-
const pad2 = (n) => String(n).padStart(2, '0');
|
|
6
|
-
/**
|
|
7
|
-
* Generate a default build name from a compact timestamp: `YYYYMMDD-HHmmss`
|
|
8
|
-
* (e.g. `20260531-143022`). Sortable, distinct down to the second, and uses
|
|
9
|
-
* local time so it lines up with when the user ran the command.
|
|
10
|
-
*/
|
|
11
|
-
export function generateBuildName(date = new Date()) {
|
|
12
|
-
const y = date.getFullYear();
|
|
13
|
-
const mo = pad2(date.getMonth() + 1);
|
|
14
|
-
const d = pad2(date.getDate());
|
|
15
|
-
const h = pad2(date.getHours());
|
|
16
|
-
const mi = pad2(date.getMinutes());
|
|
17
|
-
const s = pad2(date.getSeconds());
|
|
18
|
-
return `${y}${mo}${d}-${h}${mi}${s}`;
|
|
19
|
-
}
|
|
20
5
|
export default class StaticHostBuildCreate extends BaseCommand {
|
|
21
|
-
static hidden = true;
|
|
22
6
|
static args = {
|
|
23
7
|
static_host: Args.string({
|
|
24
8
|
description: 'Static Host name',
|
|
25
9
|
required: true,
|
|
26
10
|
}),
|
|
27
11
|
};
|
|
28
|
-
static description = '
|
|
12
|
+
static description = 'Create a new build for a static host';
|
|
29
13
|
static examples = [
|
|
30
14
|
`$ xano static_host:build:create default -f ./build.zip -n "v1.0.0"
|
|
31
15
|
Build created successfully!
|
|
32
16
|
ID: 123
|
|
33
17
|
Name: v1.0.0
|
|
34
18
|
Status: pending
|
|
35
|
-
`,
|
|
36
|
-
`$ xano static_host:build:create default -f ./build.zip
|
|
37
|
-
Build created successfully!
|
|
38
|
-
ID: 123
|
|
39
|
-
Name: 20260531-143022
|
|
40
|
-
Status: pending
|
|
41
19
|
`,
|
|
42
20
|
`$ xano static_host:build:create default -w 40 -f ./dist.zip -n "production" -d "Production build"
|
|
43
21
|
Build created successfully!
|
|
@@ -67,13 +45,8 @@ Description: Production build
|
|
|
67
45
|
}),
|
|
68
46
|
name: Flags.string({
|
|
69
47
|
char: 'n',
|
|
70
|
-
description: 'Build name
|
|
71
|
-
required:
|
|
72
|
-
}),
|
|
73
|
-
'no-wait': Flags.boolean({
|
|
74
|
-
default: false,
|
|
75
|
-
description: 'Return immediately after upload instead of waiting for the build to finish',
|
|
76
|
-
required: false,
|
|
48
|
+
description: 'Build name',
|
|
49
|
+
required: true,
|
|
77
50
|
}),
|
|
78
51
|
output: Flags.string({
|
|
79
52
|
char: 'o',
|
|
@@ -89,7 +62,6 @@ Description: Production build
|
|
|
89
62
|
}),
|
|
90
63
|
};
|
|
91
64
|
async run() {
|
|
92
|
-
this.warn('`static_host build create` is deprecated. Use `static_host build push -f <file>` instead.');
|
|
93
65
|
const { args, flags } = await this.parse(StaticHostBuildCreate);
|
|
94
66
|
const { profile, profileName } = this.resolveProfile(flags);
|
|
95
67
|
// Determine workspace_id from flag or profile
|
|
@@ -130,10 +102,7 @@ Description: Production build
|
|
|
130
102
|
const fileBuffer = fs.readFileSync(filePath);
|
|
131
103
|
const blob = new Blob([fileBuffer], { type: 'application/zip' });
|
|
132
104
|
formData.append('file', blob, path.basename(filePath));
|
|
133
|
-
|
|
134
|
-
// created without thinking up a label each time.
|
|
135
|
-
const buildName = flags.name ?? generateBuildName();
|
|
136
|
-
formData.append('name', buildName);
|
|
105
|
+
formData.append('name', flags.name);
|
|
137
106
|
if (flags.description) {
|
|
138
107
|
formData.append('description', flags.description);
|
|
139
108
|
}
|
|
@@ -172,25 +141,6 @@ Description: Production build
|
|
|
172
141
|
this.log(`Description: ${flags.description}`);
|
|
173
142
|
}
|
|
174
143
|
}
|
|
175
|
-
// Async (package.json) builds keep running after upload. Unless --no-wait,
|
|
176
|
-
// poll until the build finishes so the CLI mirrors the UI's progress.
|
|
177
|
-
const inProgress = result.status !== undefined && !['error', 'ok'].includes(result.status);
|
|
178
|
-
if (inProgress && !flags['no-wait']) {
|
|
179
|
-
const finalStatus = await this.waitForBuild({
|
|
180
|
-
buildId: result.id,
|
|
181
|
-
profile,
|
|
182
|
-
quiet: flags.output === 'json',
|
|
183
|
-
staticHost: args.static_host,
|
|
184
|
-
verbose: flags.verbose,
|
|
185
|
-
workspaceId,
|
|
186
|
-
});
|
|
187
|
-
if (finalStatus === 'error') {
|
|
188
|
-
this.error(`Build ${result.id} failed (status: error). Check the build log with: xano static_host build get ${args.static_host} --build_id ${result.id}`);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
if (flags.output !== 'json') {
|
|
192
|
-
await this.logStaticHostUrls({ profile, staticHost: args.static_host, verbose: flags.verbose, workspaceId });
|
|
193
|
-
}
|
|
194
144
|
}
|
|
195
145
|
catch (error) {
|
|
196
146
|
if (error instanceof Error) {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import BaseCommand from '../../../../base-command.js';
|
|
2
2
|
export default class StaticHostBuildGet extends BaseCommand {
|
|
3
3
|
static args: {
|
|
4
|
+
build_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
4
5
|
static_host: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
6
|
};
|
|
6
7
|
static description: string;
|
|
7
8
|
static examples: string[];
|
|
8
9
|
static flags: {
|
|
9
|
-
build_id: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
10
|
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
11
|
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
12
|
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -2,6 +2,10 @@ import { Args, Flags } from '@oclif/core';
|
|
|
2
2
|
import BaseCommand from '../../../../base-command.js';
|
|
3
3
|
export default class StaticHostBuildGet extends BaseCommand {
|
|
4
4
|
static args = {
|
|
5
|
+
build_id: Args.string({
|
|
6
|
+
description: 'Build ID',
|
|
7
|
+
required: true,
|
|
8
|
+
}),
|
|
5
9
|
static_host: Args.string({
|
|
6
10
|
description: 'Static Host name',
|
|
7
11
|
required: true,
|
|
@@ -9,24 +13,24 @@ export default class StaticHostBuildGet extends BaseCommand {
|
|
|
9
13
|
};
|
|
10
14
|
static description = 'Get details of a specific build for a static host';
|
|
11
15
|
static examples = [
|
|
12
|
-
`$ xano static_host:build:get default
|
|
16
|
+
`$ xano static_host:build:get default 52
|
|
13
17
|
Build Details:
|
|
14
18
|
ID: 52
|
|
15
19
|
Name: v1.0.0
|
|
16
20
|
Status: completed
|
|
17
21
|
`,
|
|
18
|
-
`$ xano static_host:build:get default
|
|
22
|
+
`$ xano static_host:build:get default 52 -w 40
|
|
19
23
|
Build Details:
|
|
20
24
|
ID: 52
|
|
21
25
|
Name: v1.0.0
|
|
22
26
|
Status: completed
|
|
23
27
|
`,
|
|
24
|
-
`$ xano static_host:build:get myhost
|
|
28
|
+
`$ xano static_host:build:get myhost 123 --profile production
|
|
25
29
|
Build Details:
|
|
26
30
|
ID: 123
|
|
27
31
|
Name: production-build
|
|
28
32
|
`,
|
|
29
|
-
`$ xano static_host:build:get default
|
|
33
|
+
`$ xano static_host:build:get default 52 -o json
|
|
30
34
|
{
|
|
31
35
|
"id": 52,
|
|
32
36
|
"name": "v1.0.0",
|
|
@@ -36,10 +40,6 @@ Name: production-build
|
|
|
36
40
|
];
|
|
37
41
|
static flags = {
|
|
38
42
|
...BaseCommand.baseFlags,
|
|
39
|
-
build_id: Flags.string({
|
|
40
|
-
description: 'Build ID',
|
|
41
|
-
required: true,
|
|
42
|
-
}),
|
|
43
43
|
output: Flags.string({
|
|
44
44
|
char: 'o',
|
|
45
45
|
default: 'summary',
|
|
@@ -66,11 +66,11 @@ Name: production-build
|
|
|
66
66
|
}
|
|
67
67
|
else {
|
|
68
68
|
this.error(`Workspace ID is required. Either:\n` +
|
|
69
|
-
` 1. Provide it as a flag: xano static_host:build:get <static_host>
|
|
69
|
+
` 1. Provide it as a flag: xano static_host:build:get <static_host> <build_id> -w <workspace_id>\n` +
|
|
70
70
|
` 2. Set it in your profile using: xano profile:edit ${profileName} -w <workspace_id>`);
|
|
71
71
|
}
|
|
72
72
|
// Construct the API URL
|
|
73
|
-
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/static_host/${args.static_host}/build/${
|
|
73
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/static_host/${args.static_host}/build/${args.build_id}`;
|
|
74
74
|
// Fetch build from the API
|
|
75
75
|
try {
|
|
76
76
|
const response = await this.verboseFetch(apiUrl, {
|
|
@@ -104,12 +104,6 @@ Name: production-build
|
|
|
104
104
|
if (build.status) {
|
|
105
105
|
this.log(`Status: ${build.status}`);
|
|
106
106
|
}
|
|
107
|
-
if (typeof build.file_count === 'number') {
|
|
108
|
-
this.log(`Files: ${build.file_count}`);
|
|
109
|
-
}
|
|
110
|
-
if (typeof build.file_bytes === 'number') {
|
|
111
|
-
this.log(`Size: ${build.file_bytes} bytes`);
|
|
112
|
-
}
|
|
113
107
|
if (build.created_at) {
|
|
114
108
|
this.log(`Created: ${build.created_at}`);
|
|
115
109
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
/** A knowledge record as returned by the Metadata API list endpoint. */
|
|
3
|
+
export interface KnowledgeItem {
|
|
4
|
+
branch?: {
|
|
5
|
+
id: number;
|
|
6
|
+
};
|
|
7
|
+
content?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
enabled?: boolean;
|
|
10
|
+
id: number;
|
|
11
|
+
knowledge_type?: string;
|
|
12
|
+
mode?: string;
|
|
13
|
+
name: string;
|
|
14
|
+
references?: string[];
|
|
15
|
+
scope?: string;
|
|
16
|
+
tag?: string[];
|
|
17
|
+
}
|
|
18
|
+
/** A local knowledge file: its API name (relative path) and content. */
|
|
19
|
+
export interface LocalKnowledgeEntry {
|
|
20
|
+
content: string;
|
|
21
|
+
filePath: string;
|
|
22
|
+
name: string;
|
|
23
|
+
}
|
|
24
|
+
export interface KnowledgePlan {
|
|
25
|
+
creates: LocalKnowledgeEntry[];
|
|
26
|
+
deletes: KnowledgeItem[];
|
|
27
|
+
unchanged: Array<{
|
|
28
|
+
entry: LocalKnowledgeEntry;
|
|
29
|
+
remote: KnowledgeItem;
|
|
30
|
+
}>;
|
|
31
|
+
updates: Array<{
|
|
32
|
+
entry: LocalKnowledgeEntry;
|
|
33
|
+
remote: KnowledgeItem;
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
export interface KnowledgePushFlags {
|
|
37
|
+
delete: boolean;
|
|
38
|
+
'dry-run': boolean;
|
|
39
|
+
exclude?: string[];
|
|
40
|
+
force: boolean;
|
|
41
|
+
include?: string[];
|
|
42
|
+
sync: boolean;
|
|
43
|
+
verbose: boolean;
|
|
44
|
+
}
|
|
45
|
+
export interface KnowledgeApiContext {
|
|
46
|
+
accessToken: string;
|
|
47
|
+
/** Branch name ('' = live branch) */
|
|
48
|
+
branch: string;
|
|
49
|
+
instanceOrigin: string;
|
|
50
|
+
verboseFetch: (url: string, options: RequestInit, verbose: boolean, authToken?: string) => Promise<Response>;
|
|
51
|
+
workspaceId: string;
|
|
52
|
+
}
|
|
53
|
+
export interface KnowledgePushContext extends KnowledgeApiContext {
|
|
54
|
+
cliVersion: string;
|
|
55
|
+
command: Command;
|
|
56
|
+
inputDir: string;
|
|
57
|
+
}
|
|
58
|
+
export declare const MAX_NAME_LENGTH = 200;
|
|
59
|
+
/**
|
|
60
|
+
* Validate a knowledge name (a relative path like "some/thing/CLAUDE.md")
|
|
61
|
+
* against the Metadata API's accepted character set and length.
|
|
62
|
+
* Returns null when valid, otherwise a human-readable reason.
|
|
63
|
+
*/
|
|
64
|
+
export declare function validateKnowledgeName(name: string): null | string;
|
|
65
|
+
/**
|
|
66
|
+
* Resolve a knowledge name to a local file path inside baseDir, rejecting
|
|
67
|
+
* names that would escape it (absolute paths, "..", etc.).
|
|
68
|
+
* Returns null when the name is unsafe.
|
|
69
|
+
*/
|
|
70
|
+
export declare function safeKnowledgePath(baseDir: string, name: null | string | undefined): null | string;
|
|
71
|
+
/**
|
|
72
|
+
* Infer the knowledge_type for a newly created knowledge item from its
|
|
73
|
+
* filename. AGENTS.md and SKILL.md are special-cased; everything else is a doc.
|
|
74
|
+
*/
|
|
75
|
+
export declare function inferKnowledgeType(name: string): string;
|
|
76
|
+
/**
|
|
77
|
+
* Recursively collect all files under a directory, skipping hidden files,
|
|
78
|
+
* hidden directories, and node_modules. Sorted for deterministic ordering.
|
|
79
|
+
*/
|
|
80
|
+
export declare function collectKnowledgeFiles(dir: string): string[];
|
|
81
|
+
/**
|
|
82
|
+
* Read local files into knowledge entries. The knowledge name is the path
|
|
83
|
+
* relative to inputDir, using forward slashes.
|
|
84
|
+
*/
|
|
85
|
+
export declare function readKnowledgeEntries(files: string[], inputDir: string): LocalKnowledgeEntry[];
|
|
86
|
+
/**
|
|
87
|
+
* Compute the create/update/delete plan by matching local entries against
|
|
88
|
+
* remote knowledge items by name.
|
|
89
|
+
*/
|
|
90
|
+
export declare function buildKnowledgePlan(localEntries: LocalKnowledgeEntry[], remoteItems: KnowledgeItem[]): KnowledgePlan;
|
|
91
|
+
export declare function renderKnowledgePreview(plan: KnowledgePlan, opts: {
|
|
92
|
+
cliVersion: string;
|
|
93
|
+
instanceOrigin: string;
|
|
94
|
+
label: string;
|
|
95
|
+
partial: boolean;
|
|
96
|
+
willDelete: boolean;
|
|
97
|
+
}, log: (msg: string) => void): void;
|
|
98
|
+
/**
|
|
99
|
+
* Fetch all knowledge items (with content) for a workspace/branch.
|
|
100
|
+
*/
|
|
101
|
+
export declare function fetchKnowledgeList(ctx: KnowledgeApiContext, verbose: boolean): Promise<KnowledgeItem[]>;
|
|
102
|
+
/**
|
|
103
|
+
* Execute a knowledge push: collect local files, diff against remote by name,
|
|
104
|
+
* preview, confirm, then apply creates/updates/deletes through the Metadata API.
|
|
105
|
+
* Mirrors the UX of `workspace push` (partial by default, --sync for full,
|
|
106
|
+
* --delete to remove remote-only items, --dry-run / --force).
|
|
107
|
+
*/
|
|
108
|
+
export declare function executeKnowledgePush(ctx: KnowledgePushContext, flags: KnowledgePushFlags): Promise<void>;
|