@getxflow/cli 0.1.9 → 0.2.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
@@ -3,267 +3,280 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.help = help;
4
4
  const ui_1 = require("./ui");
5
5
  const version_1 = require("./version");
6
- /**
7
- * Справка. Заменяет агенту машинную документацию: у CLI её роль играет `--help`,
8
- * поэтому текст пишем так, чтобы по нему можно было работать, не открывая сайт.
9
- */
6
+ /** Help text. For agents this is the reference documentation. */
10
7
  function help(topic) {
11
8
  if (topic && TOPICS[topic]) {
12
9
  (0, ui_1.out)(TOPICS[topic]);
13
10
  return;
14
11
  }
15
- (0, ui_1.out)(`${(0, ui_1.bold)('xflow')} ${(0, ui_1.dim)(version_1.CLI_VERSION)} XFlow application hosting
16
-
17
- ${(0, ui_1.bold)('Getting started')}
18
- xflow login sign in through the browser
19
- xflow init [dir] new project from the platform template
20
- xflow templates which templates are available
21
- xflow link <id> link this folder to a project (id: xflow projects list)
22
- xflow skills [--global] refresh the platform instructions for an AI agent
23
- xflow mcp install give the agent platform access without a terminal
24
-
25
- ${(0, ui_1.bold)('Code')}
26
- xflow status what is on the server and how the local copy differs
27
- xflow push [--force] send the sources as a new revision
28
- xflow pull [--into dir] [--revision N]
29
- fetch the sources (the latest revision by default)
30
-
31
- ${(0, ui_1.bold)('Releasing')}
32
- xflow deploy [--no-push] send the code, build on the platform, release to dev
33
- xflow publish show the dev version to visitors
34
- xflow rollback <version number> return the project to an earlier version
35
- xflow deployments version history
36
-
37
- ${(0, ui_1.bold)('Functions')}
38
- xflow functions list what is deployed
39
- xflow functions deploy [name] build and ship a function
40
- xflow functions invoke <name> [--data '{"a":1}']
41
- call a function and print the answer
42
- xflow functions logs [name] function crashes: stack and console output
43
- xflow schedules list what runs on a timer
44
- xflow schedules set <name> "0 3 ? * * *"
45
- run a function on a schedule (UTC)
46
- xflow schedules rm <name> take a schedule off
47
- xflow env [check] function variables: what is stored, what is missing
48
- xflow env set NAME=value store a variable
49
- xflow env rm NAME delete a variable
50
-
51
- ${(0, ui_1.bold)('Database')}
52
- xflow db status which migrations are applied and which are waiting
53
- xflow db migrate [--dry-run] [--allow-destructive]
54
- apply the migrations from migrations/*.sql
55
-
56
- ${(0, ui_1.bold)('Reference')}
57
- xflow logs [--limit N] browser errors from the released application
58
- xflow projects list projects of the organization
59
- xflow projects get [id] project card
60
- xflow whoami whose key this is and what it can do
61
- xflow logout forget the key
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 [--global] refresh the platform instructions for an AI agent
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
+
47
+ ${(0, ui_1.bold)('Database')}
48
+ xflow db status which migrations are applied and which are waiting
49
+ xflow db migrate [--dry-run] [--allow-destructive]
50
+ apply the migrations from migrations/*.sql
51
+
52
+ ${(0, ui_1.bold)('Reference')}
53
+ xflow logs [--limit N] browser errors from the released application
54
+ xflow projects list projects of the organization
55
+ xflow projects get [id] project card
56
+ xflow whoami whose key this is and what it can do
57
+ xflow logout forget the key
58
+
59
+ ${(0, ui_1.bold)('Environment')}
60
+ XFLOW_TOKEN access key (for CI, instead of xflow login)
61
+ XFLOW_API_URL platform address when it is not app.getxflow.com
62
+
67
63
  More about one command: xflow help <command>`);
68
64
  }
