@getxflow/cli 1.9.0 → 1.10.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/dist/commands/db.js +25 -4
- package/dist/flags.js +2 -2
- package/dist/help.js +6 -2
- package/dist/limits.js +1 -1
- package/dist/moment.js +8 -0
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/skills/xflow/SKILL.md +8 -4
package/dist/commands/db.js
CHANGED
|
@@ -321,6 +321,11 @@ async function migrate(client, projectId, migrations, dryRun, allowDestructive,
|
|
|
321
321
|
: 'The database is unchanged: the migration rolled back as a whole');
|
|
322
322
|
}
|
|
323
323
|
}
|
|
324
|
+
/** Time a branch has left, `5h` or `3d`. */
|
|
325
|
+
function left(iso) {
|
|
326
|
+
const h = Math.max(0, Math.round((Date.parse(iso) - Date.now()) / 3_600_000));
|
|
327
|
+
return h < 48 ? `${h}h left` : `${Math.round(h / 24)}d left`;
|
|
328
|
+
}
|
|
324
329
|
/** Branches of the project database: list, create, delete. */
|
|
325
330
|
async function dbBranch(args) {
|
|
326
331
|
const [sub, name] = args.words;
|
|
@@ -329,7 +334,7 @@ async function dbBranch(args) {
|
|
|
329
334
|
if (sub === undefined || sub === 'list') {
|
|
330
335
|
const data = await (0, api_1.apiJson)(client, path);
|
|
331
336
|
if ((0, args_1.flagBool)(args, 'json')) {
|
|
332
|
-
(0, ui_1.out)(JSON.stringify(data
|
|
337
|
+
(0, ui_1.out)(JSON.stringify(data, null, 2));
|
|
333
338
|
return;
|
|
334
339
|
}
|
|
335
340
|
if (data.branches.length === 0) {
|
|
@@ -337,16 +342,18 @@ async function dbBranch(args) {
|
|
|
337
342
|
(0, ui_1.note)((0, ui_1.dim)(' A copy to try things on: xflow db branch create <name> [--at 2h]'));
|
|
338
343
|
return;
|
|
339
344
|
}
|
|
340
|
-
(0, ui_1.table)(data.branches.map((b) => [b.name, b.at ? `as of ${new Date(b.at).toLocaleString()}` : 'as made', (0, ui_1.formatAge)(b.created_at)]));
|
|
345
|
+
(0, ui_1.table)(data.branches.map((b) => [b.name, b.at ? `as of ${new Date(b.at).toLocaleString()}` : 'as made', (0, ui_1.formatAge)(b.created_at), left(b.expires_at)]));
|
|
346
|
+
(0, ui_1.note)((0, ui_1.dim)(` ${data.branches.length} of ${data.plan.branches}; a branch lives up to ${data.plan.ttl_hours / 24}d on this plan (xflow db branch extend <name>)`));
|
|
341
347
|
return;
|
|
342
348
|
}
|
|
343
349
|
if (!name)
|
|
344
350
|
throw new errors_1.CliError(`xflow db branch ${sub} needs a name`, `For example: xflow db branch ${sub} fix-orders`);
|
|
345
351
|
if (sub === 'create') {
|
|
346
352
|
const at = (0, args_1.flagString)(args, 'at');
|
|
353
|
+
const ttl = (0, args_1.flagString)(args, 'ttl');
|
|
347
354
|
const data = await (0, api_1.apiJson)(client, path, {
|
|
348
355
|
method: 'POST',
|
|
349
|
-
body: { name, at: at ? (0, moment_1.parseMoment)(at) : undefined },
|
|
356
|
+
body: { name, at: at ? (0, moment_1.parseMoment)(at) : undefined, ttl_hours: ttl ? (0, moment_1.parseTtl)(ttl) : undefined },
|
|
350
357
|
timeoutMs: 180_000,
|
|
351
358
|
});
|
|
352
359
|
if ((0, args_1.flagBool)(args, 'json')) {
|
|
@@ -356,6 +363,20 @@ async function dbBranch(args) {
|
|
|
356
363
|
(0, ui_1.ok)(`Branch ${name}: the project database ${data.branch.at ? `as of ${new Date(data.branch.at).toLocaleString()}` : 'as it is now'}`);
|
|
357
364
|
(0, ui_1.note)((0, ui_1.dim)(` Work in it: xflow db query --branch ${name} '...', db exec and db migrate take --branch too`));
|
|
358
365
|
(0, ui_1.note)((0, ui_1.dim)(` Rows back into the project database: xflow db restore --from ${name} --table <table>`));
|
|
366
|
+
(0, ui_1.note)((0, ui_1.dim)(` Deleted on its own in ${left(data.branch.expires_at)}; more time: xflow db branch extend ${name}`));
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (sub === 'extend') {
|
|
370
|
+
const ttl = (0, args_1.flagString)(args, 'ttl');
|
|
371
|
+
const data = await (0, api_1.apiJson)(client, `${path}/${encodeURIComponent(name)}`, {
|
|
372
|
+
method: 'PATCH',
|
|
373
|
+
body: { ttl_hours: ttl ? (0, moment_1.parseTtl)(ttl) : undefined },
|
|
374
|
+
});
|
|
375
|
+
if ((0, args_1.flagBool)(args, 'json')) {
|
|
376
|
+
(0, ui_1.out)(JSON.stringify(data.branch, null, 2));
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
(0, ui_1.ok)(`Branch ${name}: ${left(data.branch.expires_at)}, until ${new Date(data.branch.expires_at).toLocaleString()}`);
|
|
359
380
|
return;
|
|
360
381
|
}
|
|
361
382
|
if (sub === 'delete' || sub === 'rm') {
|
|
@@ -363,7 +384,7 @@ async function dbBranch(args) {
|
|
|
363
384
|
(0, ui_1.ok)(`Branch ${name} deleted; the project database is unchanged`);
|
|
364
385
|
return;
|
|
365
386
|
}
|
|
366
|
-
throw new errors_1.CliError(`Unknown command: db branch ${sub}`, 'Available: list, create and delete');
|
|
387
|
+
throw new errors_1.CliError(`Unknown command: db branch ${sub}`, 'Available: list, create, extend and delete');
|
|
367
388
|
}
|
|
368
389
|
/** Rows of a table from a branch back into the project database, by primary key. */
|
|
369
390
|
async function dbRestore(args) {
|
package/dist/flags.js
CHANGED
|
@@ -82,8 +82,8 @@ exports.KNOWN = {
|
|
|
82
82
|
'db exec': ['allow-destructive', 'json', 'project', 'branch'],
|
|
83
83
|
'db logs': ['limit', 'project', 'cursor'],
|
|
84
84
|
'db migrate': ['dry-run', 'allow-destructive', 'branch'],
|
|
85
|
-
'db branch': ['at', 'json', 'project'],
|
|
86
|
-
'db branches': ['at', 'json', 'project'],
|
|
85
|
+
'db branch': ['at', 'json', 'project', 'ttl'],
|
|
86
|
+
'db branches': ['at', 'json', 'project', 'ttl'],
|
|
87
87
|
'db restore': ['from', 'table', 'where', 'overwrite', 'json', 'project'],
|
|
88
88
|
storage: ['json', 'project'],
|
|
89
89
|
'storage ls': ['json', 'project'],
|
package/dist/help.js
CHANGED
|
@@ -296,7 +296,9 @@ will work.`,
|
|
|
296
296
|
1 on the Free plan (--limit, --cursor for older)
|
|
297
297
|
xflow db branch [list] branches of the database (--json)
|
|
298
298
|
xflow db branch create <name> a branch as the database is now; --at 2h, --at 14:05 or
|
|
299
|
-
--at 2026-10-30T14:05+03:00 for a moment of its history
|
|
299
|
+
--at 2026-10-30T14:05+03:00 for a moment of its history;
|
|
300
|
+
--ttl 12h or --ttl 3d to delete it sooner than the plan does
|
|
301
|
+
xflow db branch extend <name> more time from now: the plan's longest, or --ttl
|
|
300
302
|
xflow db branch delete <name> the branch goes, the database is unchanged
|
|
301
303
|
xflow db restore --from <branch> --table <table> [--where '...'] [--overwrite]
|
|
302
304
|
rows of a table from a branch back into the database
|
|
@@ -331,7 +333,9 @@ ${(0, ui_1.bold)('query')}, ${(0, ui_1.bold)('exec')} and ${(0, ui_1.bold)('migr
|
|
|
331
333
|
reaches the project database. A branch sleeps after a quiet minute, so its first query takes a
|
|
332
334
|
second or two; it has no functions and no history of its own, and a branch of a branch is not
|
|
333
335
|
made. Branches per project: 2 on Free, 5 on Start, 15 on Business; delete one you no longer need.
|
|
334
|
-
|
|
336
|
+
A branch is deleted on its own when its lifetime ends: at most 1 day on Free, 7 days on Start,
|
|
337
|
+
30 days on Business, and ${(0, ui_1.bold)('extend')} gives it more from now within that. The history reaches
|
|
338
|
+
back 6 hours on Free, 3 days on Start, 7 days on Business.
|
|
335
339
|
|
|
336
340
|
${(0, ui_1.bold)('restore')} puts rows back after a mistake without rolling the whole database back: make a
|
|
337
341
|
branch as of a moment before it, check the rows there with ${(0, ui_1.bold)('query --branch')}, then restore
|
package/dist/limits.js
CHANGED
|
@@ -54,7 +54,7 @@ const QUOTA_LABELS = {
|
|
|
54
54
|
people: 'People',
|
|
55
55
|
space_mb: 'Space (databases and files together)',
|
|
56
56
|
function_minutes: 'Function minutes',
|
|
57
|
-
db_hours: 'Database hours',
|
|
57
|
+
db_hours: 'Database GB-hours',
|
|
58
58
|
builds: 'Builds this month',
|
|
59
59
|
};
|
|
60
60
|
/** Decimal units, 1000 MB per GB: matches how volumes are sold. */
|
package/dist/moment.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseMoment = parseMoment;
|
|
4
|
+
exports.parseTtl = parseTtl;
|
|
4
5
|
const errors_1 = require("./errors");
|
|
5
6
|
const UNIT_MS = { m: 60_000, h: 3_600_000, d: 86_400_000 };
|
|
6
7
|
/** `30m`/`2h`/`1d` ago, `14:05` local (yesterday if still ahead), or an ISO date. */
|
|
@@ -22,3 +23,10 @@ function parseMoment(text, now = new Date()) {
|
|
|
22
23
|
}
|
|
23
24
|
return at.toISOString();
|
|
24
25
|
}
|
|
26
|
+
/** A branch lifetime in whole hours: `12h` or `3d`. */
|
|
27
|
+
function parseTtl(text) {
|
|
28
|
+
const m = /^(\d{1,4})([hd])$/.exec(text);
|
|
29
|
+
if (!m || Number(m[1]) < 1)
|
|
30
|
+
throw new errors_1.CliError(`--ttl ${text} is not a lifetime`, 'For example: --ttl 12h or --ttl 3d');
|
|
31
|
+
return Number(m[1]) * (m[2] === 'd' ? 24 : 1);
|
|
32
|
+
}
|
package/dist/version.js
CHANGED
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DEFAULT_API_URL = exports.CLI_VERSION = void 0;
|
|
4
4
|
/** Keep in sync with cli/package.json. */
|
|
5
|
-
exports.CLI_VERSION = '1.
|
|
5
|
+
exports.CLI_VERSION = '1.10.0';
|
|
6
6
|
/** Overridden by XFLOW_API_URL or the `api` field in xflow.json. */
|
|
7
7
|
exports.DEFAULT_API_URL = 'https://app.getxflow.com';
|
package/package.json
CHANGED
package/skills/xflow/SKILL.md
CHANGED
|
@@ -28,7 +28,7 @@ instead of guessing; give the person https://docs.getxflow.com when they ask how
|
|
|
28
28
|
|
|
29
29
|
## Keeping these instructions current
|
|
30
30
|
|
|
31
|
-
These instructions ship with xflow CLI 1.
|
|
31
|
+
These instructions ship with xflow CLI 1.10.0, so the copy you are reading can be older than
|
|
32
32
|
the CLI answering your commands.
|
|
33
33
|
|
|
34
34
|
When the CLI says this file does not match it, run `xflow skills --refresh` and then **read
|
|
@@ -119,7 +119,8 @@ schedules may run, and which access levels an app may have. The number of functi
|
|
|
119
119
|
and projects is not capped; at most 15 new projects a day (UTC) per organization, deleted ones count. Most are finite; a plan
|
|
120
120
|
may leave one uncapped, and then `whoami` prints `no limit` instead of a ceiling.
|
|
121
121
|
A paid plan comes in packages (Start S/M/L, Business M/L/XL): the package sets the space,
|
|
122
|
-
database hours
|
|
122
|
+
database GB-hours (1 GB of memory a database actually uses for an hour awake; a sleeping
|
|
123
|
+
one uses none) and function minutes, the plan sets the rest. More space is a bigger package
|
|
123
124
|
chosen by a person in «Тариф» (Plan); there is no separate purchase of space.
|
|
124
125
|
`xflow whoami` prints every one of them next to what is used, and the rights of your own key
|
|
125
126
|
with them: destroying data (a migration or `db exec`), deleting files from storage, moving a database and
|
|
@@ -558,8 +559,11 @@ project database. After a data mistake, do not roll anything back: branch as of
|
|
|
558
559
|
it, check the rows with `db query --branch`, then `xflow db restore --from <branch> --table <t>
|
|
559
560
|
[--where '...']` puts the missing rows back by primary key and keeps all newer data
|
|
560
561
|
(`--overwrite` replaces rows still there and needs the destroy right). Branches per project: 2,
|
|
561
|
-
5, 15 by plan; delete them when done (`xflow db branch delete <name>`). A branch
|
|
562
|
-
|
|
562
|
+
5, 15 by plan; delete them when done (`xflow db branch delete <name>`). A branch is deleted on its
|
|
563
|
+
own when its lifetime ends: at most 1, 7 or 30 days by plan, shorter with `--ttl 12h`, more from
|
|
564
|
+
now with `xflow db branch extend <name>`. A branch sleeps after a quiet minute, has no functions,
|
|
565
|
+
and a branch of a branch is not made. People see the branches in Databases, open them there and
|
|
566
|
+
read their data.
|
|
563
567
|
|
|
564
568
|
Anything that destroys data
|
|
565
569
|
(`DROP TABLE`, `DROP COLUMN`, `TRUNCATE`, `DELETE`) needs both the `--allow-destructive` flag and
|