@getxflow/cli 0.34.2 → 1.0.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/api.js +21 -6
- package/dist/args.js +1 -1
- package/dist/bin.js +15 -4
- package/dist/commands/connections.js +4 -4
- package/dist/commands/db.js +26 -63
- package/dist/commands/deploy.js +170 -216
- package/dist/commands/dev.js +15 -29
- package/dist/commands/env.js +6 -3
- package/dist/commands/functions.js +27 -119
- package/dist/commands/logs.js +19 -15
- package/dist/commands/pages.js +26 -0
- package/dist/commands/projects.js +134 -190
- package/dist/commands/sources.js +218 -120
- package/dist/commands/storage.js +2 -2
- package/dist/config.js +82 -26
- package/dist/flags.js +21 -12
- package/dist/help.js +220 -211
- package/dist/limits.js +6 -6
- package/dist/session.js +167 -52
- package/dist/template.js +17 -113
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/skills/xflow/SKILL.md +504 -546
package/dist/help.js
CHANGED
|
@@ -12,7 +12,10 @@ const version_1 = require("./version");
|
|
|
12
12
|
const ALIASES = {
|
|
13
13
|
invoke: 'functions',
|
|
14
14
|
publish: 'deploy',
|
|
15
|
-
|
|
15
|
+
rollback: 'deploy',
|
|
16
|
+
deployments: 'deploy',
|
|
17
|
+
apps: 'projects',
|
|
18
|
+
status: 'projects',
|
|
16
19
|
};
|
|
17
20
|
/** Help text. For agents this is the reference documentation. */
|
|
18
21
|
/** Whether `xflow help <topic>` has a page: a hint that sends nowhere is a second dead end. */
|
|
@@ -35,19 +38,22 @@ Every command with a line of its own: xflow help`);
|
|
|
35
38
|
}
|
|
36
39
|
(0, ui_1.out)(`${(0, ui_1.bold)('xflow')} ${(0, ui_1.dim)(version_1.CLI_VERSION)} XFlow application hosting
|
|
37
40
|
|
|
41
|
+
A project is a box: one database, cloud functions, secrets. Its apps live in apps/<folder>,
|
|
42
|
+
each with its own versions and address. Commands follow the folder you stand in.
|
|
43
|
+
|
|
38
44
|
${(0, ui_1.bold)('Getting started')}
|
|
39
45
|
xflow login sign in through the browser
|
|
40
|
-
xflow init [dir] new project from the platform template
|
|
41
|
-
xflow
|
|
42
|
-
xflow link <id> link this folder to a project, an empty one
|
|
43
|
-
xflow dev
|
|
46
|
+
xflow init [dir] new project with its first app from the platform template
|
|
47
|
+
xflow apps add <folder> another app of this project, in apps/<folder>
|
|
48
|
+
xflow link <id> link this folder to a project, an empty one gets the whole project
|
|
49
|
+
xflow dev in an app folder: run it on 127.0.0.1:3000, functions included
|
|
44
50
|
xflow skills pick the agents that get the platform instructions
|
|
45
51
|
xflow mcp install give the agent platform access without a terminal
|
|
46
52
|
|
|
47
53
|
${(0, ui_1.bold)('Sources')}
|
|
48
|
-
xflow status what is on the server and how the local
|
|
49
|
-
xflow pull [--into dir] [--revision N]
|
|
50
|
-
fetch the sources
|
|
54
|
+
xflow status what is on the server and how the local copies differ
|
|
55
|
+
xflow pull [functions|apps/<folder>] [--into dir] [--revision N]
|
|
56
|
+
fetch the sources of the root or of one app
|
|
51
57
|
|
|
52
58
|
${(0, ui_1.bold)('Files')}
|
|
53
59
|
xflow storage ls [folder] what the file storage of the project holds
|
|
@@ -58,13 +64,16 @@ ${(0, ui_1.bold)('Files')}
|
|
|
58
64
|
delete a folder with everything in it
|
|
59
65
|
|
|
60
66
|
${(0, ui_1.bold)('Releasing')}
|
|
61
|
-
xflow deploy -m "what changed"
|
|
62
|
-
|
|
63
|
-
xflow
|
|
64
|
-
|
|
67
|
+
xflow deploy functions -m "what changed"
|
|
68
|
+
send the project root, ship the functions and their callers
|
|
69
|
+
xflow deploy apps/<folder> -m "what changed"
|
|
70
|
+
send one app and build it (a bare deploy inside its folder)
|
|
71
|
+
xflow publish [build] point the app address at the newest build, or a named one
|
|
72
|
+
xflow rollback <build> the same with an older build
|
|
73
|
+
xflow deployments [functions] builds of the app, or deploys of the functions
|
|
65
74
|
|
|
66
75
|
${(0, ui_1.bold)('Functions')}
|
|
67
|
-
xflow functions list what is deployed,
|
|
76
|
+
xflow functions list what is deployed, its callers, open or not, last call
|
|
68
77
|
xflow functions invoke <name> [--data '{"a":1}']
|
|
69
78
|
call a function and print the answer
|
|
70
79
|
xflow functions logs [name] function crashes: stack and console output
|
|
@@ -83,7 +92,7 @@ ${(0, ui_1.bold)('Functions')}
|
|
|
83
92
|
|
|
84
93
|
${(0, ui_1.bold)('Database')}
|
|
85
94
|
xflow db status which migrations are applied and which are waiting
|
|
86
|
-
xflow db list databases of the
|
|
95
|
+
xflow db list databases of the projects and the servers of the organization
|
|
87
96
|
xflow db schema [table] tables of the project, or the columns of one
|
|
88
97
|
xflow db query 'select ...' read data, in a read-only transaction
|
|
89
98
|
xflow db migrate [--dry-run] [--allow-destructive]
|
|
@@ -99,11 +108,13 @@ ${(0, ui_1.bold)('Pages')}
|
|
|
99
108
|
upload the site: live at once, there is no publish step
|
|
100
109
|
xflow pages status <page> the page and its versions
|
|
101
110
|
xflow pages rollback <page> <N> put an earlier version back on the address
|
|
111
|
+
xflow pages rm <page> --yes delete the page for good: the address empties
|
|
102
112
|
|
|
103
113
|
${(0, ui_1.bold)('Reference')}
|
|
104
|
-
xflow logs [--limit N] browser errors
|
|
114
|
+
xflow logs [--limit N] browser errors of the published apps
|
|
105
115
|
xflow projects list projects of the organization
|
|
106
|
-
xflow projects get [id] project card
|
|
116
|
+
xflow projects get [id] project card: apps, database, functions
|
|
117
|
+
xflow apps the apps of this project and their folders
|
|
107
118
|
xflow org organizations with a stored key, the active one marked
|
|
108
119
|
xflow org members who is in the organization, and with which role
|
|
109
120
|
xflow org switch <name|id> make another organization the active one
|
|
@@ -115,6 +126,7 @@ ${(0, ui_1.bold)('Reference')}
|
|
|
115
126
|
${(0, ui_1.bold)('Outside a project folder')}
|
|
116
127
|
--project <id> name the project by id, for every command that can
|
|
117
128
|
answer without the local files (xflow help project)
|
|
129
|
+
--app <id> name an app by id: deploy --revision, publish, deployments, logs
|
|
118
130
|
|
|
119
131
|
${(0, ui_1.bold)('Environment')}
|
|
120
132
|
XFLOW_TOKEN access key (for CI, instead of xflow login)
|
|
@@ -168,19 +180,23 @@ Which commands: ${(0, ui_1.bold)('db status')}, ${(0, ui_1.bold)('db query')}, $
|
|
|
168
180
|
${(0, ui_1.bold)('functions invoke')}, ${(0, ui_1.bold)('functions logs')}, ${(0, ui_1.bold)('logs')}, ${(0, ui_1.bold)('env')}, ${(0, ui_1.bold)('env rm')},
|
|
169
181
|
${(0, ui_1.bold)('connections')}, ${(0, ui_1.bold)('connections link')}, ${(0, ui_1.bold)('connections unlink')}, ${(0, ui_1.bold)('schedules')},
|
|
170
182
|
${(0, ui_1.bold)('schedules set')}, ${(0, ui_1.bold)('schedules rm')}, ${(0, ui_1.bold)('storage ls')}, ${(0, ui_1.bold)('storage push')},
|
|
171
|
-
${(0, ui_1.bold)('storage rm')}, ${(0, ui_1.bold)('
|
|
183
|
+
${(0, ui_1.bold)('storage rm')}, ${(0, ui_1.bold)('apps')}, ${(0, ui_1.bold)('deployments')} (the functions deploys) and ${(0, ui_1.bold)('deploy')}. Three of
|
|
172
184
|
them answer with less under the flag: ${(0, ui_1.bold)('db status')} gives the applied history alone,
|
|
173
185
|
because comparing it against the local ${(0, ui_1.bold)('migrations/')} directory needs the folder,
|
|
174
186
|
${(0, ui_1.bold)('connections unlink')} removes the link without naming the functions that read
|
|
175
187
|
its variables, for the same reason, and ${(0, ui_1.bold)('deploy')} takes the flag only together with
|
|
176
|
-
${(0, ui_1.bold)('--revision')}: that
|
|
177
|
-
nothing to send and no folder to send it from.
|
|
178
|
-
|
|
188
|
+
${(0, ui_1.bold)('--revision')}: that deploy runs a revision of the functions the platform already holds,
|
|
189
|
+
so there is nothing to send and no folder to send it from.
|
|
190
|
+
|
|
191
|
+
${(0, ui_1.bold)('--app <id>')} does the same for one app: ${(0, ui_1.bold)('deploy --app <id> --revision N')}, ${(0, ui_1.bold)('deployments')},
|
|
192
|
+
${(0, ui_1.bold)('logs')}, ${(0, ui_1.bold)('publish')} and ${(0, ui_1.bold)('rollback')}. Its project comes from the platform, so the two
|
|
193
|
+
flags never go together.
|
|
179
194
|
|
|
180
195
|
The rest cannot answer without the working copy and have nothing to do with an id
|
|
181
|
-
instead: ${(0, ui_1.bold)('pull')}, ${(0, ui_1.bold)('status')}, ${(0, ui_1.bold)('dev')}, ${(0, ui_1.bold)('db migrate')},
|
|
196
|
+
instead: ${(0, ui_1.bold)('pull')}, ${(0, ui_1.bold)('status')}, ${(0, ui_1.bold)('dev')}, ${(0, ui_1.bold)('apps add')}, ${(0, ui_1.bold)('db migrate')},
|
|
182
197
|
${(0, ui_1.bold)('env check')}, ${(0, ui_1.bold)('env set')}. They answer about the files next to them, so
|
|
183
|
-
they keep asking for a folder. ${(0, ui_1.bold)('
|
|
198
|
+
they keep asking for a folder. ${(0, ui_1.bold)('publish')} and ${(0, ui_1.bold)('rollback')} take ${(0, ui_1.bold)('--app')} rather than
|
|
199
|
+
${(0, ui_1.bold)('--project')}: they move one app. ${(0, ui_1.bold)('projects get')} and ${(0, ui_1.bold)('link')} take no flag either:
|
|
184
200
|
they name the project by its id as an argument.
|
|
185
201
|
|
|
186
202
|
The folder is then ignored whole, and that includes its ${(0, ui_1.bold)('xflow.json')}: the address
|
|
@@ -198,13 +214,35 @@ With several organizations signed in, the one holding the project is looked for
|
|
|
198
214
|
every call: there is no folder to write the answer into, and a second place to
|
|
199
215
|
remember it would be a second thing to keep true. When most calls go to one project,
|
|
200
216
|
${(0, ui_1.bold)('xflow org switch')} to its organization skips that search.`,
|
|
201
|
-
projects: `${(0, ui_1.bold)('xflow projects')}:
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
217
|
+
projects: `${(0, ui_1.bold)('xflow projects')} and ${(0, ui_1.bold)('xflow apps')}: projects and their apps
|
|
218
|
+
|
|
219
|
+
A project is a box: one database with its migrations, the cloud functions, secrets and
|
|
220
|
+
members, and no code of its own. An app is code the platform builds and serves at
|
|
221
|
+
https://<id>--<org>.xflowpage.ru, with its own versions and access level. One folder per
|
|
222
|
+
project, apps in subfolders:
|
|
223
|
+
|
|
224
|
+
xflow.json projectId, and who may call each function
|
|
225
|
+
package.json dependencies of the functions
|
|
226
|
+
functions/<name>/index.ts cloud functions, shared by every app
|
|
227
|
+
migrations/0001_init.sql the one database of the project
|
|
228
|
+
apps/<folder>/ an app: a Vite project with its own xflow.json (appId)
|
|
229
|
+
|
|
230
|
+
xflow projects list every project, its apps count and age
|
|
231
|
+
xflow projects get [id] one card: apps, database, functions and callers, schedules
|
|
232
|
+
xflow apps the apps of this project, their folders and addresses
|
|
233
|
+
--json as JSON, with the app ids
|
|
234
|
+
xflow apps add <folder> a new app, laid into apps/<folder> from the template
|
|
235
|
+
--name <name> its name (the folder by default)
|
|
236
|
+
--color <color> its tile color (the platform picks one by default)
|
|
237
|
+
xflow status in the root: the root and every app; in an app: that app
|
|
238
|
+
|
|
239
|
+
The root and every app are separate histories: apps/** never travels with the root, the
|
|
240
|
+
root never travels with an app. The app is its appId, so a folder can be renamed freely.
|
|
241
|
+
Who opens an app (members of the project, a list, anyone with the link) is set by a person
|
|
242
|
+
in the web interface, not here.
|
|
243
|
+
|
|
244
|
+
Both ${(0, ui_1.bold)('projects')} commands name the project positionally: the id is an argument, not a
|
|
245
|
+
--project flag. Without one, ${(0, ui_1.bold)('get')} answers about the project of the folder it stands in.
|
|
208
246
|
|
|
209
247
|
An id typed by hand is looked for across every signed-in organization, and the
|
|
210
248
|
folder around the command is ignored whole, the way the ${(0, ui_1.bold)('--project')} flag does it
|
|
@@ -218,14 +256,14 @@ projects these are.
|
|
|
218
256
|
${(0, ui_1.bold)('xflow link <id>')} binds the current folder to a project; ${(0, ui_1.bold)('xflow init')} makes a new one.`,
|
|
219
257
|
link: `${(0, ui_1.bold)('xflow link')} <id>: bind this folder to a project
|
|
220
258
|
|
|
221
|
-
Writes xflow.json and .xflow/state.json
|
|
222
|
-
|
|
223
|
-
|
|
259
|
+
Writes the root xflow.json and .xflow/state.json and lays down the instructions for the
|
|
260
|
+
agents. The id comes from ${(0, ui_1.bold)('xflow projects list')}; a project of another signed-in
|
|
261
|
+
organization is found too, and the folder is bound to that organization.
|
|
224
262
|
|
|
225
|
-
An empty folder (.git alone counts as empty)
|
|
226
|
-
|
|
227
|
-
folder with files keeps them as they are: ${(0, ui_1.bold)('xflow status')} says
|
|
228
|
-
|
|
263
|
+
An empty folder (.git alone counts as empty) gets the whole project: the latest revision
|
|
264
|
+
of the root and of every app, each app in apps/<folder> under the folder name the project
|
|
265
|
+
revision records. A folder with files keeps them as they are: ${(0, ui_1.bold)('xflow status')} says
|
|
266
|
+
how they differ, ${(0, ui_1.bold)('xflow pull --into ./server-copy')} fetches the whole project next to them.
|
|
229
267
|
|
|
230
268
|
In a folder that is already linked, the command points xflow.json at the other project and
|
|
231
269
|
keeps the rest of the file. A new project instead of an existing one: ${(0, ui_1.bold)('xflow init')}.`,
|
|
@@ -233,8 +271,8 @@ keeps the rest of the file. A new project instead of an existing one: ${(0, ui_1
|
|
|
233
271
|
|
|
234
272
|
A page is a static site at https://<name>.xflowpage.ru: a landing page, a presentation, an
|
|
235
273
|
invoice for people outside the organization. It opens by its address for anyone, without
|
|
236
|
-
signing in and
|
|
237
|
-
platform,
|
|
274
|
+
signing in, and has no build, no publish step and no database. An app is the opposite: code
|
|
275
|
+
the platform builds, with versions, access levels and a database through functions.
|
|
238
276
|
|
|
239
277
|
xflow pages the pages, their state and address
|
|
240
278
|
xflow pages create <title> <path> a new page with its first version: there are no
|
|
@@ -245,6 +283,8 @@ platform, for the members of the organization.
|
|
|
245
283
|
-m <text> what changed, one short line: it names the version
|
|
246
284
|
xflow pages status <page> the page and its versions
|
|
247
285
|
xflow pages rollback <page> <N> put version N back on the address
|
|
286
|
+
xflow pages rm <page> delete the page
|
|
287
|
+
--yes without it the command names what goes and stops
|
|
248
288
|
|
|
249
289
|
${(0, ui_1.bold)('<path>')} is a folder with index.html at the top, its .zip, or one .html file.
|
|
250
290
|
${(0, ui_1.bold)('<page>')} is the name from the address or the id. ${(0, ui_1.bold)('--json')} on list and status
|
|
@@ -261,14 +301,20 @@ build it and upload what the build writes. Dot entries are skipped. At most 5 MB
|
|
|
261
301
|
unpacked and 1000 files.
|
|
262
302
|
|
|
263
303
|
The page is public, so a file holding a secret key is refused whole: every visitor would read
|
|
264
|
-
it. A page that needs a backend (a form, a lookup) calls
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
304
|
+
it. A page that needs a backend (a form, a lookup) calls ${(0, ui_1.bold)('/_fn/<name>')} on its own address,
|
|
305
|
+
and only a function whose callers in the root xflow.json of its project name the page answers:
|
|
306
|
+
add the page name there and run ${(0, ui_1.bold)('xflow deploy functions')}. The page has to exist first.
|
|
307
|
+
Such a function is open to the whole internet. Search engines are asked not to index pages,
|
|
308
|
+
so do not promise SEO.
|
|
309
|
+
|
|
310
|
+
${(0, ui_1.bold)('Deleting is final.')} The address goes empty and every version of the page is gone: there
|
|
311
|
+
is no undo and no copy on the platform side. The name stays reserved for the organization, so
|
|
312
|
+
the same ${(0, ui_1.bold)('--slug')} is free for the next page of yours and taken for everyone else. A
|
|
313
|
+
throwaway page put up to try something is worth deleting the same way it was made.
|
|
268
314
|
|
|
269
315
|
A page takes the key of the whole organization, the one ${(0, ui_1.bold)('xflow login')} issues: a key
|
|
270
|
-
scoped to projects is refused.
|
|
271
|
-
platform interface.`,
|
|
316
|
+
scoped to projects is refused. A page is deleted by whoever made it, or by the owner of the
|
|
317
|
+
organization. Switching a page off for a while is for a person, in the platform interface.`,
|
|
272
318
|
update: `${(0, ui_1.bold)('xflow update')}: bring the CLI up to date
|
|
273
319
|
|
|
274
320
|
Installs the published version and, if anything changed, rewrites the platform
|
|
@@ -295,40 +341,35 @@ will work.`,
|
|
|
295
341
|
db: `${(0, ui_1.bold)('xflow db')}: schema, data, migrations
|
|
296
342
|
|
|
297
343
|
xflow db status what is applied, what is waiting, the server
|
|
298
|
-
xflow db list databases of the
|
|
344
|
+
xflow db list databases of the projects, the servers of the organization
|
|
299
345
|
xflow db schema [table] tables of the project, or the columns of one
|
|
300
346
|
xflow db query 'select ...' read data (--limit N: 50 by default, 300 at most;
|
|
301
347
|
--json prints the values whole)
|
|
302
348
|
xflow db query <file>.sql the same, the query read from a file
|
|
303
|
-
xflow db migrate apply migrations/*.sql
|
|
304
|
-
xflow db move --to <server> --confirm <
|
|
349
|
+
xflow db migrate store the project root, then apply migrations/*.sql
|
|
350
|
+
xflow db move --to <server> --confirm <project name>
|
|
305
351
|
move the project database to another server
|
|
306
352
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
the choice for ${(0, ui_1.bold)('xflow init --server')} and ${(0, ui_1.bold)('db move')}. Without that flag a new
|
|
315
|
-
database goes to ${(0, ui_1.bold)('platform')}.
|
|
353
|
+
Every project has exactly one database of its own, shared by all of its apps and functions,
|
|
354
|
+
and every command here works from any folder of the project. ${(0, ui_1.bold)('list')} is about the
|
|
355
|
+
organization: one database per project, and where each lives: ${(0, ui_1.bold)('platform')} is the common
|
|
356
|
+
server, ${(0, ui_1.bold)('own "<name>"')} an own server of the organization. While an own server is not
|
|
357
|
+
running, its databases do not answer, and that is not fixable from the code. An organization
|
|
358
|
+
with own servers also gets them listed, with the room used on each: that is the choice for
|
|
359
|
+
${(0, ui_1.bold)('xflow init --server')} and ${(0, ui_1.bold)('db move')}.
|
|
316
360
|
|
|
317
361
|
${(0, ui_1.bold)('move')} takes the database of this project to another server of the organization:
|
|
318
362
|
${(0, ui_1.bold)('platform')} or an own server by name (or id, when two share a name). The platform copies
|
|
319
|
-
the tables, switches the functions of
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
settings where the key permissions live. The command follows the move to the end;
|
|
363
|
+
the tables, switches the functions of the project and removes the old copy; nothing needs a
|
|
364
|
+
deploy. While it copies, the apps read data but cannot save anything new, usually for a
|
|
365
|
+
couple of minutes, which is why ${(0, ui_1.bold)('--confirm')} takes the project name. It also needs the
|
|
366
|
+
right to move databases on the key: off by default, only its owner turns it on, in the
|
|
367
|
+
platform settings where the key permissions live. The command follows the move to the end;
|
|
325
368
|
stopping it does not stop the move, and ${(0, ui_1.bold)('xflow db status')} shows where it stands. A move
|
|
326
|
-
that fails is rolled back
|
|
327
|
-
paying for and deleting servers stay with the person, in the web interface.
|
|
369
|
+
that fails is rolled back. Buying, paying for and deleting servers stay with the person.
|
|
328
370
|
|
|
329
|
-
${(0, ui_1.bold)('schema')} answers a different question than the ${(0, ui_1.bold)('migrations/')} directory:
|
|
330
|
-
|
|
331
|
-
says what is actually in there. ${(0, ui_1.bold)('query')} runs inside a READ ONLY transaction, so
|
|
371
|
+
${(0, ui_1.bold)('schema')} answers a different question than the ${(0, ui_1.bold)('migrations/')} directory: the files say
|
|
372
|
+
what you did, the schema says what is actually in there. ${(0, ui_1.bold)('query')} runs inside a READ ONLY transaction, so
|
|
332
373
|
writes are rejected by the database itself, not by us reading your SQL. Single quotes around
|
|
333
374
|
the query hold in PowerShell, bash and zsh, while cmd.exe wants double ones; a query that
|
|
334
375
|
needs quotes of its own goes in a .sql file outside the project, because Windows PowerShell
|
|
@@ -338,10 +379,12 @@ long values to keep the columns readable; ${(0, ui_1.bold)('--json')} prints the
|
|
|
338
379
|
the applied limit and its ceiling. ${(0, ui_1.bold)('status')} named by id (${(0, ui_1.bold)('--project')}) answers with
|
|
339
380
|
the applied history alone: comparing against ${(0, ui_1.bold)('migrations/')} needs the folder.
|
|
340
381
|
|
|
341
|
-
${(0, ui_1.bold)('migrate')}: the files live in the
|
|
382
|
+
${(0, ui_1.bold)('migrate')}: the files live in the project root (${(0, ui_1.bold)('migrations/0001_init.sql')},
|
|
342
383
|
${(0, ui_1.bold)('migrations/0002_orders.sql')} and so on). The order comes from the file name, which
|
|
343
|
-
is why the leading number is required.
|
|
344
|
-
|
|
384
|
+
is why the leading number is required. The command first stores the project root as a
|
|
385
|
+
revision, so the migrations stay in its history and a clone gets them, then applies what is
|
|
386
|
+
new. Every migration runs in its own transaction, the history is kept in the database
|
|
387
|
+
itself, and anything already applied is not run again.
|
|
345
388
|
|
|
346
389
|
--dry-run report what would be applied without touching the database
|
|
347
390
|
--allow-destructive allow operations that destroy data
|
|
@@ -353,21 +396,17 @@ access key. That right is off by default, and only its owner turns it on, in the
|
|
|
353
396
|
platform settings, where the key permissions live: a flag is something an agent adds by itself, a
|
|
354
397
|
right is not. With both in place, the contents of the affected tables are dumped and
|
|
355
398
|
kept for 7 days: that is a "caught it right away" safety net, not a backup.
|
|
356
|
-
${(0, ui_1.bold)('DROP DATABASE')} is never allowed
|
|
357
|
-
organization.
|
|
399
|
+
${(0, ui_1.bold)('DROP DATABASE')} is never allowed.
|
|
358
400
|
|
|
359
401
|
Editing an already applied file achieves nothing: ${(0, ui_1.bold)('migrate')} refuses a name that is
|
|
360
402
|
already applied with different content, and ${(0, ui_1.bold)('xflow db status')} lists such a file
|
|
361
|
-
separately. Write a new migration instead
|
|
362
|
-
|
|
363
|
-
One logical database can be attached to several projects, so the history may hold
|
|
364
|
-
migrations that are not in your repository. That is normal, but it also means your
|
|
365
|
-
migration can break somebody else's application, and that a name already in that history
|
|
366
|
-
is taken for you too: ${(0, ui_1.bold)('0001_init')} of a neighbour leaves yours no room.`,
|
|
403
|
+
separately. Write a new migration instead.`,
|
|
367
404
|
logs: `${(0, ui_1.bold)('xflow logs')} and ${(0, ui_1.bold)('xflow functions logs')}: project errors
|
|
368
405
|
|
|
369
|
-
|
|
370
|
-
|
|
406
|
+
Two streams: crashes of the cloud functions of the project, and browser errors of each
|
|
407
|
+
app. ${(0, ui_1.bold)('xflow logs')} in an app folder (or with ${(0, ui_1.bold)('--app <id>')}) shows that app, in the
|
|
408
|
+
project root every app, each row named by its folder. The last 200 entries of each stream
|
|
409
|
+
are kept, older ones fall off.
|
|
371
410
|
|
|
372
411
|
--limit N how many to show (10 by default)
|
|
373
412
|
|
|
@@ -382,7 +421,7 @@ never gets as far as the wrapper), going over the execution time cap of the plan
|
|
|
382
421
|
Those show up in the answer to ${(0, ui_1.bold)('xflow functions invoke')}.
|
|
383
422
|
|
|
384
423
|
Browser errors are collected by ${(0, ui_1.bold)('src/utils/error-logger.ts')} of the template and
|
|
385
|
-
only from
|
|
424
|
+
only from the published addresses: a local ${(0, ui_1.bold)('xflow dev')} writes nothing here.`,
|
|
386
425
|
storage: `${(0, ui_1.bold)('xflow storage')}: files of the application
|
|
387
426
|
|
|
388
427
|
The sources archive is capped at 10 MB and is rebuilt and re-uploaded on every
|
|
@@ -449,9 +488,10 @@ variables literally. ${(0, ui_1.bold)('check')} names those reads and the build
|
|
|
449
488
|
that quietly fails to arrive is found by the function falling over weeks later.
|
|
450
489
|
|
|
451
490
|
The value reaches the function on deploy, not at the moment it is stored: after
|
|
452
|
-
${(0, ui_1.bold)('env set')} run ${(0, ui_1.bold)('xflow deploy')}.
|
|
491
|
+
${(0, ui_1.bold)('env set')} run ${(0, ui_1.bold)('xflow deploy functions')}. It ships a function whose code did not
|
|
453
492
|
change but whose variables did, so nothing is left holding an old value. The same after a
|
|
454
|
-
delete: a function already deployed keeps the old value until its next deploy
|
|
493
|
+
delete: a function already deployed keeps the old value until its next deploy. Apps get no
|
|
494
|
+
variables at all: whatever a secret protects is reached through a function.`,
|
|
455
495
|
connections: `${(0, ui_1.bold)('xflow connections')}: accounts connected to the organization
|
|
456
496
|
|
|
457
497
|
Somebody signs in to Yandex Metrika, Bitrix or a mail service once, in the platform,
|
|
@@ -487,8 +527,8 @@ linked to a service project without a folder has to be removable the same way.
|
|
|
487
527
|
Variables are named after the alias: an OAuth connection called ${(0, ui_1.bold)('YANDEX_METRIKA')}
|
|
488
528
|
gives ${(0, ui_1.bold)('YANDEX_METRIKA_TOKEN')}, a key-based one gives a variable per field. They
|
|
489
529
|
arrive at a function on its next deploy, like every other variable, so after a change
|
|
490
|
-
run ${(0, ui_1.bold)('xflow deploy')}. A token close to expiry is renewed by
|
|
491
|
-
a revoked one is not renewed by anything until a human reconnects the account.
|
|
530
|
+
run ${(0, ui_1.bold)('xflow deploy functions')}. A token close to expiry is renewed by that deploy on the
|
|
531
|
+
way; a revoked one is not renewed by anything until a human reconnects the account.
|
|
492
532
|
|
|
493
533
|
Linking needs a right on the key, ${(0, ui_1.bold)('connections:link')}. Keys are issued with it,
|
|
494
534
|
and a person can take it away in the platform settings, where the key permissions live; a key cannot give
|
|
@@ -558,41 +598,38 @@ how Yandex works. ${(0, ui_1.bold)('The time is always UTC')}, local time is not
|
|
|
558
598
|
${(0, ui_1.bold)('list')} prints that body next to the schedule, so what a timer sends can be read
|
|
559
599
|
back without setting it again.
|
|
560
600
|
|
|
561
|
-
A scheduled run arrives at the handler as a POST with
|
|
562
|
-
|
|
563
|
-
|
|
601
|
+
A scheduled run arrives at the handler as a POST with ${(0, ui_1.bold)("event.xflow.caller === 'schedule'")}:
|
|
602
|
+
the envelope is signed by the platform, so such an event cannot be forged from outside. A
|
|
603
|
+
function without callers that only a schedule wakes is a workflow, and a project without
|
|
604
|
+
apps is made of those. The function has to be deployed already, since the schedule points
|
|
605
|
+
at it and not the other way round.
|
|
564
606
|
|
|
565
607
|
A failed run shows up in ${(0, ui_1.bold)('xflow functions logs')}: nobody is watching a scheduled
|
|
566
608
|
function, so the wrapper reports a crash the same way it does on an ordinary call.`,
|
|
567
609
|
functions: `${(0, ui_1.bold)('xflow functions')}: what is deployed, and calling it
|
|
568
610
|
|
|
569
|
-
xflow functions list name, state,
|
|
570
|
-
xflow functions invoke <name> call one
|
|
611
|
+
xflow functions list name, state, callers, open, keys, last call and error
|
|
612
|
+
xflow functions invoke <name> call one as yourself, through the platform
|
|
571
613
|
xflow functions logs [name] crashes: stack and console output
|
|
572
614
|
|
|
573
|
-
|
|
574
|
-
${(0, ui_1.bold)('functions/<name>/index.ts')}
|
|
615
|
+
Functions belong to the project and ship on their own step: ${(0, ui_1.bold)('xflow deploy functions')}
|
|
616
|
+
sends the project root and deploys everything in ${(0, ui_1.bold)('functions/<name>/index.ts')}. Every app
|
|
617
|
+
of the project runs the new code the moment it finishes; there is no publish step for them.
|
|
575
618
|
|
|
576
|
-
|
|
619
|
+
Who may call a function is its callers, in the root xflow.json:
|
|
577
620
|
|
|
578
|
-
|
|
579
|
-
external (N keys) plus an outside service holding a key a human issued in the interface
|
|
580
|
-
public plus anyone at all: the code says ${(0, ui_1.bold)("exports.access = 'public'")}
|
|
621
|
+
{ "projectId": "…", "functions": { "book-slot": { "callers": ["zapis", "clinic-landing"] } } }
|
|
581
622
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
623
|
+
A caller is an app folder of this project (apps/zapis is "zapis") or the name of a page of
|
|
624
|
+
the organization. A function nobody lists has no callers: a workflow, woken by a schedule,
|
|
625
|
+
by a service holding a key a person issued, or by ${(0, ui_1.bold)('invoke')}. ${(0, ui_1.bold)('open')} in the list means a
|
|
626
|
+
caller is public (a page, or an app anyone with the link opens), so anyone on the internet
|
|
627
|
+
can call it. The address in the note under the table is for webhooks and other functions,
|
|
628
|
+
with a key; apps and pages call ${(0, ui_1.bold)('/_fn/<name>')} on their own address.
|
|
588
629
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
${(0, ui_1.bold)('invoke')} calls it exactly the way the application does: the project token plus a
|
|
594
|
-
visitor pass for the person who owns the key, so the function sees a real caller and its
|
|
595
|
-
role. Both are taken from the platform, no local .env is needed.
|
|
630
|
+
${(0, ui_1.bold)('invoke')} calls it as the person who owns the key: the platform checks the role, gives
|
|
631
|
+
the function a one-minute ticket and calls it itself, so the function sees
|
|
632
|
+
${(0, ui_1.bold)("event.xflow.caller === 'platform'")} and the person in ${(0, ui_1.bold)('event.xflow.user')}.
|
|
596
633
|
|
|
597
634
|
--data '{"a":1}' request body (the method becomes POST by default)
|
|
598
635
|
--data-file body.json the same body from a file
|
|
@@ -604,16 +641,17 @@ strips double quotes from a native call, so ${(0, ui_1.bold)('--data')} needs th
|
|
|
604
641
|
(${(0, ui_1.bold)('--data \'{\\"a\\":1}\'')}) and a file needs nothing.
|
|
605
642
|
|
|
606
643
|
Prints the status, the response time and the body; under ${(0, ui_1.bold)('--json')} the body
|
|
607
|
-
alone. A non-zero exit code on 4xx and 5xx: in CI such a call has to fail the step.
|
|
608
|
-
|
|
644
|
+
alone. A non-zero exit code on 4xx and 5xx: in CI such a call has to fail the step. A
|
|
645
|
+
refusal of the guard names its reason in the body (${(0, ui_1.bold)('not_bound')}, ${(0, ui_1.bold)('no_access')},
|
|
646
|
+
${(0, ui_1.bold)('functions_stopped')}); for a crash the hint points at ${(0, ui_1.bold)('xflow functions logs <name>')}.`,
|
|
609
647
|
mcp: `${(0, ui_1.bold)('xflow mcp install')}: connect the agent to the platform directly
|
|
610
648
|
|
|
611
649
|
Writes the MCP server into the configuration of a client that has its own connect
|
|
612
650
|
command: today that is Claude Code, and for the rest the command prints what to enter
|
|
613
651
|
by hand. After that the agent runs the platform without a terminal: it inspects the
|
|
614
|
-
database, applies migrations,
|
|
615
|
-
The commands stay: code, builds and
|
|
616
|
-
pushing sources through MCP means filling the agent context and spending your money.
|
|
652
|
+
database, applies migrations, calls functions, sets schedules, publishes app builds, reads
|
|
653
|
+
logs. The commands stay: code, builds and functions deploys are still done by the CLI,
|
|
654
|
+
because pushing sources through MCP means filling the agent context and spending your money.
|
|
617
655
|
|
|
618
656
|
The command takes the access key from your own login and does not print it: usually
|
|
619
657
|
the agent runs this command itself, and everything printed lands in its context and in
|
|
@@ -653,53 +691,57 @@ Plus a few pointer lines in ${(0, ui_1.bold)('AGENTS.md')}. A skill is picked up
|
|
|
653
691
|
description matches the task, and "add a customers table" will not trigger it. AGENTS.md
|
|
654
692
|
is always read by the agent, which is why the pointer is appended there, at the end of
|
|
655
693
|
the file and once. ${(0, ui_1.bold)('CLAUDE.md')} is left alone: Claude Code reads .claude/skills anyway.`,
|
|
656
|
-
dev: `${(0, ui_1.bold)('xflow dev')}: run
|
|
657
|
-
|
|
658
|
-
Runs ${(0, ui_1.bold)('npm run dev')} in the project folder (127.0.0.1:3000) with the access key of the
|
|
659
|
-
organization that holds the project. The dev server trades that key for a visitor pass,
|
|
660
|
-
so the cloud functions answer here the way they do on the platform. The key is picked
|
|
661
|
-
as for every other command (${(0, ui_1.bold)('xflow help org')}): with several organizations signed
|
|
662
|
-
in, the right one is found and the folder is bound to it.
|
|
694
|
+
dev: `${(0, ui_1.bold)('xflow dev')}: run an app on this machine
|
|
663
695
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
696
|
+
Runs ${(0, ui_1.bold)('npm run dev')} in the app folder it stands in (127.0.0.1:3000) with the access key
|
|
697
|
+
of the organization that holds the project. Every ${(0, ui_1.bold)('/_fn/<name>')} call of the app goes to
|
|
698
|
+
the platform as this app, so the callers, the access and the person calling are the same
|
|
699
|
+
as on its address. Nothing about functions is kept on this machine: a function someone
|
|
700
|
+
else shipped answers here at once.
|
|
668
701
|
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
fetched on every start as well: a function someone else shipped answers here without a
|
|
673
|
-
deploy of yours.
|
|
702
|
+
Use it instead of a bare ${(0, ui_1.bold)('npm run dev')}: that one is handed no key and no app id, so
|
|
703
|
+
its calls to functions fail. The platform address comes from the CLI too, so the key goes
|
|
704
|
+
to the platform that issued it and nowhere else. It never reaches the browser.
|
|
674
705
|
|
|
675
706
|
The server listens on 127.0.0.1 and nowhere else, because it holds that key: on a
|
|
676
707
|
shared network, anything else would let a neighbour call the functions of the project
|
|
677
708
|
as you. A phone on the same Wi-Fi therefore cannot open it either.`,
|
|
678
|
-
pull: `${(0, ui_1.bold)('xflow pull')}: fetch the sources
|
|
709
|
+
pull: `${(0, ui_1.bold)('xflow pull')} [functions|apps/<folder>]: fetch the sources
|
|
679
710
|
|
|
680
|
-
|
|
681
|
-
|
|
711
|
+
Every app and the project root are separate histories, and pull fetches one of them: in an
|
|
712
|
+
app folder that app, in the project root the root (apps/ stays as it is), or the one named.
|
|
713
|
+
It refuses to write into a non-empty folder: the platform cannot merge changes, that is
|
|
714
|
+
git's job.
|
|
682
715
|
|
|
683
|
-
--into <dir> unpack alongside, to compare
|
|
684
|
-
|
|
716
|
+
--into <dir> unpack alongside, to compare; in the project root with nothing named
|
|
717
|
+
it fetches the whole project there
|
|
718
|
+
--revision <N> a specific revision of that history (xflow deployments lists them)
|
|
685
719
|
--force overwrite the folder completely`,
|
|
686
|
-
deploy: `${(0, ui_1.bold)('xflow deploy')}: build
|
|
720
|
+
deploy: `${(0, ui_1.bold)('xflow deploy')}: deploy the functions or build an app, then publish
|
|
687
721
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
722
|
+
xflow deploy functions -m "…" send the project root, check it, ship the functions
|
|
723
|
+
and their callers
|
|
724
|
+
xflow deploy apps/<folder> -m "…" send one app and build it; inside its folder a plain
|
|
725
|
+
xflow deploy does the same
|
|
726
|
+
xflow publish [build] point the app address at the newest build, or a named one
|
|
727
|
+
xflow rollback <build> the same, the number required
|
|
728
|
+
xflow deployments [functions] the builds of the app, or the deploys of the functions
|
|
729
|
+
|
|
730
|
+
In the project root a plain ${(0, ui_1.bold)('xflow deploy')} refuses and names the targets: functions and
|
|
731
|
+
apps never go in one run, because a change of functions reaches every app at once.
|
|
691
732
|
|
|
692
733
|
-m <text> the version comment, required: one short line in the language the
|
|
693
|
-
user speaks, saying what changed
|
|
694
|
-
history (--message is the long form)
|
|
734
|
+
user speaks, saying what changed (--message is the long form)
|
|
695
735
|
--no-push do not send sources, build from the latest server revision
|
|
696
|
-
--revision <N>
|
|
697
|
-
|
|
698
|
-
--project <id> with --revision only: the
|
|
736
|
+
--revision <N> run a revision the platform already holds: that is how the functions
|
|
737
|
+
come back (xflow deployments functions lists them)
|
|
738
|
+
--project <id> with --revision only: the functions of that project, no folder needed
|
|
739
|
+
--app <id> with --revision only: that app, no folder needed
|
|
699
740
|
--force allow overwriting the server revision while sending
|
|
700
741
|
--allow-removals agree in advance to remove the functions gone from the sources
|
|
701
742
|
|
|
702
|
-
|
|
743
|
+
What goes up: an app sends its own folder; the project root sends everything but apps/,
|
|
744
|
+
plus the apps/<folder>/xflow.json files its callers are read from. Never sent: node_modules,
|
|
703
745
|
.git, dist, build, .next, any .env, plus everything listed in .xflowignore and in the
|
|
704
746
|
ignore field of xflow.json.
|
|
705
747
|
|
|
@@ -713,20 +755,21 @@ CLI shows whose work you are about to destroy and asks for confirmation by typin
|
|
|
713
755
|
project name. In a non-interactive run (CI, an agent) there is no way to confirm: a
|
|
714
756
|
version conflict has to fail the build rather than silently destroy somebody else's work.
|
|
715
757
|
|
|
716
|
-
The platform builds, in a clean sandbox on one Node version for everybody
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
that
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
758
|
+
The platform builds, in a clean sandbox on one Node version for everybody. Before that the
|
|
759
|
+
sources are checked: what breaks silently (the platform files, the calls in the entry point,
|
|
760
|
+
keys in the code, alert instead of a dialog) is printed as a list and nothing starts. What is
|
|
761
|
+
merely unfinished (the template page title, no icon) goes through and prints a line
|
|
762
|
+
${(0, ui_1.bold)('Unfinished:')}. Fix it before telling the person the app is done.
|
|
763
|
+
|
|
764
|
+
${(0, ui_1.bold)('deploy functions')} bundles everything in ${(0, ui_1.bold)('functions/<name>/index.ts')}. A function whose
|
|
765
|
+
code and variables did not change is left alone, and one that fails to ship fails the whole
|
|
766
|
+
deploy. It then records the callers and prints which functions are open to the whole
|
|
767
|
+
internet now. A change that touches data and screens goes in this order: the migration,
|
|
768
|
+
the functions, then every app that shows it; an app deploy warns when the root holds
|
|
769
|
+
changes nobody deployed.
|
|
770
|
+
|
|
771
|
+
A function gone from the sources would be deleted from the cloud along with its keys and
|
|
772
|
+
schedules, and that is final. So the deploy does not start at all until you say yes. In a terminal the CLI names the functions
|
|
730
773
|
and asks; without one (CI, an agent) it stops and ${(0, ui_1.bold)('--allow-removals')} is the only way
|
|
731
774
|
to agree. If the sources hold no functions at all while the cloud holds several, nothing
|
|
732
775
|
is deleted and nothing is asked: that looks like a directory which never made it (a
|
|
@@ -735,60 +778,26 @@ ${(0, ui_1.bold)('functions/')} line in .xflowignore) rather than a decision.
|
|
|
735
778
|
The database is not part of this: migrations change data in ways nothing can undo, so
|
|
736
779
|
they stay their own command (${(0, ui_1.bold)('xflow db migrate')}).
|
|
737
780
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
Lays down the platform template: React on Vite, Tailwind, a set of components
|
|
753
|
-
(${(0, ui_1.bold)('src/components/ui')}) and ready-made blocks (${(0, ui_1.bold)('src/components/blocks')}): app
|
|
754
|
-
shell, page header, section, toolbar, list, empty state, stats, detail card, form rows,
|
|
755
|
-
card grid, timeline, table, charts. Projects created in the web get the same template,
|
|
756
|
-
which is what makes the applications look alike.
|
|
757
|
-
|
|
758
|
-
Building the interface on top of these components and the tokens in ${(0, ui_1.bold)('src/index.css')}
|
|
759
|
-
is not a formality: your own palette on top of them looks foreign inside the platform.
|
|
781
|
+
A build of an app is not shown to visitors until ${(0, ui_1.bold)('xflow publish')}: publishing points the
|
|
782
|
+
app address at a build and takes a second, and an older kept build is the rollback. Only
|
|
783
|
+
the app moves: the functions and the database are one per project and stay as they are.
|
|
784
|
+
The very first build of a new app publishes itself. The files of the three latest builds
|
|
785
|
+
are kept; ${(0, ui_1.bold)('xflow deployments')} marks the rest "no pages". Give a person the app address
|
|
786
|
+
the CLI prints, and name builds by their number.`,
|
|
787
|
+
init: `${(0, ui_1.bold)('xflow init')} [dir]: a new project with its first app
|
|
788
|
+
|
|
789
|
+
Creates the project on the platform and lays out its folder: the root xflow.json, a
|
|
790
|
+
package.json for the functions, empty ${(0, ui_1.bold)('functions/')} and ${(0, ui_1.bold)('migrations/')}, and the first app in
|
|
791
|
+
apps/<folder> from the platform template: React on Vite, Tailwind, components in
|
|
792
|
+
${(0, ui_1.bold)('src/components/ui')} and blocks in ${(0, ui_1.bold)('src/components/blocks')}. Those are a starting set:
|
|
793
|
+
change them, restyle them or delete what you do not use.
|
|
760
794
|
|
|
761
795
|
--name <name> project name (the folder name by default)
|
|
762
|
-
--
|
|
763
|
-
--
|
|
764
|
-
--
|
|
765
|
-
--server <name> a new database for the project, right away on that server:
|
|
796
|
+
--app <folder> the folder of the first app (made from the name by default)
|
|
797
|
+
--no-app a project without apps: its functions are workflows
|
|
798
|
+
--server <name> the database of the project, right away on that server:
|
|
766
799
|
platform, or an own server of the organization (xflow db list)
|
|
767
800
|
|
|
768
|
-
${(0, ui_1.bold)('
|
|
769
|
-
|
|
770
|
-
application of the organization calls it; who exactly is allowed in is decided by
|
|
771
|
-
${(0, ui_1.bold)('minRole')} inside the function. It builds and releases with the same deploy, but
|
|
772
|
-
there are no pages to publish, so publish and rollback do not apply to it.
|
|
773
|
-
|
|
774
|
-
Without either flag the project gets its database with the first migration, on the
|
|
775
|
-
platform server. ${(0, ui_1.bold)('--server')} decides it up front: the server has to be running, and
|
|
776
|
-
the database counts against the plan at once.
|
|
777
|
-
|
|
778
|
-
The order: the project is created on the platform first, because without it there is
|
|
779
|
-
nowhere to get the token for .env. If writing the files fails, the project stays empty
|
|
780
|
-
and the CLI explains how to pick it up with the link command.`,
|
|
781
|
-
rollback: `${(0, ui_1.bold)('xflow rollback')} <version number>: serve the pages of an earlier build
|
|
782
|
-
|
|
783
|
-
Points the project at the pages of that build. ${(0, ui_1.bold)('Only the pages come back.')} Cloud
|
|
784
|
-
functions and the database are one per project: they are not versioned, they are shared
|
|
785
|
-
with the published application, and they stay exactly as they are now. A page from August
|
|
786
|
-
will be talking to today's functions.
|
|
787
|
-
|
|
788
|
-
The sources stay at their own revision too. Fetching the code of that build is a separate
|
|
789
|
-
command (${(0, ui_1.bold)('xflow pull --revision N --into ../old-version')}, keep the copy outside the
|
|
790
|
-
project folder). Building that code over the current one is a further, deliberate step: it
|
|
791
|
-
replaces the server revision, and whatever is newer on the platform goes away.
|
|
792
|
-
|
|
793
|
-
Visitors keep seeing the published pages until ${(0, ui_1.bold)('xflow publish')} is run.`,
|
|
801
|
+
More apps later: ${(0, ui_1.bold)('xflow apps add <folder>')}. If writing the files fails, the project stays
|
|
802
|
+
on the platform and the CLI says how to pick it up with the link command.`,
|
|
794
803
|
};
|