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