69
65
  const TOPICS = {
70
- db: `${(0, ui_1.bold)('xflow db migrate')}: apply migrations
71
-
72
- The files live in the repository: ${(0, ui_1.bold)('migrations/0001_init.sql')}, ${(0, ui_1.bold)('migrations/0002_orders.sql')}
73
- and so on. The order comes from the file name, which is why the leading number is
74
- required. Every migration runs in its own transaction, the history is kept in the
75
- database itself, and anything already applied is not run again.
76
-
77
- --dry-run report what would be applied without touching the database
78
- --allow-destructive allow operations that destroy data
79
-
80
- About destructive ones. The platform keeps no database history and makes no backups,
81
- 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
82
- condition are rejected unless the flag is given. With the flag, the contents of the
83
- affected tables are dumped and kept for 7 days: that is a "caught it right away"
84
- safety net, not a backup. ${(0, ui_1.bold)('DROP DATABASE')} is never allowed, because the database
85
- is shared across the organization.
86
-
87
- Editing an already applied file achieves nothing: the comparison is by name, not by
88
- content. ${(0, ui_1.bold)('xflow db status')} lists such a file separately, so write a new migration
89
- instead.
90
-
91
- One logical database can be attached to several projects, so the history may hold
92
- migrations that are not in your repository. That is normal, but it also means your
66
+ db: `${(0, ui_1.bold)('xflow db migrate')}: apply migrations
67
+
68
+ The files live in the repository: ${(0, ui_1.bold)('migrations/0001_init.sql')}, ${(0, ui_1.bold)('migrations/0002_orders.sql')}
69
+ and so on. The order comes from the file name, which is why the leading number is
70
+ required. Every migration runs in its own transaction, the history is kept in the
71
+ database itself, and anything already applied is not run again.
72
+
73
+ --dry-run report what would be applied without touching the database
74
+ --allow-destructive allow operations that destroy data
75
+
76
+ About destructive ones. The platform keeps no database history and makes no backups,
77
+ 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
78
+ condition are rejected unless the flag is given. With the flag, the contents of the
79
+ affected tables are dumped and kept for 7 days: that is a "caught it right away"
80
+ safety net, not a backup. ${(0, ui_1.bold)('DROP DATABASE')} is never allowed, because the database
81
+ is shared across the organization.
82
+
83
+ Editing an already applied file achieves nothing: the comparison is by name, not by
84
+ content. ${(0, ui_1.bold)('xflow db status')} lists such a file separately, so write a new migration
85
+ instead.
86
+
87
+ One logical database can be attached to several projects, so the history may hold
88
+ migrations that are not in your repository. That is normal, but it also means your
93
89
  migration can break somebody else's application.`,
94
- logs: `${(0, ui_1.bold)('xflow logs')} and ${(0, ui_1.bold)('xflow functions logs')}: project errors
95
-
96
- One stream per project: crashes of the released application in the browser and
97
- crashes of the cloud functions. The last 200 entries are kept, older ones fall off.
98
-
99
- --limit N how many to show (10 by default)
100
-
101
- A function reports its own crashes: Yandex exposes its logs only over gRPC, so the
102
- wrapper catches the exception, carries the tail of the console with it (the last 40
103
- ${(0, ui_1.bold)('console.log')} lines of that call) and sends it to the platform. Successful calls
104
- write nothing, otherwise every request would pay for it in latency.
105
-
106
- What never reaches this list: a crash while the module is starting (the function
107
- never gets as far as the wrapper), going over 90 seconds, and running out of memory.
108
- Those show up in the answer to ${(0, ui_1.bold)('xflow functions invoke')}.
109
-
110
- Browser errors are collected by ${(0, ui_1.bold)('src/utils/error-logger.ts')} of the template and
90
+ logs: `${(0, ui_1.bold)('xflow logs')} and ${(0, ui_1.bold)('xflow functions logs')}: project errors
91
+
92
+ One stream per project: crashes of the released application in the browser and
93
+ crashes of the cloud functions. The last 200 entries are kept, older ones fall off.
94
+
95
+ --limit N how many to show (10 by default)
96
+
97
+ A function reports its own crashes: Yandex exposes its logs only over gRPC, so the
98
+ wrapper catches the exception, carries the tail of the console with it (the last 40
99
+ ${(0, ui_1.bold)('console.log')} lines of that call) and sends it to the platform. Successful calls
100
+ write nothing, otherwise every request would pay for it in latency.
101
+
102
+ What never reaches this list: a crash while the module is starting (the function
103
+ never gets as far as the wrapper), going over 90 seconds, and running out of memory.
104
+ Those show up in the answer to ${(0, ui_1.bold)('xflow functions invoke')}.
105
+
106
+ Browser errors are collected by ${(0, ui_1.bold)('src/utils/error-logger.ts')} of the template and
111
107
  only from released addresses: a local ${(0, ui_1.bold)('npm run dev')} writes nothing here.`,
112
- env: `${(0, ui_1.bold)('xflow env')}: environment variables of the functions
113
-
114
- Keys, passwords and third-party addresses are kept by the platform, not by the
115
- repository. A value goes up and never comes back: the only place to see it is inside
116
- the function. So keep your own copy wherever you got it from.
117
-
118
- xflow env what is stored (names, not values)
119
- xflow env check what the functions of this project are missing
120
- xflow env set SMTP_PASSWORD=… store
121
- xflow env rm SMTP_PASSWORD delete
122
-
123
- --scope project visible to this project only, not the whole organization
124
-
125
- ${(0, ui_1.bold)('check')} reads the sources in ${(0, ui_1.bold)('functions/')} and looks for ${(0, ui_1.bold)('process.env.NAME')}
126
- references. The same rule applies on deploy: a function receives only the variables it
127
- mentions by name. A name assembled from an expression (${(0, ui_1.bold)("process.env['KEY_' + n]")})
128
- never reaches the environment, so read variables literally.
129
-
130
- The value reaches the function on deploy, not at the moment it is stored: after
131
- ${(0, ui_1.bold)('env set')} you need ${(0, ui_1.bold)('xflow functions deploy')} for the functions involved. The same
132
- after a delete: a function already deployed keeps the old value until its next deploy.`,
133
- schedules: `${(0, ui_1.bold)('xflow schedules')}: running functions on a timer
134
-
135
- A schedule is a Yandex timer trigger: it calls the function itself, with no
136
- application and no browser involved.
137
-
138
- xflow schedules set report "0 3 ? * * *" every day at 03:00
139
- xflow schedules set digest "0 */4 ? * * *" every 4 hours
140
- xflow schedules rm report take off every schedule of a function
141
-
142
- Six fields: ${(0, ui_1.bold)('minutes hours day-of-month month day-of-week year')}. Exactly one of
143
- the day-of-month and day-of-week fields has to be ${(0, ui_1.bold)('?')}, which is not our quirk but
144
- how Yandex works. ${(0, ui_1.bold)('The time is always UTC')}, local time is not understood.
145
-
146
- --payload '{"mode":"full"}' the body the function will receive
147
-
148
- A scheduled run arrives at the handler as a POST with no headers, and the project
149
- token is not checked on it: such an event cannot be forged from outside. The function
150
- has to be deployed already, since the schedule points at it and not the other way round.
151
-
152
- A failed run shows up in ${(0, ui_1.bold)('xflow functions logs')}: nobody is watching a scheduled
108
+ env: `${(0, ui_1.bold)('xflow env')}: environment variables of the functions
109
+
110
+ Keys, passwords and third-party addresses are kept by the platform, not by the
111
+ repository. A value goes up and never comes back: the only place to see it is inside
112
+ the function. So keep your own copy wherever you got it from.
113
+
114
+ xflow env what is stored (names, not values)
115
+ xflow env check what the functions of this project are missing
116
+ xflow env set SMTP_PASSWORD=… store
117
+ xflow env rm SMTP_PASSWORD delete
118
+
119
+ --scope project visible to this project only, not the whole organization
120
+
121
+ ${(0, ui_1.bold)('check')} reads the sources in ${(0, ui_1.bold)('functions/')} and looks for ${(0, ui_1.bold)('process.env.NAME')}
122
+ references. The same rule applies on deploy: a function receives only the variables it
123
+ mentions by name. A name assembled from an expression (${(0, ui_1.bold)("process.env['KEY_' + n]")})
124
+ never reaches the environment, so read variables literally.
125
+
126
+ The value reaches the function on deploy, not at the moment it is stored: after
127
+ ${(0, ui_1.bold)('env set')} run ${(0, ui_1.bold)('xflow deploy')}. The build ships a function whose code did not
128
+ change but whose variables did, so nothing is left holding an old value. The same after a
129
+ delete: a function already deployed keeps the old value until its next deploy.`,
130
+ schedules: `${(0, ui_1.bold)('xflow schedules')}: running functions on a timer
131
+
132
+ A schedule is a Yandex timer trigger: it calls the function itself, with no
133
+ application and no browser involved.
134
+
135
+ xflow schedules set report "0 3 ? * * *" every day at 03:00
136
+ xflow schedules set digest "0 */4 ? * * *" every 4 hours
137
+ xflow schedules rm report take off every schedule of a function
138
+
139
+ Six fields: ${(0, ui_1.bold)('minutes hours day-of-month month day-of-week year')}. Exactly one of
140
+ the day-of-month and day-of-week fields has to be ${(0, ui_1.bold)('?')}, which is not our quirk but
141
+ how Yandex works. ${(0, ui_1.bold)('The time is always UTC')}, local time is not understood.
142
+
143
+ --payload '{"mode":"full"}' the body the function will receive
144
+
145
+ A scheduled run arrives at the handler as a POST with no headers, and the project
146
+ token is not checked on it: such an event cannot be forged from outside. The function
147
+ has to be deployed already, since the schedule points at it and not the other way round.
148
+
149
+ A failed run shows up in ${(0, ui_1.bold)('xflow functions logs')}: nobody is watching a scheduled
153
150
  function, so the wrapper reports a crash the same way it does on an ordinary call.`,
154
- invoke: `${(0, ui_1.bold)('xflow functions invoke')} <name>: call a function
155
-
156
- Calls it exactly the way the application does, with the X-Project-Token header. The
157
- token is taken from the project card on the platform, no local .env is needed.
158
-
159
- --data '{"a":1}' request body (the method becomes POST by default)
160
- --method GET a different method
161
-
162
- Prints the status, the response time and the body. A non-zero exit code on 4xx and
163
- 5xx: in CI such a call has to fail the step. The cause of a crash is shown by
151
+ invoke: `${(0, ui_1.bold)('xflow functions invoke')} <name>: call a function
152
+
153
+ Calls it exactly the way the application does, with the X-Project-Token header. The
154
+ token is taken from the project card on the platform, no local .env is needed.
155
+
156
+ --data '{"a":1}' request body (the method becomes POST by default)
157
+ --method GET a different method
158
+
159
+ Prints the status, the response time and the body. A non-zero exit code on 4xx and
160
+ 5xx: in CI such a call has to fail the step. The cause of a crash is shown by
164
161
  ${(0, ui_1.bold)('xflow functions logs <name>')}.`,
165
- mcp: `${(0, ui_1.bold)('xflow mcp install')}: connect the agent to the platform directly
166
-
167
- Writes the MCP server into the configuration of a client that has its own connect
168
- command: today that is Claude Code, and for the rest the command prints what to enter
169
- by hand. After that the agent runs the platform without a terminal: it inspects the
170
- database, applies migrations, ships and calls functions, sets schedules, reads logs.
171
- The commands stay: code, builds and releases are still done by the CLI, because
172
- pushing sources through MCP means filling the agent context and spending your money.
173
-
174
- The command takes the access key from your own login and does not print it: usually
175
- the agent runs this command itself, and everything printed lands in its context and in
176
- the chat history.
177
-
178
- --show-token print the key anyway (not while under an agent)
179
-
180
- There is one key per person per organization and its scope is not narrowed: the agent
181
- sees every project of the organization and names the one it needs. It takes the
162
+ mcp: `${(0, ui_1.bold)('xflow mcp install')}: connect the agent to the platform directly
163
+
164
+ Writes the MCP server into the configuration of a client that has its own connect
165
+ command: today that is Claude Code, and for the rest the command prints what to enter
166
+ by hand. After that the agent runs the platform without a terminal: it inspects the
167
+ database, applies migrations, ships and calls functions, sets schedules, reads logs.
168
+ The commands stay: code, builds and releases are still done by the CLI, because
169
+ pushing sources through MCP means filling the agent context and spending your money.
170
+
171
+ The command takes the access key from your own login and does not print it: usually
172
+ the agent runs this command itself, and everything printed lands in its context and in
173
+ the chat history.
174
+
175
+ --show-token print the key anyway (not while under an agent)
176
+
177
+ There is one key per person per organization and its scope is not narrowed: the agent
178
+ sees every project of the organization and names the one it needs. It takes the
182
179
  project from the ${(0, ui_1.bold)('project_id')} argument, not from your folder, which it cannot see.`,
183
- skills: `${(0, ui_1.bold)('xflow skills')}: platform instructions for an AI agent
184
-
185
- An agent has no way to know about XFlow: the platform is not in its training. The
186
- instructions explain how to release and publish, where the design system components
187
- come from, and where to look for production errors. ${(0, ui_1.bold)('init')} and ${(0, ui_1.bold)('link')} lay them
188
- down on their own, so this command is for refreshing them after a CLI update: the two
189
- ship as one version.
190
-
191
- The format is shared (agentskills.io) but the folders differ per tool, so we write all:
192
-
193
- .claude/skills/xflow/SKILL.md Claude Code
194
- .agents/skills/xflow/SKILL.md Codex, OpenClaw
195
- .cursor/rules/xflow.mdc Cursor: it does not read the shared format
196
-
197
- --global the same in the home folder, which makes the skill visible in every
198
- project. There ~/.codex/skills is added as well, because Codex looks in
199
- its shared catalog only inside a repository. The Cursor rule and
200
- AGENTS.md stay per project, they have no global counterpart
201
-
202
- Plus a few pointer lines in ${(0, ui_1.bold)('AGENTS.md')}. A skill is picked up lazily, only when its
203
- description matches the task, and "add a customers table" will not trigger it. AGENTS.md
204
- is always read by the agent, which is why the pointer is appended there, at the end of
180
+ skills: `${(0, ui_1.bold)('xflow skills')}: platform instructions for an AI agent
181
+
182
+ An agent has no way to know about XFlow: the platform is not in its training. The
183
+ instructions explain how to release and publish, where the design system components
184
+ come from, and where to look for production errors. ${(0, ui_1.bold)('init')} and ${(0, ui_1.bold)('link')} lay them
185
+ down on their own, so this command is for refreshing them after a CLI update: the two
186
+ ship as one version.
187
+
188
+ The format is shared (agentskills.io) but the folders differ per tool, so we write all:
189
+
190
+ .claude/skills/xflow/SKILL.md Claude Code
191
+ .agents/skills/xflow/SKILL.md Codex, OpenClaw
192
+ .cursor/rules/xflow.mdc Cursor: it does not read the shared format
193
+
194
+ --global the same in the home folder, which makes the skill visible in every
195
+ project. There ~/.codex/skills is added as well, because Codex looks in
196
+ its shared catalog only inside a repository. The Cursor rule and
197
+ AGENTS.md stay per project, they have no global counterpart
198
+
199
+ Plus a few pointer lines in ${(0, ui_1.bold)('AGENTS.md')}. A skill is picked up lazily, only when its
200
+ description matches the task, and "add a customers table" will not trigger it. AGENTS.md
201
+ is always read by the agent, which is why the pointer is appended there, at the end of
205
202
  the file and once. ${(0, ui_1.bold)('CLAUDE.md')} is left alone: Claude Code reads .claude/skills anyway.`,
206
- push: `${(0, ui_1.bold)('xflow push')}: send the sources
207
-
208
- The whole working copy goes up at once, as one revision. What is not sent:
209
- node_modules, .git, dist, build, .next, any .env, plus everything listed in
210
- .xflowignore and in the ignore field of xflow.json.
211
-
212
- If the server holds a revision newer than the one you worked from, the push is
213
- rejected. That means somebody pushed before you: fetch their changes alongside
214
- (${(0, ui_1.bold)('xflow pull --into ./server-copy')}), merge them in git on your side, and retry.
215
-
216
- --force overwrites the server revision. Before that the CLI shows whose work you are
217
- about to destroy and asks for confirmation by typing the project name. In a
218
- non-interactive run (CI, an agent) there is no way to confirm: a version conflict has
203
+ push: `${(0, ui_1.bold)('xflow push')}: send the sources
204
+
205
+ The whole working copy goes up at once, as one revision. What is not sent:
206
+ node_modules, .git, dist, build, .next, any .env, plus everything listed in
207
+ .xflowignore and in the ignore field of xflow.json.
208
+
209
+ If the server holds a revision newer than the one you worked from, the push is
210
+ rejected. That means somebody pushed before you: fetch their changes alongside
211
+ (${(0, ui_1.bold)('xflow pull --into ./server-copy')}), merge them in git on your side, and retry.
212
+
213
+ --force overwrites the server revision. Before that the CLI shows whose work you are
214
+ about to destroy and asks for confirmation by typing the project name. In a
215
+ non-interactive run (CI, an agent) there is no way to confirm: a version conflict has
219
216
  to fail the build rather than silently destroy somebody else's work.`,
220
- pull: `${(0, ui_1.bold)('xflow pull')}: fetch the sources
221
-
222
- By default it fetches the latest revision into the project folder and refuses to write
223
- into a non-empty one: the platform cannot merge changes, that is git's job.
224
-
225
- --into <dir> unpack alongside, to compare
226
- --revision <N> a specific revision (the list: xflow deployments)
217
+ pull: `${(0, ui_1.bold)('xflow pull')}: fetch the sources
218
+
219
+ By default it fetches the latest revision into the project folder and refuses to write
220
+ into a non-empty one: the platform cannot merge changes, that is git's job.
221
+
222
+ --into <dir> unpack alongside, to compare
223
+ --revision <N> a specific revision (the list: xflow deployments)
227
224
  --force overwrite the folder completely`,
228
- deploy: `${(0, ui_1.bold)('xflow deploy')}: build and release
229
-
230
- Two steps: sending the sources and building on the platform. The build command and the
231
- output directory come from xflow.json (npm run build and dist by default).
232
-
233
- --no-push do not send sources, build from the latest server revision
234
- --force allow overwriting the server revision while sending
235
-
236
- The platform builds, in a clean sandbox on one Node version for everybody, so "it
237
- worked on my machine" no longer depends on your machine. Before the build the project
238
- is checked against the template: mismatches are printed as a list and the build does
239
- not start at all.
240
-
241
- The built version is visible on the project page, and the CLI prints the link. Visitors
242
- see it after ${(0, ui_1.bold)('xflow publish')}. The address of the build itself is not printed:
243
- it carries the version number, and after the next publish such a link quietly serves an
225
+ deploy: `${(0, ui_1.bold)('xflow deploy')}: build and release
226
+
227
+ Three steps: sending the sources, shipping the cloud functions, building the application.
228
+ The build command and the output directory come from xflow.json (npm run build and dist
229
+ by default).
230
+
231
+ --no-push do not send sources, build from the latest server revision
232
+ --force allow overwriting the server revision while sending
233
+
234
+ The platform builds, in a clean sandbox on one Node version for everybody, so "it
235
+ worked on my machine" no longer depends on your machine. Before the build the project
236
+ is checked against the template: mismatches are printed as a list and the build does
237
+ not start at all.
238
+
239
+ Everything in ${(0, ui_1.bold)('functions/<name>/index.ts')} is bundled and shipped by the same run,
240
+ before the application is built. That order is not a convention but the only one that
241
+ works: the addresses of the functions are baked into the bundle, so they have to exist
242
+ first. A function whose code and variables did not change is left alone, and a function
243
+ that fails to ship fails the whole build.
244
+
245
+ A function gone from the sources is deleted from the cloud along with its schedules, and
246
+ the CLI names it before the build starts. That one is final: a function created again
247
+ later gets a different address. If the sources hold no functions at all while the cloud
248
+ holds several, nothing is deleted: that looks like a directory which never made it (a
249
+ ${(0, ui_1.bold)('functions/')} line in .xflowignore) rather than a decision.
250
+
251
+ The database is not part of this: migrations change data in ways nothing can undo, so
252
+ they stay their own command (${(0, ui_1.bold)('xflow db migrate')}).
253
+
254
+ The built version is visible on the project page, and the CLI prints the link. Visitors
255
+ see it after ${(0, ui_1.bold)('xflow publish')}. The address of the build itself is not printed:
256
+ it carries the version number, and after the next publish such a link quietly serves an
244
257
  old copy.`,
245
- init: `${(0, ui_1.bold)('xflow init')} [dir]: a new project
246
-
247
- Lays down the platform template: React on Vite, Tailwind, a set of components
248
- (${(0, ui_1.bold)('src/components/ui')}) and ready-made blocks (${(0, ui_1.bold)('src/components/blocks')}): table,
249
- form, filters, kanban, charts. Projects created in the web get the same template, which
250
- is what makes the applications look alike.
251
-
252
- Building the interface on top of these components and the tokens in ${(0, ui_1.bold)('src/index.css')}
253
- is not a formality: your own palette on top of them looks foreign inside the platform.
254
-
255
- --name <name> project name (the folder name by default)
256
- --template <id> a different template (the list: xflow templates)
257
- --database <id> attach a logical database of the organization
258
-
259
- The order: the project is created on the platform first, because without it there is
260
- nowhere to get the token for .env. If writing the files fails, the project stays empty
258
+ init: `${(0, ui_1.bold)('xflow init')} [dir]: a new project
259
+
260
+ Lays down the platform template: React on Vite, Tailwind, a set of components
261
+ (${(0, ui_1.bold)('src/components/ui')}) and ready-made blocks (${(0, ui_1.bold)('src/components/blocks')}): table,
262
+ form, filters, kanban, charts. Projects created in the web get the same template, which
263
+ is what makes the applications look alike.
264
+
265
+ Building the interface on top of these components and the tokens in ${(0, ui_1.bold)('src/index.css')}
266
+ is not a formality: your own palette on top of them looks foreign inside the platform.
267
+
268
+ --name <name> project name (the folder name by default)
269
+ --template <id> a different template (the list: xflow templates)
270
+ --database <id> attach a logical database of the organization
271
+
272
+ The order: the project is created on the platform first, because without it there is
273
+ nowhere to get the token for .env. If writing the files fails, the project stays empty
261
274
  and the CLI explains how to pick it up with the link command.`,
262
- rollback: `${(0, ui_1.bold)('xflow rollback')} <version number>: return an earlier build
263
-
264
- Switches the project to the chosen build. Only the build comes back: the sources stay
265
- at their own revision, and returning them is a separate decision
266
- (${(0, ui_1.bold)('xflow pull --revision N --into ./old-version')}).
267
-
275
+ rollback: `${(0, ui_1.bold)('xflow rollback')} <version number>: return an earlier build
276
+
277
+ Switches the project to the chosen build. Only the build comes back: the sources stay
278
+ at their own revision, and returning them is a separate decision
279
+ (${(0, ui_1.bold)('xflow pull --revision N --into ./old-version')}).
280
+
268
281
  Visitors keep seeing the published version until ${(0, ui_1.bold)('xflow publish')} is run.`,
269
282
  };
