@getxflow/cli 0.10.0 → 0.10.2

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/help.js CHANGED
@@ -3,459 +3,479 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.help = help;
4
4
  const ui_1 = require("./ui");
5
5
  const version_1 = require("./version");
6
+ /**
7
+ * A page keyed by something other than the command that leads to it. `xflow help invoke`
8
+ * is nobody's first guess when the command is `xflow functions invoke`, and a page that
9
+ * covers a neighbour answers for it too.
10
+ */
11
+ const ALIASES = {
12
+ functions: 'invoke',
13
+ publish: 'deploy',
14
+ deployments: 'rollback',
15
+ };
6
16
  /** Help text. For agents this is the reference documentation. */
7
17
  function help(topic) {
8
- if (topic && TOPICS[topic]) {
9
- (0, ui_1.out)(TOPICS[topic]);
18
+ if (topic) {
19
+ const page = TOPICS[ALIASES[topic] ?? topic];
20
+ if (page) {
21
+ (0, ui_1.out)(page);
22
+ return;
23
+ }
24
+ // Falling through to the full help here is what this used to do, and it reads as an
25
+ // answer: the agent asked about one command and got the whole screen back, with no
26
+ // sign that the page it asked for does not exist.
27
+ (0, ui_1.out)(`No page for "${topic}". Pages: ${Object.keys(TOPICS).sort().join(', ')}
28
+ Every command with a line of its own: xflow help`);
10
29
  return;
11
30
  }
12
- (0, ui_1.out)(`${(0, ui_1.bold)('xflow')} ${(0, ui_1.dim)(version_1.CLI_VERSION)} XFlow application hosting
13
-
14
- ${(0, ui_1.bold)('Getting started')}
15
- xflow login sign in through the browser
16
- xflow init [dir] new project from the platform template
17
- xflow templates which templates are available
18
- xflow link <id> link this folder to a project, an empty one also gets the sources
19
- xflow skills pick the agents that get the platform instructions
20
- xflow mcp install give the agent platform access without a terminal
21
-
22
- ${(0, ui_1.bold)('Sources')}
23
- xflow status what is on the server and how the local copy differs
24
- xflow pull [--into dir] [--revision N]
25
- fetch the sources (the latest revision by default)
26
-
27
- ${(0, ui_1.bold)('Files')}
28
- xflow storage ls [folder] what the file storage of the project holds
29
- xflow storage push <dir> [--to folder] [--replace]
30
- upload heavy static assets: photos, video, PDFs
31
- xflow storage rm <address> delete one file
32
- xflow storage rm --folder <path> --yes
33
- delete a folder with everything in it
34
-
35
- ${(0, ui_1.bold)('Releasing')}
36
- xflow deploy [--no-push] send the code, ship the functions, build on the platform
37
- xflow publish show the dev version to visitors
38
- xflow rollback <version number> serve the pages of an earlier build
39
- xflow deployments version history
40
-
41
- ${(0, ui_1.bold)('Functions')}
42
- xflow functions list what is deployed (they ship with xflow deploy)
43
- xflow functions invoke <name> [--data '{"a":1}']
44
- call a function and print the answer
45
- xflow functions logs [name] function crashes: stack and console output
46
- xflow schedules list what runs on a timer
47
- xflow schedules set <name> "0 3 ? * * *"
48
- run a function on a schedule (UTC)
49
- xflow schedules rm <name> take a schedule off
50
- xflow env [check] function variables: what is stored, what is missing
51
- xflow env set NAME=value store a variable
52
- xflow env rm NAME delete a variable
53
- xflow connections connected accounts the project can use, and their variables
54
- xflow connections link <name> --as ALIAS
55
- give the functions the credentials of an account
56
- xflow connections unlink <ALIAS> [--force]
57
- take them away again
58
-
59
- ${(0, ui_1.bold)('Database')}
60
- xflow db status which migrations are applied and which are waiting
61
- xflow db schema [table] tables of the project, or the columns of one
62
- xflow db query "select ..." read data, in a read-only transaction
63
- xflow db migrate [--dry-run] [--allow-destructive]
64
- apply the migrations from migrations/*.sql
65
-
66
- ${(0, ui_1.bold)('Reference')}
67
- xflow logs [--limit N] browser errors from the released application
68
- xflow projects list projects of the organization
69
- xflow projects get [id] project card
70
- xflow org organizations with a stored key, the active one marked
71
- xflow org switch <name|id> make another organization the active one
72
- xflow whoami whose key this is and what it can do
73
- xflow logout [--all] forget the key of the active organization (--all: every one)
74
- xflow update update the CLI itself, and the skill that ships with it
75
-
76
- ${(0, ui_1.bold)('Environment')}
77
- XFLOW_TOKEN access key (for CI, instead of xflow login)
78
- XFLOW_API_URL platform address when it is not app.getxflow.com
79
-
31
+ (0, ui_1.out)(`${(0, ui_1.bold)('xflow')} ${(0, ui_1.dim)(version_1.CLI_VERSION)} XFlow application hosting
32
+
33
+ ${(0, ui_1.bold)('Getting started')}
34
+ xflow login sign in through the browser
35
+ xflow init [dir] new project from the platform template
36
+ xflow templates which templates are available
37
+ xflow link <id> link this folder to a project, an empty one also gets the sources
38
+ xflow skills pick the agents that get the platform instructions
39
+ xflow mcp install give the agent platform access without a terminal
40
+
41
+ ${(0, ui_1.bold)('Sources')}
42
+ xflow status what is on the server and how the local copy differs
43
+ xflow pull [--into dir] [--revision N]
44
+ fetch the sources (the latest revision by default)
45
+
46
+ ${(0, ui_1.bold)('Files')}
47
+ xflow storage ls [folder] what the file storage of the project holds
48
+ xflow storage push <dir> [--to folder] [--replace]
49
+ upload heavy static assets: photos, video, PDFs
50
+ xflow storage rm <address> delete one file
51
+ xflow storage rm --folder <path> --yes
52
+ delete a folder with everything in it
53
+
54
+ ${(0, ui_1.bold)('Releasing')}
55
+ xflow deploy [--no-push] send the code, ship the functions, build on the platform
56
+ xflow publish show the dev version to visitors
57
+ xflow rollback <version number> serve the pages of an earlier build
58
+ xflow deployments version history
59
+
60
+ ${(0, ui_1.bold)('Functions')}
61
+ xflow functions list what is deployed (they ship with xflow deploy)
62
+ xflow functions invoke <name> [--data '{"a":1}']
63
+ call a function and print the answer
64
+ xflow functions logs [name] function crashes: stack and console output
65
+ xflow schedules list what runs on a timer
66
+ xflow schedules set <name> "0 3 ? * * *"
67
+ run a function on a schedule (UTC)
68
+ xflow schedules rm <name> take a schedule off
69
+ xflow env [check] function variables: what is stored, what is missing
70
+ xflow env set NAME=value store a variable
71
+ xflow env rm NAME delete a variable
72
+ xflow connections connected accounts the project can use, and their variables
73
+ xflow connections link <name> --as ALIAS
74
+ give the functions the credentials of an account
75
+ xflow connections unlink <ALIAS> [--force]
76
+ take them away again
77
+
78
+ ${(0, ui_1.bold)('Database')}
79
+ xflow db status which migrations are applied and which are waiting
80
+ xflow db schema [table] tables of the project, or the columns of one
81
+ xflow db query "select ..." read data, in a read-only transaction
82
+ xflow db migrate [--dry-run] [--allow-destructive]
83
+ apply the migrations from migrations/*.sql
84
+
85
+ ${(0, ui_1.bold)('Reference')}
86
+ xflow logs [--limit N] browser errors from the released application
87
+ xflow projects list projects of the organization
88
+ xflow projects get [id] project card
89
+ xflow org organizations with a stored key, the active one marked
90
+ xflow org switch <name|id> make another organization the active one
91
+ xflow whoami whose key this is and what it can do
92
+ xflow logout [--all] forget the key of the active organization (--all: every one)
93
+ xflow update update the CLI itself, and the skill that ships with it
94
+ xflow --version which version is installed
95
+
96
+ ${(0, ui_1.bold)('Environment')}
97
+ XFLOW_TOKEN access key (for CI, instead of xflow login)
98
+ XFLOW_API_URL platform address when it is not app.getxflow.com
99
+
80
100
  More about one command: xflow help <command>`);
81
101
  }
82
102
  const TOPICS = {
83
- org: `${(0, ui_1.bold)('xflow org')}: several organizations, one terminal
84
-
85
- One key per organization: every ${(0, ui_1.bold)('xflow login')} stores the key of the organization
86
- chosen in the browser next to the ones already stored, it does not replace them.
87
- Which key a command then uses, in order:
88
-
89
- 1. XFLOW_TOKEN, when set (and only for the default platform address)
90
- 2. the organization this folder is bound to (${(0, ui_1.bold)('.xflow/state.json')}, written by
91
- init, link and the first successful deploy or pull)
92
- 3. the active organization
93
-
94
- xflow org the stored organizations, the active one marked
95
- xflow org switch <name|id> make another one active, no browser involved
96
- xflow logout forget the key of the active organization
97
- xflow logout --all forget every key of this platform address
98
-
99
- The switch is local: it changes which stored key is used and nothing happens on
100
- the platform. A folder bound to an organization is not affected, its commands
101
- stay in its own organization: two projects of two organizations in two terminals
102
- work without switching anything.
103
-
104
- Outside a project folder the switch also rewrites the key in the agent config
105
- (see ${(0, ui_1.bold)('xflow mcp')}) when an xflow entry already exists there; a running agent
106
- session picks the new key up only after a restart.
107
-
108
- A project id is unique across the whole platform, so "project not found" under
109
- the wrong organization can never touch somebody else's project. When that error
110
- names a project you know exists, the key is simply from another organization:
103
+ org: `${(0, ui_1.bold)('xflow org')}: several organizations, one terminal
104
+
105
+ One key per organization: every ${(0, ui_1.bold)('xflow login')} stores the key of the organization
106
+ chosen in the browser next to the ones already stored, it does not replace them.
107
+ Which key a command then uses, in order:
108
+
109
+ 1. XFLOW_TOKEN, when set (and only for the default platform address)
110
+ 2. the organization this folder is bound to (${(0, ui_1.bold)('.xflow/state.json')}, written by
111
+ init, link and the first successful deploy or pull)
112
+ 3. the active organization
113
+
114
+ xflow org the stored organizations, the active one marked
115
+ xflow org switch <name|id> make another one active, no browser involved
116
+ xflow logout forget the key of the active organization
117
+ xflow logout --all forget every key of this platform address
118
+
119
+ The switch is local: it changes which stored key is used and nothing happens on
120
+ the platform. A folder bound to an organization is not affected, its commands
121
+ stay in its own organization: two projects of two organizations in two terminals
122
+ work without switching anything.
123
+
124
+ Outside a project folder the switch also rewrites the key in the agent config
125
+ (see ${(0, ui_1.bold)('xflow mcp')}) when an xflow entry already exists there; a running agent
126
+ session picks the new key up only after a restart.
127
+
128
+ A project id is unique across the whole platform, so "project not found" under
129
+ the wrong organization can never touch somebody else's project. When that error
130
+ names a project you know exists, the key is simply from another organization:
111
131
  check ${(0, ui_1.bold)('xflow org')}.`,
112
- update: `${(0, ui_1.bold)('xflow update')}: bring the CLI up to date
113
-
114
- Installs the published version and, if anything changed, rewrites the platform
115
- instructions the agents already have: the skill ships inside the package and shares
116
- its version, so a fresh CLI next to a stale skill means the agent is reading last
117
- month's rules. Copies that are not installed are not created, so it is safe to run
118
- from any folder.
119
-
120
- The platform reports the published version with every answer, which is where the
121
- ${(0, ui_1.bold)('xflow N is out')} line comes from. It appears once a day at most, and never in CI,
122
- where nobody is going to update anything anyway.
123
-
124
- The command works out how this copy was installed from its own path, and refuses to
125
- cross package managers: an install made by pnpm or bun is printed as their own
126
- command rather than handed to npm, which would leave you with two copies and no way
127
- to tell which one answers. Two more cases end the same way. Under ${(0, ui_1.bold)('npx')} there is
128
- nothing to update, only a cache. And a copy inside a project's node_modules is a
129
- dependency of that project: ${(0, ui_1.bold)('npm i -D @getxflow/cli@latest')} is the fix, because
130
- inside npm scripts the local copy wins over anything installed globally.
131
-
132
- A refusal from the platform saying the CLI is too old is a different thing: that one
133
- is a hard gate on a changed contract, and until you run this command nothing else
132
+ update: `${(0, ui_1.bold)('xflow update')}: bring the CLI up to date
133
+
134
+ Installs the published version and, if anything changed, rewrites the platform
135
+ instructions the agents already have: the skill ships inside the package and shares
136
+ its version, so a fresh CLI next to a stale skill means the agent is reading last
137
+ month's rules. Copies that are not installed are not created, so it is safe to run
138
+ from any folder.
139
+
140
+ The platform reports the published version with every answer, which is where the
141
+ ${(0, ui_1.bold)('xflow N is out')} line comes from. It appears once a day at most, and never in CI,
142
+ where nobody is going to update anything anyway.
143
+
144
+ The command works out how this copy was installed from its own path, and refuses to
145
+ cross package managers: an install made by pnpm or bun is printed as their own
146
+ command rather than handed to npm, which would leave you with two copies and no way
147
+ to tell which one answers. Two more cases end the same way. Under ${(0, ui_1.bold)('npx')} there is
148
+ nothing to update, only a cache. And a copy inside a project's node_modules is a
149
+ dependency of that project: ${(0, ui_1.bold)('npm i -D @getxflow/cli@latest')} is the fix, because
150
+ inside npm scripts the local copy wins over anything installed globally.
151
+
152
+ A refusal from the platform saying the CLI is too old is a different thing: that one
153
+ is a hard gate on a changed contract, and until you run this command nothing else
134
154
  will work.`,
135
- db: `${(0, ui_1.bold)('xflow db')}: schema, data, migrations
136
-
137
- xflow db status what is applied and what is waiting
138
- xflow db schema [table] tables of the project, or the columns of one
139
- xflow db query "select ..." read data (--limit N)
140
- xflow db migrate apply migrations/*.sql
141
-
142
- ${(0, ui_1.bold)('schema')} answers a different question than the ${(0, ui_1.bold)('migrations/')} directory: one logical
143
- database is shared by several projects, so the files say what you did, and the schema
144
- says what is actually in there. ${(0, ui_1.bold)('query')} runs inside a READ ONLY transaction, so
145
- writes are rejected by the database itself, not by us reading your SQL.
146
-
147
- ${(0, ui_1.bold)('migrate')}: the files live in the repository (${(0, ui_1.bold)('migrations/0001_init.sql')},
148
- ${(0, ui_1.bold)('migrations/0002_orders.sql')} and so on). The order comes from the file name, which
149
- is why the leading number is required. Every migration runs in its own transaction, the
150
- history is kept in the database itself, and anything already applied is not run again.
151
-
152
- --dry-run report what would be applied without touching the database
153
- --allow-destructive allow operations that destroy data
154
-
155
- About destructive ones. The platform keeps no database history and makes no backups,
156
- so ${(0, ui_1.bold)('DROP TABLE')}, ${(0, ui_1.bold)('DROP COLUMN')}, ${(0, ui_1.bold)('TRUNCATE')} and ${(0, ui_1.bold)('DELETE FROM')} without a
157
- condition need two things at once: this flag, and the right to destroy data on the
158
- access key. That right is off by default, and only its owner turns it on, in the
159
- platform settings under Developers: a flag is something an agent adds by itself, a
160
- right is not. With both in place, the contents of the affected tables are dumped and
161
- kept for 7 days: that is a "caught it right away" safety net, not a backup.
162
- ${(0, ui_1.bold)('DROP DATABASE')} is never allowed, because the database is shared across the
163
- organization.
164
-
165
- Editing an already applied file achieves nothing: the comparison is by name, not by
166
- content. ${(0, ui_1.bold)('xflow db status')} lists such a file separately, so write a new migration
167
- instead.
168
-
169
- One logical database can be attached to several projects, so the history may hold
170
- migrations that are not in your repository. That is normal, but it also means your
155
+ db: `${(0, ui_1.bold)('xflow db')}: schema, data, migrations
156
+
157
+ xflow db status what is applied and what is waiting
158
+ xflow db schema [table] tables of the project, or the columns of one
159
+ xflow db query "select ..." read data (--limit N)
160
+ xflow db migrate apply migrations/*.sql
161
+
162
+ ${(0, ui_1.bold)('schema')} answers a different question than the ${(0, ui_1.bold)('migrations/')} directory: one logical
163
+ database is shared by several projects, so the files say what you did, and the schema
164
+ says what is actually in there. ${(0, ui_1.bold)('query')} runs inside a READ ONLY transaction, so
165
+ writes are rejected by the database itself, not by us reading your SQL.
166
+
167
+ ${(0, ui_1.bold)('migrate')}: the files live in the repository (${(0, ui_1.bold)('migrations/0001_init.sql')},
168
+ ${(0, ui_1.bold)('migrations/0002_orders.sql')} and so on). The order comes from the file name, which
169
+ is why the leading number is required. Every migration runs in its own transaction, the
170
+ history is kept in the database itself, and anything already applied is not run again.
171
+
172
+ --dry-run report what would be applied without touching the database
173
+ --allow-destructive allow operations that destroy data
174
+
175
+ About destructive ones. The platform keeps no database history and makes no backups,
176
+ so ${(0, ui_1.bold)('DROP TABLE')}, ${(0, ui_1.bold)('DROP COLUMN')}, ${(0, ui_1.bold)('TRUNCATE')} and ${(0, ui_1.bold)('DELETE FROM')} without a
177
+ condition need two things at once: this flag, and the right to destroy data on the
178
+ access key. That right is off by default, and only its owner turns it on, in the
179
+ platform settings under Developers: a flag is something an agent adds by itself, a
180
+ right is not. With both in place, the contents of the affected tables are dumped and
181
+ kept for 7 days: that is a "caught it right away" safety net, not a backup.
182
+ ${(0, ui_1.bold)('DROP DATABASE')} is never allowed, because the database is shared across the
183
+ organization.
184
+
185
+ Editing an already applied file achieves nothing: the comparison is by name, not by
186
+ content. ${(0, ui_1.bold)('xflow db status')} lists such a file separately, so write a new migration
187
+ instead.
188
+
189
+ One logical database can be attached to several projects, so the history may hold
190
+ migrations that are not in your repository. That is normal, but it also means your
171
191
  migration can break somebody else's application.`,
172
- logs: `${(0, ui_1.bold)('xflow logs')} and ${(0, ui_1.bold)('xflow functions logs')}: project errors
173
-
174
- One stream per project: crashes of the released application in the browser and
175
- crashes of the cloud functions. The last 200 entries are kept, older ones fall off.
176
-
177
- --limit N how many to show (10 by default)
178
-
179
- A function reports its own crashes: Yandex exposes its logs only over gRPC, so the
180
- wrapper catches the exception, carries the tail of the console with it (the last 40
181
- ${(0, ui_1.bold)('console.log')} lines of that call) and sends it to the platform. Successful calls
182
- write nothing, otherwise every request would pay for it in latency.
183
-
184
- What never reaches this list: a crash while the module is starting (the function
185
- never gets as far as the wrapper), going over 90 seconds, and running out of memory.
186
- Those show up in the answer to ${(0, ui_1.bold)('xflow functions invoke')}.
187
-
188
- Browser errors are collected by ${(0, ui_1.bold)('src/utils/error-logger.ts')} of the template and
192
+ logs: `${(0, ui_1.bold)('xflow logs')} and ${(0, ui_1.bold)('xflow functions logs')}: project errors
193
+
194
+ One stream per project: crashes of the released application in the browser and
195
+ crashes of the cloud functions. The last 200 entries are kept, older ones fall off.
196
+
197
+ --limit N how many to show (10 by default)
198
+
199
+ A function reports its own crashes: Yandex exposes its logs only over gRPC, so the
200
+ wrapper catches the exception, carries the tail of the console with it (the last 40
201
+ ${(0, ui_1.bold)('console.log')} lines of that call) and sends it to the platform. Successful calls
202
+ write nothing, otherwise every request would pay for it in latency.
203
+
204
+ What never reaches this list: a crash while the module is starting (the function
205
+ never gets as far as the wrapper), going over 90 seconds, and running out of memory.
206
+ Those show up in the answer to ${(0, ui_1.bold)('xflow functions invoke')}.
207
+
208
+ Browser errors are collected by ${(0, ui_1.bold)('src/utils/error-logger.ts')} of the template and
189
209
  only from released addresses: a local ${(0, ui_1.bold)('npm run dev')} writes nothing here.`,
190
- storage: `${(0, ui_1.bold)('xflow storage')}: files of the application
191
-
192
- The sources archive is capped at 10 MB and is rebuilt and re-uploaded on every
193
- deploy, so photos, video and PDFs do not belong in the repository. File storage
194
- is the place for them: the files sit beside the versions, survive a build, and
195
- are metered against the plan of the organization.
196
-
197
- xflow storage ls [folder] what is there, with addresses and sizes
198
- xflow storage push ./media --to media
199
- upload a folder, keeping its structure
200
- xflow storage rm <address> delete one file
201
- xflow storage rm --folder photos --yes
202
- delete a folder with everything in it
203
-
204
- --to <folder> where to put it in the project storage
205
- --replace upload files whose size differs from the stored one
206
- --json machine-readable output, with every address
207
-
208
- ${(0, ui_1.bold)('push')} sends only what is missing: a file already stored under the same path and
209
- size is skipped, so running the command again after a broken connection is cheap.
210
- A file that differs is left alone unless ${(0, ui_1.bold)('--replace')} is given. Bytes go straight
211
- into the bucket, they do not pass through the platform.
212
-
213
- Addresses are permanent and belong to the record, not to the bytes: replacing a
214
- file keeps its address, so the links in the code and in the tables of the
215
- application keep working. That is why there is no delete-and-upload-again.
216
-
217
- The address is not public: it is a page of the platform that checks the rights of
218
- the viewer on every request, so a closed project stops serving its pictures too.
219
- Use the address as it came back, in ${(0, ui_1.bold)('<img src>')} and in the data of the application.
220
-
221
- Dot entries and symbolic links are skipped, and ${(0, ui_1.bold)('.xflowignore')} is not read here:
222
- those are the rules for the sources, and a media folder is exactly what one is
223
- asked to put there so it stays out of the archive.
224
-
225
- ${(0, ui_1.bold)('rm')} needs a right of its own (${(0, ui_1.bold)('Delete files')} in the Developers section of the
226
- platform), off by default. The reason is the neighbourhood: whatever the users of
227
- the application uploaded lives in the same folders, and there is no undo. A folder
228
- first answers with the number of files it holds and deletes only on ${(0, ui_1.bold)('--yes')}.
229
-
210
+ storage: `${(0, ui_1.bold)('xflow storage')}: files of the application
211
+
212
+ The sources archive is capped at 10 MB and is rebuilt and re-uploaded on every
213
+ deploy, so photos, video and PDFs do not belong in the repository. File storage
214
+ is the place for them: the files sit beside the versions, survive a build, and
215
+ are metered against the plan of the organization.
216
+
217
+ xflow storage ls [folder] what is there, with addresses and sizes
218
+ xflow storage push ./media --to media
219
+ upload a folder, keeping its structure
220
+ xflow storage rm <address> delete one file
221
+ xflow storage rm --folder photos --yes
222
+ delete a folder with everything in it
223
+
224
+ --to <folder> where to put it in the project storage
225
+ --replace upload files whose size differs from the stored one
226
+ --json machine-readable output, with every address
227
+
228
+ ${(0, ui_1.bold)('push')} sends only what is missing: a file already stored under the same path and
229
+ size is skipped, so running the command again after a broken connection is cheap.
230
+ A file that differs is left alone unless ${(0, ui_1.bold)('--replace')} is given. Bytes go straight
231
+ into the bucket, they do not pass through the platform.
232
+
233
+ Addresses are permanent and belong to the record, not to the bytes: replacing a
234
+ file keeps its address, so the links in the code and in the tables of the
235
+ application keep working. That is why there is no delete-and-upload-again.
236
+
237
+ The address is not public: it is a page of the platform that checks the rights of
238
+ the viewer on every request, so a closed project stops serving its pictures too.
239
+ Use the address as it came back, in ${(0, ui_1.bold)('<img src>')} and in the data of the application.
240
+
241
+ Dot entries and symbolic links are skipped, and ${(0, ui_1.bold)('.xflowignore')} is not read here:
242
+ those are the rules for the sources, and a media folder is exactly what one is
243
+ asked to put there so it stays out of the archive.
244
+
245
+ ${(0, ui_1.bold)('rm')} needs a right of its own (${(0, ui_1.bold)('Delete files')} in the Developers section of the
246
+ platform), off by default. The reason is the neighbourhood: whatever the users of
247
+ the application uploaded lives in the same folders, and there is no undo. A folder
248
+ first answers with the number of files it holds and deletes only on ${(0, ui_1.bold)('--yes')}.
249
+
230
250
  There is no command back: sources return with ${(0, ui_1.bold)('xflow pull')}, files do not.`,
231
- env: `${(0, ui_1.bold)('xflow env')}: environment variables of the functions
232
-
233
- Keys, passwords and third-party addresses are kept by the platform, not by the
234
- repository. A value goes up and never comes back: the only place to see it is inside
235
- the function. So keep your own copy wherever you got it from.
236
-
237
- xflow env what is stored (names, not values)
238
- xflow env check what the functions of this project are missing
239
- xflow env set SMTP_PASSWORD=… store
240
- xflow env rm SMTP_PASSWORD delete
241
-
242
- --scope project visible to this project only, not the whole organization
243
-
244
- ${(0, ui_1.bold)('check')} reads the sources in ${(0, ui_1.bold)('functions/')} and looks for ${(0, ui_1.bold)('process.env.NAME')}
245
- references. The same rule applies on deploy: a function receives only the variables it
246
- mentions by name. A name assembled from an expression (${(0, ui_1.bold)("process.env['KEY_' + n]")}),
247
- reached through ${(0, ui_1.bold)('?.')}, destructured or spread never reaches the environment, so read
248
- variables literally. ${(0, ui_1.bold)('check')} names those reads and the build rejects them: a secret
249
- that quietly fails to arrive is found by the function falling over weeks later.
250
-
251
- The value reaches the function on deploy, not at the moment it is stored: after
252
- ${(0, ui_1.bold)('env set')} run ${(0, ui_1.bold)('xflow deploy')}. The build ships a function whose code did not
253
- change but whose variables did, so nothing is left holding an old value. The same after a
251
+ env: `${(0, ui_1.bold)('xflow env')}: environment variables of the functions
252
+
253
+ Keys, passwords and third-party addresses are kept by the platform, not by the
254
+ repository. A value goes up and never comes back: the only place to see it is inside
255
+ the function. So keep your own copy wherever you got it from.
256
+
257
+ xflow env what is stored (names, not values)
258
+ xflow env check what the functions of this project are missing
259
+ xflow env set SMTP_PASSWORD=… store
260
+ xflow env rm SMTP_PASSWORD delete
261
+
262
+ --scope project visible to this project only, not the whole organization
263
+
264
+ ${(0, ui_1.bold)('check')} reads the sources in ${(0, ui_1.bold)('functions/')} and looks for ${(0, ui_1.bold)('process.env.NAME')}
265
+ references. The same rule applies on deploy: a function receives only the variables it
266
+ mentions by name. A name assembled from an expression (${(0, ui_1.bold)("process.env['KEY_' + n]")}),
267
+ reached through ${(0, ui_1.bold)('?.')}, destructured or spread never reaches the environment, so read
268
+ variables literally. ${(0, ui_1.bold)('check')} names those reads and the build rejects them: a secret
269
+ that quietly fails to arrive is found by the function falling over weeks later.
270
+
271
+ The value reaches the function on deploy, not at the moment it is stored: after
272
+ ${(0, ui_1.bold)('env set')} run ${(0, ui_1.bold)('xflow deploy')}. The build ships a function whose code did not
273
+ change but whose variables did, so nothing is left holding an old value. The same after a
254
274
  delete: a function already deployed keeps the old value until its next deploy.`,
255
- connections: `${(0, ui_1.bold)('xflow connections')}: accounts connected to the organization
256
-
257
- Somebody signs in to Yandex Metrika, Bitrix or a mail service once, in the platform,
258
- and that account becomes a connection of the organization. Linked to a project, it
259
- hands its credentials to the cloud functions as environment variables. Nobody has to
260
- paste a token into the repository, and nobody sees the value: it goes straight from
261
- the platform into the function.
262
-
263
- xflow connections what this project can use, and what it already uses
264
- xflow connections link <name> --as ALIAS
265
- hand its credentials to the functions
266
- xflow connections unlink <ALIAS> take them away again
267
-
268
- Every row says whether the connection is linked to this project (its alias) and what
269
- state the access is in. ${(0, ui_1.bold)('available, not linked')} is the useful one: the account
270
- exists in the organization, but this project gets nothing from it yet: link it.
271
-
272
- Both commands take the name of the connection, the first column of the list, and both
273
- also take its identifier. ${(0, ui_1.bold)('unlink')} takes the alias as well, and prefers it: the
274
- alias is the name your own code already uses. One organization can hold several accounts
275
- of the same service under one name; then the command prints their identifiers and asks
276
- for one of those instead of guessing.
277
-
278
- ${(0, ui_1.bold)('unlink')} refuses while a function still reads one of the variables and names
279
- those functions; ${(0, ui_1.bold)('--force')} goes through anyway.
280
-
281
- Variables are named after the alias: an OAuth connection called ${(0, ui_1.bold)('YANDEX_METRIKA')}
282
- gives ${(0, ui_1.bold)('YANDEX_METRIKA_TOKEN')}, a key-based one gives a variable per field. They
283
- arrive at a function on its next deploy, like every other variable, so after a change
284
- run ${(0, ui_1.bold)('xflow deploy')}. A token close to expiry is renewed by any build on the way;
285
- a revoked one is not renewed by anything until a human reconnects the account.
286
-
287
- Linking needs a right on the key, ${(0, ui_1.bold)('connections:link')}. Keys are issued with it,
288
- and a person can take it away in the platform settings under Developers; a key cannot give
289
- it back to itself. Losing it is what the refusal says, and the answer is to ask a person,
290
- not to look for another route. Either way only the accounts granted to you personally can
291
- be linked at all: that rule holds whatever the key is allowed to do.
292
-
293
- Connecting a new account and switching one off stay with a person, in the platform
275
+ connections: `${(0, ui_1.bold)('xflow connections')}: accounts connected to the organization
276
+
277
+ Somebody signs in to Yandex Metrika, Bitrix or a mail service once, in the platform,
278
+ and that account becomes a connection of the organization. Linked to a project, it
279
+ hands its credentials to the cloud functions as environment variables. Nobody has to
280
+ paste a token into the repository, and nobody sees the value: it goes straight from
281
+ the platform into the function.
282
+
283
+ xflow connections what this project can use, and what it already uses
284
+ xflow connections link <name> --as ALIAS
285
+ hand its credentials to the functions
286
+ xflow connections unlink <ALIAS> take them away again
287
+
288
+ Every row says whether the connection is linked to this project (its alias) and what
289
+ state the access is in. ${(0, ui_1.bold)('available, not linked')} is the useful one: the account
290
+ exists in the organization, but this project gets nothing from it yet: link it.
291
+
292
+ Both commands take the name of the connection, the first column of the list, and both
293
+ also take its identifier. ${(0, ui_1.bold)('unlink')} takes the alias as well, and prefers it: the
294
+ alias is the name your own code already uses. One organization can hold several accounts
295
+ of the same service under one name; then the command prints their identifiers and asks
296
+ for one of those instead of guessing.
297
+
298
+ ${(0, ui_1.bold)('unlink')} refuses while a function still reads one of the variables and names
299
+ those functions; ${(0, ui_1.bold)('--force')} goes through anyway.
300
+
301
+ Variables are named after the alias: an OAuth connection called ${(0, ui_1.bold)('YANDEX_METRIKA')}
302
+ gives ${(0, ui_1.bold)('YANDEX_METRIKA_TOKEN')}, a key-based one gives a variable per field. They
303
+ arrive at a function on its next deploy, like every other variable, so after a change
304
+ run ${(0, ui_1.bold)('xflow deploy')}. A token close to expiry is renewed by any build on the way;
305
+ a revoked one is not renewed by anything until a human reconnects the account.
306
+
307
+ Linking needs a right on the key, ${(0, ui_1.bold)('connections:link')}. Keys are issued with it,
308
+ and a person can take it away in the platform settings under Developers; a key cannot give
309
+ it back to itself. Losing it is what the refusal says, and the answer is to ask a person,
310
+ not to look for another route. Either way only the accounts granted to you personally can
311
+ be linked at all: that rule holds whatever the key is allowed to do.
312
+
313
+ Connecting a new account and switching one off stay with a person, in the platform
294
314
  interface. There is no command for either.`,
295
- schedules: `${(0, ui_1.bold)('xflow schedules')}: running functions on a timer
296
-
297
- A schedule is a Yandex timer trigger: it calls the function itself, with no
298
- application and no browser involved.
299
-
300
- xflow schedules set report "0 3 ? * * *" every day at 03:00
301
- xflow schedules set digest "0 */4 ? * * *" every 4 hours
302
- xflow schedules rm report take off every schedule of a function
303
-
304
- Six fields: ${(0, ui_1.bold)('minutes hours day-of-month month day-of-week year')}. Exactly one of
305
- the day-of-month and day-of-week fields has to be ${(0, ui_1.bold)('?')}, which is not our quirk but
306
- how Yandex works. ${(0, ui_1.bold)('The time is always UTC')}, local time is not understood.
307
-
308
- --payload '{"mode":"full"}' the body the function will receive
309
-
310
- A scheduled run arrives at the handler as a POST with no headers, and the project
311
- token is not checked on it: such an event cannot be forged from outside. The function
312
- has to be deployed already, since the schedule points at it and not the other way round.
313
-
314
- A failed run shows up in ${(0, ui_1.bold)('xflow functions logs')}: nobody is watching a scheduled
315
+ schedules: `${(0, ui_1.bold)('xflow schedules')}: running functions on a timer
316
+
317
+ A schedule is a Yandex timer trigger: it calls the function itself, with no
318
+ application and no browser involved.
319
+
320
+ xflow schedules set report "0 3 ? * * *" every day at 03:00
321
+ xflow schedules set digest "0 */4 ? * * *" every 4 hours
322
+ xflow schedules rm report take off every schedule of a function
323
+
324
+ Six fields: ${(0, ui_1.bold)('minutes hours day-of-month month day-of-week year')}. Exactly one of
325
+ the day-of-month and day-of-week fields has to be ${(0, ui_1.bold)('?')}, which is not our quirk but
326
+ how Yandex works. ${(0, ui_1.bold)('The time is always UTC')}, local time is not understood.
327
+
328
+ --payload '{"mode":"full"}' the body the function will receive
329
+
330
+ A scheduled run arrives at the handler as a POST with no headers, and the project
331
+ token is not checked on it: such an event cannot be forged from outside. The function
332
+ has to be deployed already, since the schedule points at it and not the other way round.
333
+
334
+ A failed run shows up in ${(0, ui_1.bold)('xflow functions logs')}: nobody is watching a scheduled
315
335
  function, so the wrapper reports a crash the same way it does on an ordinary call.`,
316
- invoke: `${(0, ui_1.bold)('xflow functions invoke')} <name>: call a function
317
-
318
- Calls it exactly the way the application does: the project token plus a visitor pass
319
- for the person who owns the key, so the function sees a real caller and its role.
320
- Both are taken from the platform, no local .env is needed.
321
-
322
- --data '{"a":1}' request body (the method becomes POST by default)
323
- --method GET a different method
324
-
325
- Prints the status, the response time and the body. A non-zero exit code on 4xx and
326
- 5xx: in CI such a call has to fail the step. The cause of a crash is shown by
336
+ invoke: `${(0, ui_1.bold)('xflow functions invoke')} <name>: call a function
337
+
338
+ Calls it exactly the way the application does: the project token plus a visitor pass
339
+ for the person who owns the key, so the function sees a real caller and its role.
340
+ Both are taken from the platform, no local .env is needed.
341
+
342
+ --data '{"a":1}' request body (the method becomes POST by default)
343
+ --method GET a different method
344
+
345
+ Prints the status, the response time and the body. A non-zero exit code on 4xx and
346
+ 5xx: in CI such a call has to fail the step. The cause of a crash is shown by
327
347
  ${(0, ui_1.bold)('xflow functions logs <name>')}.`,
328
- mcp: `${(0, ui_1.bold)('xflow mcp install')}: connect the agent to the platform directly
329
-
330
- Writes the MCP server into the configuration of a client that has its own connect
331
- command: today that is Claude Code, and for the rest the command prints what to enter
332
- by hand. After that the agent runs the platform without a terminal: it inspects the
333
- database, applies migrations, ships and calls functions, sets schedules, reads logs.
334
- The commands stay: code, builds and releases are still done by the CLI, because
335
- pushing sources through MCP means filling the agent context and spending your money.
336
-
337
- The command takes the access key from your own login and does not print it: usually
338
- the agent runs this command itself, and everything printed lands in its context and in
339
- the chat history.
340
-
341
- --show-token print the key anyway (not while under an agent)
342
-
343
- There is one key per person per organization and its scope is not narrowed: the agent
344
- sees every project of the organization and names the one it needs. It takes the
348
+ mcp: `${(0, ui_1.bold)('xflow mcp install')}: connect the agent to the platform directly
349
+
350
+ Writes the MCP server into the configuration of a client that has its own connect
351
+ command: today that is Claude Code, and for the rest the command prints what to enter
352
+ by hand. After that the agent runs the platform without a terminal: it inspects the
353
+ database, applies migrations, ships and calls functions, sets schedules, reads logs.
354
+ The commands stay: code, builds and releases are still done by the CLI, because
355
+ pushing sources through MCP means filling the agent context and spending your money.
356
+
357
+ The command takes the access key from your own login and does not print it: usually
358
+ the agent runs this command itself, and everything printed lands in its context and in
359
+ the chat history.
360
+
361
+ --show-token print the key anyway (not while under an agent)
362
+
363
+ There is one key per person per organization and its scope is not narrowed: the agent
364
+ sees every project of the organization and names the one it needs. It takes the
345
365
  project from the ${(0, ui_1.bold)('project_id')} argument, not from your folder, which it cannot see.`,
346
- skills: `${(0, ui_1.bold)('xflow skills')}: platform instructions for an AI agent
347
-
348
- An agent has no way to know about XFlow: the platform is not in its training. The
349
- instructions explain how to release and publish, where the design system components
350
- come from, and where to look for production errors. ${(0, ui_1.bold)('init')} and ${(0, ui_1.bold)('link')} lay them
351
- down on their own for the common agents, so this command exists to pick tools
352
- precisely. Refreshing after a CLI update is not your job any more: the skill and the
353
- CLI ship as one version, and ${(0, ui_1.bold)('xflow update')} rewrites the copies you already have.
354
-
355
- Run in a terminal, it asks two questions: where (this project, or the home folder,
356
- which makes the skill visible in every project) and for which agents. Copies that are
357
- already installed come pre-selected and are updated in place. Without a terminal, in
358
- CI or when an agent runs the command, there are no questions: the defaults plus every
359
- already installed copy are refreshed.
360
-
361
- xflow skills list the agents, their folders and what is installed
362
- --agent claude,cursor exact agents, no questions asked
363
- --global the home folder instead of the project
364
- --yes no questions: defaults plus what is installed
365
- --refresh rewrite the copies that exist, create none
366
-
367
- The format is shared (agentskills.io) and inside a project most tools read the shared
368
- .agents/skills folder, so the paths differ mostly in the home directory. Cursor also
369
- gets .cursor/rules/xflow.mdc: its older versions do not read the shared format.
370
-
371
- Plus a few pointer lines in ${(0, ui_1.bold)('AGENTS.md')}. A skill is picked up lazily, only when its
372
- description matches the task, and "add a customers table" will not trigger it. AGENTS.md
373
- is always read by the agent, which is why the pointer is appended there, at the end of
366
+ skills: `${(0, ui_1.bold)('xflow skills')}: platform instructions for an AI agent
367
+
368
+ An agent has no way to know about XFlow: the platform is not in its training. The
369
+ instructions explain how to release and publish, where the design system components
370
+ come from, and where to look for production errors. ${(0, ui_1.bold)('init')} and ${(0, ui_1.bold)('link')} lay them
371
+ down on their own for the common agents, so this command exists to pick tools
372
+ precisely. Refreshing after a CLI update is not your job any more: the skill and the
373
+ CLI ship as one version, and ${(0, ui_1.bold)('xflow update')} rewrites the copies you already have.
374
+
375
+ Run in a terminal, it asks two questions: where (this project, or the home folder,
376
+ which makes the skill visible in every project) and for which agents. Copies that are
377
+ already installed come pre-selected and are updated in place. Without a terminal, in
378
+ CI or when an agent runs the command, there are no questions: the defaults plus every
379
+ already installed copy are refreshed.
380
+
381
+ xflow skills list the agents, their folders and what is installed
382
+ --agent claude,cursor exact agents, no questions asked
383
+ --global the home folder instead of the project
384
+ --yes no questions: defaults plus what is installed
385
+ --refresh rewrite the copies that exist, create none
386
+
387
+ The format is shared (agentskills.io) and inside a project most tools read the shared
388
+ .agents/skills folder, so the paths differ mostly in the home directory. Cursor also
389
+ gets .cursor/rules/xflow.mdc: its older versions do not read the shared format.
390
+
391
+ Plus a few pointer lines in ${(0, ui_1.bold)('AGENTS.md')}. A skill is picked up lazily, only when its
392
+ description matches the task, and "add a customers table" will not trigger it. AGENTS.md
393
+ is always read by the agent, which is why the pointer is appended there, at the end of
374
394
  the file and once. ${(0, ui_1.bold)('CLAUDE.md')} is left alone: Claude Code reads .claude/skills anyway.`,
375
- pull: `${(0, ui_1.bold)('xflow pull')}: fetch the sources
376
-
377
- By default it fetches the latest revision into the project folder and refuses to write
378
- into a non-empty one: the platform cannot merge changes, that is git's job.
379
-
380
- --into <dir> unpack alongside, to compare
381
- --revision <N> a specific revision (the list: xflow deployments)
395
+ pull: `${(0, ui_1.bold)('xflow pull')}: fetch the sources
396
+
397
+ By default it fetches the latest revision into the project folder and refuses to write
398
+ into a non-empty one: the platform cannot merge changes, that is git's job.
399
+
400
+ --into <dir> unpack alongside, to compare
401
+ --revision <N> a specific revision (the list: xflow deployments)
382
402
  --force overwrite the folder completely`,
383
- deploy: `${(0, ui_1.bold)('xflow deploy')}: build and release
384
-
385
- Three steps: sending the sources, shipping the cloud functions, building the application.
386
- The build command and the output directory come from xflow.json (npm run build and dist
387
- by default).
388
-
389
- --no-push do not send sources, build from the latest server revision
390
- --force allow overwriting the server revision while sending
391
- --allow-removals agree in advance to remove the functions gone from the sources
392
-
393
- The whole working copy goes up at once, as one revision. What is not sent: node_modules,
394
- .git, dist, build, .next, any .env, plus everything listed in .xflowignore and in the
395
- ignore field of xflow.json.
396
-
397
- If the server holds a revision newer than the one you worked from, the sources are
398
- rejected and the build does not start. That means somebody deployed before you: fetch
399
- their changes alongside (${(0, ui_1.bold)('xflow pull --into ./server-copy')}), merge them in git on
400
- your side, and retry. ${(0, ui_1.bold)('--force')} overwrites the server revision, and before that the
401
- CLI shows whose work you are about to destroy and asks for confirmation by typing the
402
- project name. In a non-interactive run (CI, an agent) there is no way to confirm: a
403
- version conflict has to fail the build rather than silently destroy somebody else's work.
404
-
405
- The platform builds, in a clean sandbox on one Node version for everybody, so "it
406
- worked on my machine" no longer depends on your machine. Before the build the project
407
- is checked against the template: mismatches are printed as a list and the build does
408
- not start at all.
409
-
410
- Everything in ${(0, ui_1.bold)('functions/<name>/index.ts')} is bundled and shipped by the same run,
411
- before the application is built. That order is not a convention but the only one that
412
- works: the addresses of the functions are baked into the bundle, so they have to exist
413
- first. A function whose code and variables did not change is left alone, and a function
414
- that fails to ship fails the whole build.
415
-
416
- A function gone from the sources would be deleted from the cloud along with its
417
- schedules, and that is final: one created again later gets a different address. So the
418
- build does not start at all until you say yes. In a terminal the CLI names the functions
419
- and asks; without one (CI, an agent) it stops and ${(0, ui_1.bold)('--allow-removals')} is the only way
420
- to agree. If the sources hold no functions at all while the cloud holds several, nothing
421
- is deleted and nothing is asked: that looks like a directory which never made it (a
422
- ${(0, ui_1.bold)('functions/')} line in .xflowignore) rather than a decision.
423
-
424
- The database is not part of this: migrations change data in ways nothing can undo, so
425
- they stay their own command (${(0, ui_1.bold)('xflow db migrate')}).
426
-
427
- The built version is visible on the project page, and the CLI prints the link. Visitors
428
- see it after ${(0, ui_1.bold)('xflow publish')}. The address of the build itself is not printed:
429
- it carries the version number, and after the next publish such a link quietly serves an
403
+ deploy: `${(0, ui_1.bold)('xflow deploy')}: build and release
404
+
405
+ Three steps: sending the sources, shipping the cloud functions, building the application.
406
+ The build command and the output directory come from xflow.json (npm run build and dist
407
+ by default).
408
+
409
+ --no-push do not send sources, build from the latest server revision
410
+ --force allow overwriting the server revision while sending
411
+ --allow-removals agree in advance to remove the functions gone from the sources
412
+
413
+ The whole working copy goes up at once, as one revision. What is not sent: node_modules,
414
+ .git, dist, build, .next, any .env, plus everything listed in .xflowignore and in the
415
+ ignore field of xflow.json.
416
+
417
+ If the server holds a revision newer than the one you worked from, the sources are
418
+ rejected and the build does not start. That means somebody deployed before you: fetch
419
+ their changes alongside (${(0, ui_1.bold)('xflow pull --into ./server-copy')}), merge them in git on
420
+ your side, and retry. ${(0, ui_1.bold)('--force')} overwrites the server revision, and before that the
421
+ CLI shows whose work you are about to destroy and asks for confirmation by typing the
422
+ project name. In a non-interactive run (CI, an agent) there is no way to confirm: a
423
+ version conflict has to fail the build rather than silently destroy somebody else's work.
424
+
425
+ The platform builds, in a clean sandbox on one Node version for everybody, so "it
426
+ worked on my machine" no longer depends on your machine. Before the build the project
427
+ is checked against the template: mismatches are printed as a list and the build does
428
+ not start at all.
429
+
430
+ Everything in ${(0, ui_1.bold)('functions/<name>/index.ts')} is bundled and shipped by the same run,
431
+ before the application is built. That order is not a convention but the only one that
432
+ works: the addresses of the functions are baked into the bundle, so they have to exist
433
+ first. A function whose code and variables did not change is left alone, and a function
434
+ that fails to ship fails the whole build.
435
+
436
+ A function gone from the sources would be deleted from the cloud along with its
437
+ schedules, and that is final: one created again later gets a different address. So the
438
+ build does not start at all until you say yes. In a terminal the CLI names the functions
439
+ and asks; without one (CI, an agent) it stops and ${(0, ui_1.bold)('--allow-removals')} is the only way
440
+ to agree. If the sources hold no functions at all while the cloud holds several, nothing
441
+ is deleted and nothing is asked: that looks like a directory which never made it (a
442
+ ${(0, ui_1.bold)('functions/')} line in .xflowignore) rather than a decision.
443
+
444
+ The database is not part of this: migrations change data in ways nothing can undo, so
445
+ they stay their own command (${(0, ui_1.bold)('xflow db migrate')}).
446
+
447
+ The built version is visible on the project page, and the CLI prints the link. Visitors
448
+ see it after ${(0, ui_1.bold)('xflow publish')}. The address of the build itself is not printed:
449
+ it carries the version number, and after the next publish such a link quietly serves an
430
450
  old copy.`,
431
- init: `${(0, ui_1.bold)('xflow init')} [dir]: a new project
432
-
433
- Lays down the platform template: React on Vite, Tailwind, a set of components
434
- (${(0, ui_1.bold)('src/components/ui')}) and ready-made blocks (${(0, ui_1.bold)('src/components/blocks')}): table,
435
- form, filters, kanban, charts. Projects created in the web get the same template, which
436
- is what makes the applications look alike.
437
-
438
- Building the interface on top of these components and the tokens in ${(0, ui_1.bold)('src/index.css')}
439
- is not a formality: your own palette on top of them looks foreign inside the platform.
440
-
441
- --name <name> project name (the folder name by default)
442
- --template <id> a different template (the list: xflow templates)
443
- --database <id> attach a logical database of the organization
444
-
445
- The order: the project is created on the platform first, because without it there is
446
- nowhere to get the token for .env. If writing the files fails, the project stays empty
451
+ init: `${(0, ui_1.bold)('xflow init')} [dir]: a new project
452
+
453
+ Lays down the platform template: React on Vite, Tailwind, a set of components
454
+ (${(0, ui_1.bold)('src/components/ui')}) and ready-made blocks (${(0, ui_1.bold)('src/components/blocks')}): table,
455
+ form, filters, kanban, charts. Projects created in the web get the same template, which
456
+ is what makes the applications look alike.
457
+
458
+ Building the interface on top of these components and the tokens in ${(0, ui_1.bold)('src/index.css')}
459
+ is not a formality: your own palette on top of them looks foreign inside the platform.
460
+
461
+ --name <name> project name (the folder name by default)
462
+ --template <id> a different template (the list: xflow templates)
463
+ --database <id> attach a logical database of the organization
464
+
465
+ The order: the project is created on the platform first, because without it there is
466
+ nowhere to get the token for .env. If writing the files fails, the project stays empty
447
467
  and the CLI explains how to pick it up with the link command.`,
448
- rollback: `${(0, ui_1.bold)('xflow rollback')} <version number>: serve the pages of an earlier build
449
-
450
- Points the project at the pages of that build. ${(0, ui_1.bold)('Only the pages come back.')} Cloud
451
- functions and the database are one per project: they are not versioned, they are shared
452
- with the published application, and they stay exactly as they are now. A page from August
453
- will be talking to today's functions.
454
-
455
- The sources stay at their own revision too. Fetching the code of that build is a separate
456
- command (${(0, ui_1.bold)('xflow pull --revision N --into ../old-version')}, keep the copy outside the
457
- project folder). Building that code over the current one is a further, deliberate step: it
458
- replaces the server revision, and whatever is newer on the platform goes away.
459
-
468
+ rollback: `${(0, ui_1.bold)('xflow rollback')} <version number>: serve the pages of an earlier build
469
+
470
+ Points the project at the pages of that build. ${(0, ui_1.bold)('Only the pages come back.')} Cloud
471
+ functions and the database are one per project: they are not versioned, they are shared
472
+ with the published application, and they stay exactly as they are now. A page from August
473
+ will be talking to today's functions.
474
+
475
+ The sources stay at their own revision too. Fetching the code of that build is a separate
476
+ command (${(0, ui_1.bold)('xflow pull --revision N --into ../old-version')}, keep the copy outside the
477
+ project folder). Building that code over the current one is a further, deliberate step: it
478
+ replaces the server revision, and whatever is newer on the platform goes away.
479
+
460
480
  Visitors keep seeing the published pages until ${(0, ui_1.bold)('xflow publish')} is run.`,
461
481
  };