@getxflow/cli 0.6.5 → 0.7.0

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