package/dist/limits.js CHANGED
@@ -1,15 +1,8 @@
1
1
  "use strict";
2
- /**
3
- * Лимиты тарифа в выводе CLI.
4
- *
5
- * Два места, где они видны: `xflow whoami` (что есть) и отказ команды (во что
6
- * упёрлись). Второе важнее: без явной пометки агент читает 403 как ошибку
7
- * запроса и уходит переписывать команду, которая была верной.
8
- */
2
+ /** Plan limits in CLI output: whoami and command refusals. */
9
3
  Object.defineProperty(exports, "__esModule", { value: true });
10
4
  exports.limitLine = limitLine;
11
5
  exports.quotaRows = quotaRows;
12
- /** Что кончилось, по коду отказа гейта. */
13
6
  const DENIAL_LABELS = {
14
7
  limit_projects: 'projects',
15
8
  limit_functions: 'cloud functions',
@@ -21,7 +14,7 @@ const DENIAL_LABELS = {
21
14
  subscription_blocked: 'the subscription is unpaid',
22
15
  billing_unavailable: 'plan check',
23
16
  };
24
- /** Строка под сообщением отказа: почему повторять команду бесполезно. */
17
+ /** Why repeating the same command will not help. */
25
18
  function limitLine(detail) {
26
19
  const label = DENIAL_LABELS[detail.code] ?? detail.code;
27
20
  const numbers = detail.used !== undefined && detail.limit !== undefined
@@ -29,7 +22,6 @@ function limitLine(detail) {
29
22
  : '';
30
23
  return `Plan limit: ${label}${numbers}. Repeating the same command will not help.`;
31
24
  }
32
- /** Порядок строк в whoami: сперва штучное, потом объёмы, потом расход месяца. */
33
25
  const QUOTA_ORDER = [
34
26
  'projects',
35
27
  'functions',
@@ -50,9 +42,7 @@ const QUOTA_LABELS = {
50
42
  function_minutes: 'Function minutes',
51
43
  builds: 'Builds this month',
52
44
  };
53
- /** Гигабайт десятичный, 1000 МБ: так объём и продаётся, шаг докупки 500 МБ
54
- * обязан читаться как полгигабайта. Сравнение с байтами живёт на сервере и
55
- * остаётся на 1024². */
45
+ /** Decimal units, 1000 MB per GB: matches how volumes are sold. */
56
46
  function sizeLabel(mb) {
57
47
  if (mb < 1000)
58
48
  return `${Math.round(mb)} MB`;
@@ -60,14 +50,14 @@ function sizeLabel(mb) {
60
50
  return `${Number.isInteger(gb) ? String(gb) : gb.toFixed(1)} GB`;
61
51
  }
62
52
  function quotaValue(key, use) {
63
- // Обе стороны в одних единицах: «0.2 of 15 GB» читается, «245 of 15360» нет.
53
+ // Same unit on both sides: "0.2 of 15 GB", not "245 of 15360".
64
54
  if (key.endsWith('_mb')) {
65
55
  const used = use.limit >= 1000 ? (use.used / 1000).toFixed(1) : String(use.used);
66
56
  return `${used} of ${sizeLabel(use.limit)}`;
67
57
  }
68
58
  return `${use.used} of ${use.limit}`;
69
59
  }
70
- /** Строки таблицы для whoami. Незнакомые ключи не прячем: сервер мог добавить. */
60
+ /** Unknown keys stay visible: the server may add new ones. */
71
61
  function quotaRows(quotas) {
72
62
  const known = QUOTA_ORDER.filter((key) => quotas[key]);
73
63
  const rest = Object.keys(quotas).filter((key) => !QUOTA_ORDER.includes(key));
package/dist/session.js CHANGED
@@ -5,22 +5,18 @@ exports.anonymous = anonymous;
5
5
  const config_1 = require("./config");
6
6
  const credentials_1 = require("./credentials");
7
7
  const errors_1 = require("./errors");
8
- /**
9
- * Соединение с платформой: адрес плюс ключ.
10
- *
11
- * Адрес берётся из XFLOW_API_URL, потом из xflow.json, потом умолчание —
12
- * порядок такой, чтобы разработчик мог указать локальную платформу одной
13
- * переменной, не правя конфигурацию проекта.
14
- */
8
+ /** Platform connection: address plus key. */
15
9
  function connect(config) {
16
10
  const apiUrl = (0, config_1.apiUrlFor)(config);
17
- const token = (0, credentials_1.resolveToken)(apiUrl);
11
+ // XFLOW_TOKEN never follows an address taken from the repo's xflow.json:
12
+ // a cloned config must not be able to redirect the key elsewhere.
13
+ const token = (0, credentials_1.resolveToken)(apiUrl, apiUrl === (0, config_1.apiUrlFor)());
18
14
  if (!token) {
19
15
  throw new errors_1.CliError(`No access key for ${apiUrl}`, 'Sign in: xflow login. In CI pass the key in the XFLOW_TOKEN variable');
20
16
  }
21
17
  return { apiUrl, token };
22
18
  }
23
- /** Соединение без ключа: только для входа. */
19
+ /** Sign-in only. */
24
20
  function anonymous(config) {
25
21
  return { apiUrl: (0, config_1.apiUrlFor)(config), token: '' };
26
22
  }