@xano/cli 1.0.4-beta.3 → 1.0.4-beta.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/dist/commands/sandbox/pull/index.js +12 -2
- package/dist/commands/sandbox/push/index.d.ts +1 -1
- package/dist/commands/sandbox/push/index.js +17 -13
- package/dist/commands/workspace/pull/index.js +13 -2
- package/dist/commands/workspace/push/index.d.ts +1 -1
- package/dist/commands/workspace/push/index.js +15 -5
- package/dist/utils/knowledge-sync.d.ts +113 -0
- package/dist/utils/knowledge-sync.js +404 -0
- package/dist/utils/multidoc-push.d.ts +22 -0
- package/dist/utils/multidoc-push.js +242 -91
- package/oclif.manifest.json +2426 -2424
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@ import { Flags } from '@oclif/core';
|
|
|
2
2
|
import snakeCase from 'lodash.snakecase';
|
|
3
3
|
import BaseCommand from '../../../base-command.js';
|
|
4
4
|
import { buildApiGroupFolderResolver, parseDocument } from '../../../utils/document-parser.js';
|
|
5
|
+
import { fetchKnowledge, writeKnowledge } from '../../../utils/knowledge-sync.js';
|
|
5
6
|
import * as fs from 'node:fs';
|
|
6
7
|
import * as path from 'node:path';
|
|
7
8
|
export default class SandboxPull extends BaseCommand {
|
|
@@ -87,7 +88,6 @@ Pulled 42 documents from sandbox environment to ./my-sandbox
|
|
|
87
88
|
}
|
|
88
89
|
if (documents.length === 0) {
|
|
89
90
|
this.log('No documents found in response');
|
|
90
|
-
return;
|
|
91
91
|
}
|
|
92
92
|
const outputDir = path.resolve(flags.directory);
|
|
93
93
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
@@ -182,7 +182,17 @@ Pulled 42 documents from sandbox environment to ./my-sandbox
|
|
|
182
182
|
fs.writeFileSync(filePath, doc.content, 'utf8');
|
|
183
183
|
writtenCount++;
|
|
184
184
|
}
|
|
185
|
-
|
|
185
|
+
// ── Pull knowledge ────────────────────────────────────────────────────
|
|
186
|
+
const knowledgeUrl = `${profile.instance_origin}/api:meta/sandbox/knowledge/sync`;
|
|
187
|
+
const knowledgeObjects = await fetchKnowledge(knowledgeUrl, '', profile.access_token, this.verboseFetch.bind(this), flags.verbose);
|
|
188
|
+
let knowledgeCount = 0;
|
|
189
|
+
if (knowledgeObjects.length > 0) {
|
|
190
|
+
knowledgeCount = writeKnowledge(knowledgeObjects, outputDir);
|
|
191
|
+
}
|
|
192
|
+
const parts = [`${writtenCount} documents`];
|
|
193
|
+
if (knowledgeCount > 0)
|
|
194
|
+
parts.push(`${knowledgeCount} knowledge file${knowledgeCount === 1 ? '' : 's'}`);
|
|
195
|
+
this.log(`Pulled ${parts.join(' + ')} from sandbox environment to ${flags.directory}`);
|
|
186
196
|
}
|
|
187
197
|
sanitizeFilename(name) {
|
|
188
198
|
return snakeCase(name.replaceAll('"', ''));
|
|
@@ -130,12 +130,29 @@ Push and open sandbox review in the browser
|
|
|
130
130
|
branch: '',
|
|
131
131
|
command: this,
|
|
132
132
|
inputDir,
|
|
133
|
+
knowledge: {
|
|
134
|
+
listUrl: () => `${baseUrl}/knowledge/sync`,
|
|
135
|
+
rootDir: inputDir,
|
|
136
|
+
},
|
|
133
137
|
verboseFetch: this.verboseFetch.bind(this),
|
|
134
138
|
}, target, pushFlags);
|
|
135
139
|
if (flags.review) {
|
|
136
140
|
await this.openReview(profile.instance_origin, profile.access_token, flags.verbose);
|
|
137
141
|
}
|
|
138
142
|
}
|
|
143
|
+
getFrontendUrl(instanceOrigin) {
|
|
144
|
+
try {
|
|
145
|
+
const url = new URL(instanceOrigin);
|
|
146
|
+
if (url.hostname === 'localhost' || url.hostname === '127.0.0.1') {
|
|
147
|
+
url.port = '4200';
|
|
148
|
+
return url.origin;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// fall through
|
|
153
|
+
}
|
|
154
|
+
return instanceOrigin;
|
|
155
|
+
}
|
|
139
156
|
async openReview(instanceOrigin, accessToken, verbose) {
|
|
140
157
|
const response = await this.verboseFetch(`${instanceOrigin}/api:meta/sandbox/impersonate`, {
|
|
141
158
|
headers: {
|
|
@@ -158,17 +175,4 @@ Push and open sandbox review in the browser
|
|
|
158
175
|
this.log('Opening sandbox review...');
|
|
159
176
|
await open(reviewUrl);
|
|
160
177
|
}
|
|
161
|
-
getFrontendUrl(instanceOrigin) {
|
|
162
|
-
try {
|
|
163
|
-
const url = new URL(instanceOrigin);
|
|
164
|
-
if (url.hostname === 'localhost' || url.hostname === '127.0.0.1') {
|
|
165
|
-
url.port = '4200';
|
|
166
|
-
return url.origin;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
catch {
|
|
170
|
-
// fall through
|
|
171
|
-
}
|
|
172
|
-
return instanceOrigin;
|
|
173
|
-
}
|
|
174
178
|
}
|
|
@@ -4,11 +4,12 @@ import * as path from 'node:path';
|
|
|
4
4
|
import snakeCase from 'lodash.snakecase';
|
|
5
5
|
import BaseCommand from '../../../base-command.js';
|
|
6
6
|
import { buildApiGroupFolderResolver, parseDocument } from '../../../utils/document-parser.js';
|
|
7
|
+
import { fetchKnowledge, writeKnowledge } from '../../../utils/knowledge-sync.js';
|
|
7
8
|
export default class Pull extends BaseCommand {
|
|
8
9
|
static description = 'Pull a workspace multidoc from the Xano Metadata API and split into individual files';
|
|
9
10
|
static examples = [
|
|
10
11
|
`$ xano workspace pull
|
|
11
|
-
Pulled 42 documents to current directory
|
|
12
|
+
Pulled 42 documents + 5 knowledge files to current directory
|
|
12
13
|
`,
|
|
13
14
|
`$ xano workspace pull -d ./my-workspace
|
|
14
15
|
Pulled 42 documents to ./my-workspace
|
|
@@ -239,7 +240,17 @@ Pulled 58 documents
|
|
|
239
240
|
fs.writeFileSync(filePath, doc.content, 'utf8');
|
|
240
241
|
writtenCount++;
|
|
241
242
|
}
|
|
242
|
-
|
|
243
|
+
// ── Pull knowledge ────────────────────────────────────────────────────
|
|
244
|
+
const knowledgeUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/knowledge/sync`;
|
|
245
|
+
const knowledgeObjects = await fetchKnowledge(knowledgeUrl, branch, profile.access_token, this.verboseFetch.bind(this), flags.verbose);
|
|
246
|
+
let knowledgeCount = 0;
|
|
247
|
+
if (knowledgeObjects.length > 0) {
|
|
248
|
+
knowledgeCount = writeKnowledge(knowledgeObjects, outputDir);
|
|
249
|
+
}
|
|
250
|
+
const parts = [`${writtenCount} documents`];
|
|
251
|
+
if (knowledgeCount > 0)
|
|
252
|
+
parts.push(`${knowledgeCount} knowledge file${knowledgeCount === 1 ? '' : 's'}`);
|
|
253
|
+
this.log(`Pulled ${parts.join(' + ')} to ${flags.directory}`);
|
|
243
254
|
}
|
|
244
255
|
/**
|
|
245
256
|
* Sanitize a document name for use as a filename.
|
|
@@ -4,8 +4,8 @@ export default class Push extends BaseCommand {
|
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|
|
6
6
|
branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
-
directory: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
7
|
delete: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
directory: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
9
|
'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
10
|
env: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
11
|
exclude: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -53,6 +53,12 @@ Push all files except tables
|
|
|
53
53
|
`,
|
|
54
54
|
`$ xano workspace push -i "function/*" -e "**/test*"
|
|
55
55
|
Push functions but exclude test files
|
|
56
|
+
`,
|
|
57
|
+
`$ xano workspace push -i "knowledge/**"
|
|
58
|
+
Push only knowledge files (agents.md / skills / docs)
|
|
59
|
+
`,
|
|
60
|
+
`$ xano workspace push --sync --delete
|
|
61
|
+
Full sync including knowledge files; removes server objects not present locally
|
|
56
62
|
`,
|
|
57
63
|
];
|
|
58
64
|
static flags = {
|
|
@@ -62,17 +68,17 @@ Push functions but exclude test files
|
|
|
62
68
|
description: 'Branch name (optional if set in profile, defaults to live)',
|
|
63
69
|
required: false,
|
|
64
70
|
}),
|
|
71
|
+
delete: Flags.boolean({
|
|
72
|
+
default: false,
|
|
73
|
+
description: '[CRITICAL] STOP and confirm with the user before running. Delete workspace objects not included in the push (requires --sync).',
|
|
74
|
+
required: false,
|
|
75
|
+
}),
|
|
65
76
|
directory: Flags.string({
|
|
66
77
|
char: 'd',
|
|
67
78
|
default: '.',
|
|
68
79
|
description: 'Directory containing documents to push (defaults to current directory)',
|
|
69
80
|
required: false,
|
|
70
81
|
}),
|
|
71
|
-
delete: Flags.boolean({
|
|
72
|
-
default: false,
|
|
73
|
-
description: '[CRITICAL] STOP and confirm with the user before running. Delete workspace objects not included in the push (requires --sync).',
|
|
74
|
-
required: false,
|
|
75
|
-
}),
|
|
76
82
|
'dry-run': Flags.boolean({
|
|
77
83
|
default: false,
|
|
78
84
|
description: 'Show preview of changes without pushing (exit after preview)',
|
|
@@ -186,6 +192,10 @@ Push functions but exclude test files
|
|
|
186
192
|
branch,
|
|
187
193
|
command: this,
|
|
188
194
|
inputDir,
|
|
195
|
+
knowledge: {
|
|
196
|
+
listUrl: () => `${baseUrl}/knowledge/sync`,
|
|
197
|
+
rootDir: inputDir,
|
|
198
|
+
},
|
|
189
199
|
verboseFetch: this.verboseFetch.bind(this),
|
|
190
200
|
}, target, pushFlags);
|
|
191
201
|
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
export declare const KNOWLEDGE_DIR = "knowledge";
|
|
2
|
+
export type KnowledgeType = 'agents.md' | 'doc' | 'skill';
|
|
3
|
+
/** A reference file attached to a skill (knowledge_file). */
|
|
4
|
+
export interface KnowledgeFile {
|
|
5
|
+
content: string;
|
|
6
|
+
/** Path relative to the skill's `references/` folder, POSIX-separated. */
|
|
7
|
+
name: string;
|
|
8
|
+
}
|
|
9
|
+
/** A workspace-scoped knowledge object as exchanged with the API. */
|
|
10
|
+
export interface KnowledgeObject {
|
|
11
|
+
content: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
enabled?: boolean;
|
|
14
|
+
files?: KnowledgeFile[];
|
|
15
|
+
guid?: string;
|
|
16
|
+
knowledge_type: KnowledgeType;
|
|
17
|
+
mode?: string;
|
|
18
|
+
name: string;
|
|
19
|
+
scope?: string;
|
|
20
|
+
}
|
|
21
|
+
/** A locally-collected object that also remembers its primary file on disk (for GUID writeback). */
|
|
22
|
+
export interface LocalKnowledgeObject extends KnowledgeObject {
|
|
23
|
+
filePath: string;
|
|
24
|
+
}
|
|
25
|
+
export interface KnowledgePushBody {
|
|
26
|
+
branch?: string;
|
|
27
|
+
delete?: boolean;
|
|
28
|
+
dry_run?: boolean;
|
|
29
|
+
force?: boolean;
|
|
30
|
+
items: KnowledgeObject[];
|
|
31
|
+
}
|
|
32
|
+
export interface KnowledgePushResult {
|
|
33
|
+
deleted?: number;
|
|
34
|
+
guid_map?: Array<{
|
|
35
|
+
guid: string;
|
|
36
|
+
name: string;
|
|
37
|
+
}>;
|
|
38
|
+
imported?: number;
|
|
39
|
+
operations?: KnowledgeDryRunOperation[];
|
|
40
|
+
summary?: Record<string, KnowledgeDryRunSummary>;
|
|
41
|
+
}
|
|
42
|
+
export interface KnowledgeDryRunOperation {
|
|
43
|
+
action: string;
|
|
44
|
+
name: string;
|
|
45
|
+
type: string;
|
|
46
|
+
}
|
|
47
|
+
export interface KnowledgeDryRunSummary {
|
|
48
|
+
created: number;
|
|
49
|
+
deleted: number;
|
|
50
|
+
unchanged: number;
|
|
51
|
+
updated: number;
|
|
52
|
+
}
|
|
53
|
+
export interface KnowledgeDryRunResult {
|
|
54
|
+
operations: KnowledgeDryRunOperation[];
|
|
55
|
+
summary: Record<string, KnowledgeDryRunSummary>;
|
|
56
|
+
}
|
|
57
|
+
type VerboseFetch = (url: string, options: RequestInit, verbose: boolean, authToken?: string) => Promise<Response>;
|
|
58
|
+
/**
|
|
59
|
+
* Build a primary `.md` file body: YAML frontmatter (built from the object's
|
|
60
|
+
* structured fields) followed by the markdown content.
|
|
61
|
+
*/
|
|
62
|
+
export declare function buildPrimaryContent(obj: KnowledgeObject): string;
|
|
63
|
+
/**
|
|
64
|
+
* Split a primary `.md` file into its frontmatter fields and markdown body.
|
|
65
|
+
* Returns `{meta: {}, body: <raw>}` when there is no leading `---` block.
|
|
66
|
+
*/
|
|
67
|
+
export declare function parsePrimaryContent(raw: string): {
|
|
68
|
+
body: string;
|
|
69
|
+
meta: Record<string, unknown>;
|
|
70
|
+
};
|
|
71
|
+
/** Read just `guid`/`name` from frontmatter (used by GUID matching). */
|
|
72
|
+
export declare function parseFrontmatter(content: string): {
|
|
73
|
+
guid?: string;
|
|
74
|
+
name?: string;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Write knowledge objects under `<outputDir>/knowledge/`, building frontmatter
|
|
78
|
+
* and laying out skill reference files under `<skill>/references/`.
|
|
79
|
+
* Returns the number of files written (primaries + references).
|
|
80
|
+
*/
|
|
81
|
+
export declare function writeKnowledge(objects: KnowledgeObject[], outputDir: string): number;
|
|
82
|
+
/**
|
|
83
|
+
* Walk `<inputDir>/knowledge/`, reconstructing structured knowledge objects from
|
|
84
|
+
* primary `.md` files (frontmatter → fields, body → content) and attaching each
|
|
85
|
+
* skill's `references/` files. Include/exclude globs are matched against
|
|
86
|
+
* `knowledge/<path>` (relative to inputDir), consistent with multidoc filtering.
|
|
87
|
+
* Returns `[]` when the directory is absent.
|
|
88
|
+
*/
|
|
89
|
+
export declare function collectKnowledgeObjects(inputDir: string, include?: string[], exclude?: string[]): LocalKnowledgeObject[];
|
|
90
|
+
/** Strip the local-only `filePath` before sending objects to the API. */
|
|
91
|
+
export declare function toPushItems(objects: LocalKnowledgeObject[]): KnowledgeObject[];
|
|
92
|
+
/**
|
|
93
|
+
* GET workspace knowledge objects (with content). Returns `[]` on 404 (instance
|
|
94
|
+
* without the feature) or any network error, so pull degrades gracefully.
|
|
95
|
+
*/
|
|
96
|
+
export declare function fetchKnowledge(baseUrl: string, branch: string, accessToken: string, verboseFetch: VerboseFetch, verbose: boolean): Promise<KnowledgeObject[]>;
|
|
97
|
+
/**
|
|
98
|
+
* POST knowledge objects to the workspace. Throws on non-2xx. When `body.dry_run`
|
|
99
|
+
* is set the result may carry `operations`/`summary` instead of `imported`.
|
|
100
|
+
*/
|
|
101
|
+
export declare function pushKnowledge(url: string, accessToken: string, verboseFetch: VerboseFetch, verbose: boolean, body: KnowledgePushBody): Promise<KnowledgePushResult>;
|
|
102
|
+
/**
|
|
103
|
+
* Compute a dry-run-style preview by diffing local objects against server
|
|
104
|
+
* objects (match by `guid`, then `name`). Used when the server doesn't honor
|
|
105
|
+
* the `dry_run` flag.
|
|
106
|
+
*/
|
|
107
|
+
export declare function knowledgePreview(local: KnowledgeObject[], server: KnowledgeObject[], willDelete: boolean): KnowledgeDryRunResult;
|
|
108
|
+
/**
|
|
109
|
+
* Set or update `guid:` in a primary `.md` file's YAML frontmatter.
|
|
110
|
+
* Returns true if the file was modified.
|
|
111
|
+
*/
|
|
112
|
+
export declare function syncGuidToFrontmatter(filePath: string, guid: string): boolean;
|
|
113
|
+
export {};
|