@xano/cli 1.0.6-beta.17 → 1.0.6-beta.19
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 +17 -2
- package/dist/commands/ephemeral/create/index.js +3 -3
- package/dist/utils/multidoc-push.d.ts +22 -0
- package/dist/utils/multidoc-push.js +54 -0
- package/oclif.manifest.json +4162 -4162
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -229,6 +229,21 @@ xano workspace git pull -r https://github.com/owner/private-repo -t ghp_xxx
|
|
|
229
229
|
xano workspace git pull -r https://github.com/owner/repo --path subdir
|
|
230
230
|
```
|
|
231
231
|
|
|
232
|
+
**One workspace document per tree.** A push directory must contain at most one
|
|
233
|
+
`workspace/*.xs` document. The server applies the first workspace document it
|
|
234
|
+
receives to the workspace being pushed into and silently discards the rest,
|
|
235
|
+
without checking that it describes that workspace — so a second one renames the
|
|
236
|
+
destination to a foreign name while leaving its content untouched. `workspace push`
|
|
237
|
+
and `sandbox push` refuse a tree carrying more than one, naming the offending
|
|
238
|
+
files; there is no `--force` override, because only you know which workspace the
|
|
239
|
+
tree is meant to be.
|
|
240
|
+
|
|
241
|
+
Trees pick up a second one easily: `pull` names the file after the workspace
|
|
242
|
+
itself (`workspace/{name}.xs`), so pulling a different workspace into the same
|
|
243
|
+
directory *adds* a file rather than overwriting, and renaming a workspace leaves
|
|
244
|
+
the old-name file behind. Delete the stale ones, keeping the single document that
|
|
245
|
+
matches your target workspace.
|
|
246
|
+
|
|
232
247
|
### Knowledge
|
|
233
248
|
|
|
234
249
|
Knowledge items are user-authored docs and skills (e.g. `CLAUDE.md`, `AGENTS.md`, runbooks)
|
|
@@ -702,7 +717,7 @@ xano tenant cluster license set <cluster_id> --file ./kubeconfig.yaml
|
|
|
702
717
|
|
|
703
718
|
### Ephemeral Tenants
|
|
704
719
|
|
|
705
|
-
Manage ephemeral tenants — short-lived, auto-expiring tenants scoped to a workspace (default TTL 1h, max
|
|
720
|
+
Manage ephemeral tenants — short-lived, auto-expiring tenants scoped to a workspace (default TTL 1h, max 24h). Unlike a sandbox, an ephemeral tenant requires a workspace.
|
|
706
721
|
|
|
707
722
|
```bash
|
|
708
723
|
# List ephemeral tenants in the current workspace
|
|
@@ -715,7 +730,7 @@ xano ephemeral list --global
|
|
|
715
730
|
# Create an ephemeral tenant (workspace required)
|
|
716
731
|
xano ephemeral create "PR preview"
|
|
717
732
|
xano ephemeral create "Demo" --expires-hours 24 -w 5
|
|
718
|
-
xano ephemeral create "Load test" -d "
|
|
733
|
+
xano ephemeral create "Load test" -d "overnight soak" --expires-hours 24
|
|
719
734
|
|
|
720
735
|
# Get / edit / delete
|
|
721
736
|
xano ephemeral get <tenant_name>
|
|
@@ -14,7 +14,7 @@ Created ephemeral tenant: PR preview (e4f2-9ab1-...) - ID: 42
|
|
|
14
14
|
Expires: in 1 hour
|
|
15
15
|
`,
|
|
16
16
|
`$ xano ephemeral create "Demo" --expires-hours 24 -w 114`,
|
|
17
|
-
`$ xano ephemeral create "Load test" --description "
|
|
17
|
+
`$ xano ephemeral create "Load test" --description "overnight soak" --expires-hours 24 -o json`,
|
|
18
18
|
];
|
|
19
19
|
static flags = {
|
|
20
20
|
...BaseCommand.baseFlags,
|
|
@@ -25,8 +25,8 @@ Created ephemeral tenant: PR preview (e4f2-9ab1-...) - ID: 42
|
|
|
25
25
|
}),
|
|
26
26
|
'expires-hours': Flags.integer({
|
|
27
27
|
default: 1,
|
|
28
|
-
description: 'Hours until the tenant auto-expires (1-
|
|
29
|
-
max:
|
|
28
|
+
description: 'Hours until the tenant auto-expires (1-24)',
|
|
29
|
+
max: 24,
|
|
30
30
|
min: 1,
|
|
31
31
|
required: false,
|
|
32
32
|
}),
|
|
@@ -163,6 +163,28 @@ export declare function findMultiDocEntries(entries: Array<{
|
|
|
163
163
|
count: number;
|
|
164
164
|
filePath: string;
|
|
165
165
|
}>;
|
|
166
|
+
/**
|
|
167
|
+
* Return every `workspace` document in the push set, with the name it declares.
|
|
168
|
+
*
|
|
169
|
+
* A tree must carry at most ONE. The server applies the first workspace
|
|
170
|
+
* document it encounters to the workspace being pushed into and silently
|
|
171
|
+
* discards the rest, with no check that the document actually describes that
|
|
172
|
+
* workspace — so a second one renames the destination to a foreign name while
|
|
173
|
+
* leaving its content untouched, and which name wins flips with the contents of
|
|
174
|
+
* the pushed set.
|
|
175
|
+
*
|
|
176
|
+
* Trees accumulate a second one easily, because `pull` names the file after the
|
|
177
|
+
* workspace's own name (`workspace/{name}.xs`): pulling a different workspace
|
|
178
|
+
* into the same directory ADDS a file rather than overwriting, and renaming a
|
|
179
|
+
* workspace leaves the old-name file behind.
|
|
180
|
+
*/
|
|
181
|
+
export declare function findWorkspaceEntries(entries: Array<{
|
|
182
|
+
content: string;
|
|
183
|
+
filePath: string;
|
|
184
|
+
}>): Array<{
|
|
185
|
+
filePath: string;
|
|
186
|
+
name: string;
|
|
187
|
+
}>;
|
|
166
188
|
export declare function renderBadReferences(badRefs: BadReference[], log: (msg: string) => void): void;
|
|
167
189
|
export declare function renderBadIndexes(badIndexes: BadIndex[], log: (msg: string) => void): void;
|
|
168
190
|
export declare function confirm(message: string): Promise<boolean>;
|
|
@@ -249,6 +249,33 @@ export function findMultiDocEntries(entries) {
|
|
|
249
249
|
}
|
|
250
250
|
return offenders;
|
|
251
251
|
}
|
|
252
|
+
/**
|
|
253
|
+
* Return every `workspace` document in the push set, with the name it declares.
|
|
254
|
+
*
|
|
255
|
+
* A tree must carry at most ONE. The server applies the first workspace
|
|
256
|
+
* document it encounters to the workspace being pushed into and silently
|
|
257
|
+
* discards the rest, with no check that the document actually describes that
|
|
258
|
+
* workspace — so a second one renames the destination to a foreign name while
|
|
259
|
+
* leaving its content untouched, and which name wins flips with the contents of
|
|
260
|
+
* the pushed set.
|
|
261
|
+
*
|
|
262
|
+
* Trees accumulate a second one easily, because `pull` names the file after the
|
|
263
|
+
* workspace's own name (`workspace/{name}.xs`): pulling a different workspace
|
|
264
|
+
* into the same directory ADDS a file rather than overwriting, and renaming a
|
|
265
|
+
* workspace leaves the old-name file behind.
|
|
266
|
+
*/
|
|
267
|
+
export function findWorkspaceEntries(entries) {
|
|
268
|
+
const found = [];
|
|
269
|
+
for (const entry of entries) {
|
|
270
|
+
const parsed = parseDocument(entry.content);
|
|
271
|
+
// Exact match: `workspace_trigger` is a different document type that also
|
|
272
|
+
// lives under workspace/ and must not be counted here.
|
|
273
|
+
if (parsed && parsed.type === 'workspace') {
|
|
274
|
+
found.push({ filePath: entry.filePath, name: parsed.name });
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return found;
|
|
278
|
+
}
|
|
252
279
|
// ── Validation Rendering ────────────────────────────────────────────────────
|
|
253
280
|
export function renderBadReferences(badRefs, log) {
|
|
254
281
|
log(ux.colorize('yellow', ux.colorize('bold', '=== Unresolved References ===')));
|
|
@@ -637,6 +664,33 @@ export async function executePush(ctx, target, flags) {
|
|
|
637
664
|
command.error('After flattening, no .xs documents remain to push.');
|
|
638
665
|
}
|
|
639
666
|
}
|
|
667
|
+
// ── Refuse a tree carrying more than one workspace document ───────────
|
|
668
|
+
// The server applies the FIRST workspace document it sees to the workspace
|
|
669
|
+
// being pushed into and silently drops the rest, with no check that the
|
|
670
|
+
// document describes that workspace. A second one therefore renames the
|
|
671
|
+
// destination to a foreign name while leaving all of its content intact,
|
|
672
|
+
// and which name wins flips with whatever happens to be in the pushed set.
|
|
673
|
+
//
|
|
674
|
+
// Checked against the WHOLE tree rather than the partial changed-set, because
|
|
675
|
+
// the flip-flop case is exactly the one where only the foreign document is
|
|
676
|
+
// "changed" and would be the sole workspace document actually sent.
|
|
677
|
+
//
|
|
678
|
+
// No --force escape and no auto-fix: unlike the multi-doc bundle above there
|
|
679
|
+
// is no safe repair, since only the user knows which workspace this tree is
|
|
680
|
+
// meant to be. Picking one is the bug.
|
|
681
|
+
const workspaceDocs = findWorkspaceEntries(documentEntries);
|
|
682
|
+
if (workspaceDocs.length > 1) {
|
|
683
|
+
const list = workspaceDocs
|
|
684
|
+
.map((w) => ` ${relative(inputDir, w.filePath) || w.filePath} → workspace "${w.name}"`)
|
|
685
|
+
.join('\n');
|
|
686
|
+
command.error(`${inputDir} holds ${workspaceDocs.length} workspace documents:\n${list}\n\n` +
|
|
687
|
+
'A push targets one workspace, and the server applies only the first of these and ' +
|
|
688
|
+
'silently discards the rest — renaming the destination to a foreign name while leaving ' +
|
|
689
|
+
'its content untouched.\n\n' +
|
|
690
|
+
'Delete the ones that do not describe the workspace you are pushing to, keeping a single ' +
|
|
691
|
+
'workspace/*.xs. (`pull` names this file after the workspace, so pulling a different ' +
|
|
692
|
+
'workspace into this tree, or renaming one, leaves the old file behind.)');
|
|
693
|
+
}
|
|
640
694
|
multidoc = documentEntries.map((d) => d.content).join('\n---\n');
|
|
641
695
|
// ── Build document key → file path map (for GUID writeback) ─────────
|
|
642
696
|
for (const entry of documentEntries) {
|