@getxflow/cli 0.7.0 → 0.8.1
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 +1 -1
- package/dist/bin.js +0 -3
- package/dist/commands/deploy.js +14 -1
- package/dist/commands/sources.js +3 -9
- package/dist/help.js +14 -17
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/skills/xflow/SKILL.md +15 -10
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ else's code belongs next to it.
|
|
|
18
18
|
|---|---|
|
|
19
19
|
| `login` / `logout` / `whoami` | sign in through the browser, sign out, whose key this is and what it can do |
|
|
20
20
|
| `init` / `link` | new project, link a folder to an existing one |
|
|
21
|
-
| `status` / `
|
|
21
|
+
| `status` / `pull` | state of the sources on the server and fetching them back |
|
|
22
22
|
| `deploy` / `publish` / `rollback` / `deployments` | build, publish, roll back, version history |
|
|
23
23
|
| `db status` / `db migrate` | migrations from `migrations/*.sql` with a gate on destructive ones |
|
|
24
24
|
| `db schema` / `db query` | tables and columns, reading data in a read-only transaction |
|
package/dist/bin.js
CHANGED
package/dist/commands/deploy.js
CHANGED
|
@@ -100,6 +100,18 @@ async function startBuild(client, projectId, revision, allowRemovals) {
|
|
|
100
100
|
});
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Ask whether the plan allows a build before the sources go up.
|
|
105
|
+
*
|
|
106
|
+
* The gate sits on the build itself, so a refusal used to arrive after the archive
|
|
107
|
+
* had already become a revision, leaving the project looking like a forgotten
|
|
108
|
+
* deploy. Same gate, same wording, one round trip earlier. A platform older than
|
|
109
|
+
* this CLI answers the plain build state instead, and the deploy carries on as
|
|
110
|
+
* before: the check is an improvement, not a requirement.
|
|
111
|
+
*/
|
|
112
|
+
async function assertBuildAllowed(client, projectId) {
|
|
113
|
+
await (0, api_1.apiJson)(client, `/api/v1/projects/${projectId}/builds?check=plan`);
|
|
114
|
+
}
|
|
103
115
|
async function deploy(args) {
|
|
104
116
|
const { root, config } = (0, config_1.requireProject)();
|
|
105
117
|
const client = await (0, session_1.connectProject)(root, config);
|
|
@@ -107,12 +119,13 @@ async function deploy(args) {
|
|
|
107
119
|
if ((0, args_1.flagBool)(args, 'no-push')) {
|
|
108
120
|
const server = await (0, sources_1.latestRevision)(client, config.projectId);
|
|
109
121
|
if (!server) {
|
|
110
|
-
throw new errors_1.CliError('The server holds no sources', '
|
|
122
|
+
throw new errors_1.CliError('The server holds no sources', 'Repeat without --no-push, so that the sources go up first');
|
|
111
123
|
}
|
|
112
124
|
revision = server.revision;
|
|
113
125
|
(0, ui_1.warn)(`Sources not sent (--no-push), the build runs from revision ${revision}`);
|
|
114
126
|
}
|
|
115
127
|
else {
|
|
128
|
+
await assertBuildAllowed(client, config.projectId);
|
|
116
129
|
revision = (await (0, sources_1.pushSources)(root, config, client, { force: (0, args_1.flagBool)(args, 'force') })).revision;
|
|
117
130
|
}
|
|
118
131
|
(0, ui_1.step)(`Building on the platform from revision ${revision}`);
|
package/dist/commands/sources.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.latestRevision = latestRevision;
|
|
4
4
|
exports.pushSources = pushSources;
|
|
5
|
-
exports.push = push;
|
|
6
5
|
exports.pull = pull;
|
|
7
6
|
exports.status = status;
|
|
8
7
|
const node_fs_1 = require("node:fs");
|
|
@@ -69,7 +68,7 @@ async function confirmForce(client, projectId, server, local) {
|
|
|
69
68
|
if (!confirmed)
|
|
70
69
|
throw new errors_1.CliError('Cancelled');
|
|
71
70
|
}
|
|
72
|
-
/**
|
|
71
|
+
/** The first step of deploy: sources go up as a revision before the build. */
|
|
73
72
|
async function pushSources(root, config, client, options) {
|
|
74
73
|
const tree = prepareTree(root, config);
|
|
75
74
|
const state = (0, config_1.readState)(root);
|
|
@@ -111,11 +110,6 @@ async function pushSources(root, config, client, options) {
|
|
|
111
110
|
}
|
|
112
111
|
return { revision: result.revision, status: result.status };
|
|
113
112
|
}
|
|
114
|
-
async function push(args) {
|
|
115
|
-
const { root, config } = (0, config_1.requireProject)();
|
|
116
|
-
const client = await (0, session_1.connectProject)(root, config);
|
|
117
|
-
await pushSources(root, config, client, { force: (0, args_1.flagBool)(args, 'force') });
|
|
118
|
-
}
|
|
119
113
|
function hasContent(dir) {
|
|
120
114
|
if (!(0, node_fs_1.existsSync)(dir))
|
|
121
115
|
return false;
|
|
@@ -169,7 +163,7 @@ async function status() {
|
|
|
169
163
|
(0, ui_1.out)(`Local: ${tree.files.length} files, ${(0, ui_1.formatBytes)(tree.archive.length)} archived`);
|
|
170
164
|
if (!server) {
|
|
171
165
|
(0, ui_1.out)('On the server: no sources yet');
|
|
172
|
-
(0, ui_1.note)((0, ui_1.dim)(' Send them: xflow
|
|
166
|
+
(0, ui_1.note)((0, ui_1.dim)(' Send and build them: xflow deploy'));
|
|
173
167
|
}
|
|
174
168
|
else {
|
|
175
169
|
(0, ui_1.out)(`On the server: revision ${server.revision}, ${server.file_count} files, ${(0, ui_1.formatAge)(server.created_at)}`);
|
|
@@ -182,7 +176,7 @@ async function status() {
|
|
|
182
176
|
}
|
|
183
177
|
else {
|
|
184
178
|
(0, ui_1.out)(`State: ${(0, ui_1.bold)('local changes')}`);
|
|
185
|
-
(0, ui_1.note)((0, ui_1.dim)(' Send them: xflow
|
|
179
|
+
(0, ui_1.note)((0, ui_1.dim)(' Send and build them: xflow deploy'));
|
|
186
180
|
}
|
|
187
181
|
}
|
|
188
182
|
(0, ui_1.out)('');
|
package/dist/help.js
CHANGED
|
@@ -19,9 +19,8 @@ ${(0, ui_1.bold)('Getting started')}
|
|
|
19
19
|
xflow skills pick the agents that get the platform instructions
|
|
20
20
|
xflow mcp install give the agent platform access without a terminal
|
|
21
21
|
|
|
22
|
-
${(0, ui_1.bold)('
|
|
22
|
+
${(0, ui_1.bold)('Sources')}
|
|
23
23
|
xflow status what is on the server and how the local copy differs
|
|
24
|
-
xflow push [--force] send the sources as a new revision
|
|
25
24
|
xflow pull [--into dir] [--revision N]
|
|
26
25
|
fetch the sources (the latest revision by default)
|
|
27
26
|
|
|
@@ -81,7 +80,7 @@ Which key a command then uses, in order:
|
|
|
81
80
|
|
|
82
81
|
1. XFLOW_TOKEN, when set (and only for the default platform address)
|
|
83
82
|
2. the organization this folder is bound to (${(0, ui_1.bold)('.xflow/state.json')}, written by
|
|
84
|
-
init, link and the first successful
|
|
83
|
+
init, link and the first successful deploy or pull)
|
|
85
84
|
3. the active organization
|
|
86
85
|
|
|
87
86
|
xflow org the stored organizations, the active one marked
|
|
@@ -322,20 +321,6 @@ Plus a few pointer lines in ${(0, ui_1.bold)('AGENTS.md')}. A skill is picked up
|
|
|
322
321
|
description matches the task, and "add a customers table" will not trigger it. AGENTS.md
|
|
323
322
|
is always read by the agent, which is why the pointer is appended there, at the end of
|
|
324
323
|
the file and once. ${(0, ui_1.bold)('CLAUDE.md')} is left alone: Claude Code reads .claude/skills anyway.`,
|
|
325
|
-
push: `${(0, ui_1.bold)('xflow push')}: send the sources
|
|
326
|
-
|
|
327
|
-
The whole working copy goes up at once, as one revision. What is not sent:
|
|
328
|
-
node_modules, .git, dist, build, .next, any .env, plus everything listed in
|
|
329
|
-
.xflowignore and in the ignore field of xflow.json.
|
|
330
|
-
|
|
331
|
-
If the server holds a revision newer than the one you worked from, the push is
|
|
332
|
-
rejected. That means somebody pushed before you: fetch their changes alongside
|
|
333
|
-
(${(0, ui_1.bold)('xflow pull --into ./server-copy')}), merge them in git on your side, and retry.
|
|
334
|
-
|
|
335
|
-
--force overwrites the server revision. Before that the CLI shows whose work you are
|
|
336
|
-
about to destroy and asks for confirmation by typing the project name. In a
|
|
337
|
-
non-interactive run (CI, an agent) there is no way to confirm: a version conflict has
|
|
338
|
-
to fail the build rather than silently destroy somebody else's work.`,
|
|
339
324
|
pull: `${(0, ui_1.bold)('xflow pull')}: fetch the sources
|
|
340
325
|
|
|
341
326
|
By default it fetches the latest revision into the project folder and refuses to write
|
|
@@ -354,6 +339,18 @@ by default).
|
|
|
354
339
|
--force allow overwriting the server revision while sending
|
|
355
340
|
--allow-removals agree in advance to remove the functions gone from the sources
|
|
356
341
|
|
|
342
|
+
The whole working copy goes up at once, as one revision. What is not sent: node_modules,
|
|
343
|
+
.git, dist, build, .next, any .env, plus everything listed in .xflowignore and in the
|
|
344
|
+
ignore field of xflow.json.
|
|
345
|
+
|
|
346
|
+
If the server holds a revision newer than the one you worked from, the sources are
|
|
347
|
+
rejected and the build does not start. That means somebody deployed before you: fetch
|
|
348
|
+
their changes alongside (${(0, ui_1.bold)('xflow pull --into ./server-copy')}), merge them in git on
|
|
349
|
+
your side, and retry. ${(0, ui_1.bold)('--force')} overwrites the server revision, and before that the
|
|
350
|
+
CLI shows whose work you are about to destroy and asks for confirmation by typing the
|
|
351
|
+
project name. In a non-interactive run (CI, an agent) there is no way to confirm: a
|
|
352
|
+
version conflict has to fail the build rather than silently destroy somebody else's work.
|
|
353
|
+
|
|
357
354
|
The platform builds, in a clean sandbox on one Node version for everybody, so "it
|
|
358
355
|
worked on my machine" no longer depends on your machine. Before the build the project
|
|
359
356
|
is checked against the template: mismatches are printed as a list and the build does
|
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 = '0.
|
|
5
|
+
exports.CLI_VERSION = '0.8.1';
|
|
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
|
@@ -361,12 +361,17 @@ const link = await fetch(`${process.env.XFLOW_API_URL}/api/storage/project/uploa
|
|
|
361
361
|
```
|
|
362
362
|
|
|
363
363
|
`confirm` takes the same fields plus the returned `s3Key` and answers with the file and its
|
|
364
|
-
|
|
364
|
+
address; `delete` takes that same `url`. Never pipe the bytes through the function itself.
|
|
365
365
|
|
|
366
366
|
There is no endpoint that lists the files back, so the address that `confirm` returns is the
|
|
367
367
|
only copy you get: write it into a table of your own in the same call, and the application
|
|
368
368
|
reads its files from there.
|
|
369
369
|
|
|
370
|
+
That address never expires and is safe to store, but it is not a public link. It opens only
|
|
371
|
+
for a visitor who is signed in and has access to this project, the same rule that guards the
|
|
372
|
+
application itself, so it works on your pages and does nothing in an email or on a page
|
|
373
|
+
anyone can open.
|
|
374
|
+
|
|
370
375
|
Four things bite an upload that otherwise looks right, and none of them is obvious from the
|
|
371
376
|
answers you get:
|
|
372
377
|
|
|
@@ -397,19 +402,19 @@ key that reached a visitor lets them delete every file of the project.
|
|
|
397
402
|
## Syncing code
|
|
398
403
|
|
|
399
404
|
`xflow status` shows how the local copy differs from the server revision.
|
|
400
|
-
`xflow
|
|
405
|
+
`xflow deploy` sends sources, `xflow pull` fetches them.
|
|
401
406
|
|
|
402
|
-
If a
|
|
403
|
-
Fetch their work next to yours (`xflow pull --into ./server-copy`),
|
|
404
|
-
then
|
|
405
|
-
error.
|
|
407
|
+
If a deploy is rejected before the build starts, the server revision is newer, meaning
|
|
408
|
+
someone deployed first. Fetch their work next to yours (`xflow pull --into ./server-copy`),
|
|
409
|
+
merge it locally, then deploy again. `--force` destroys their work: a last resort, not a
|
|
410
|
+
way around the error.
|
|
406
411
|
|
|
407
412
|
## Organizations and keys
|
|
408
413
|
|
|
409
414
|
A key belongs to one organization, and `xflow login` stores it next to the ones already
|
|
410
415
|
stored instead of replacing them. Which key a command uses, in order: `XFLOW_TOKEN` when
|
|
411
416
|
set (default platform address only), then the organization the project folder is bound
|
|
412
|
-
to (`.xflow/state.json`, written by init, link and the first successful
|
|
417
|
+
to (`.xflow/state.json`, written by init, link and the first successful deploy or pull),
|
|
413
418
|
then the active organization. `xflow org` lists the stored organizations with the active
|
|
414
419
|
one marked, `xflow org switch <name|id>` makes another one active without a browser, and
|
|
415
420
|
`xflow whoami` names the organization behind the current key.
|
|
@@ -430,8 +435,8 @@ read-only queries, migrations, function logs and invocations, schedules, environ
|
|
|
430
435
|
variables, versions, publish and rollback. They answer with aggregates and say explicitly
|
|
431
436
|
when a result is truncated, which parsing terminal output does not.
|
|
432
437
|
|
|
433
|
-
Anything that depends on the working copy stays in the CLI: sending sources
|
|
434
|
-
|
|
438
|
+
Anything that depends on the working copy stays in the CLI: sending sources, building and
|
|
439
|
+
shipping the functions (`xflow deploy`), creating a project (`xflow init`). The
|
|
435
440
|
tools cannot see the folder you are working in, so a build started from there would release
|
|
436
441
|
whatever revision the server happens to hold, not what you have on disk. Pulling a repository
|
|
437
442
|
through tool calls also burns the user's tokens for nothing.
|
|
@@ -440,7 +445,7 @@ through tool calls also burns the user's tokens for nothing.
|
|
|
440
445
|
|
|
441
446
|
- Edit `xflow.json` by hand: the CLI writes it.
|
|
442
447
|
- Commit `.env`: it holds the project token.
|
|
443
|
-
-
|
|
448
|
+
- Deploy with `--force` without checking `xflow status` first.
|
|
444
449
|
- Invent platform commands: what is not in `xflow help` does not exist.
|
|
445
450
|
|
|
446
451
|
## App design
|