@getxflow/cli 0.1.8 → 0.1.10

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,265 +3,264 @@ 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
16
-
17
- ${(0, ui_1.bold)('Начало работы')}
18
- xflow login вход через браузер
19
- xflow init [папка] новый проект из шаблона платформы
20
- xflow templates какие шаблоны доступны
21
- xflow link <id> связать текущую папку с проектом (id: xflow projects list)
22
- xflow skills [--global] обновить инструкцию о платформе для ИИ-агента
23
- xflow mcp install доступ к платформе для агента без терминала
24
-
25
- ${(0, ui_1.bold)('Код')}
26
- xflow status что на сервере и чем отличается локальная копия
27
- xflow push [--force] отправить исходники новой ревизией
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
28
25
  xflow pull [--into dir] [--revision N]
29
- забрать исходники (по умолчанию последнюю ревизию)
30
-
31
- ${(0, ui_1.bold)('Выкладка')}
32
- xflow deploy [--no-push] отправить код, собрать на платформе и выложить на dev
33
- xflow publish показать dev-версию посетителям
34
- xflow rollback <номер версии> вернуть проект на прошлую версию
35
- xflow deployments история версий
36
-
37
- ${(0, ui_1.bold)('Функции')}
38
- xflow functions list что выложено
39
- xflow functions deploy [имя] собрать и выкатить функцию
40
- xflow functions invoke <имя> [--data '{"a":1}']
41
- вызвать функцию и показать ответ
42
- xflow functions logs [имя] падения функций: стек и вывод консоли
43
- xflow schedules list что запускается по времени
44
- xflow schedules set <имя> "0 3 ? * * *"
45
- запускать функцию по расписанию (UTC)
46
- xflow schedules rm <имя> снять расписание
47
- xflow env [check] переменные функций: что записано, чего не хватает
48
- xflow env set ИМЯ=значение записать переменную
49
- xflow env rm ИМЯ удалить переменную
50
-
51
- ${(0, ui_1.bold)('База данных')}
52
- xflow db status какие миграции применены, а какие ждут
26
+ fetch the sources (the latest revision by default)
27
+
28
+ ${(0, ui_1.bold)('Releasing')}
29
+ xflow deploy [--no-push] send the code, build on the platform, release to dev
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
36
+ xflow functions deploy [name] build and ship a function
37
+ xflow functions invoke <name> [--data '{"a":1}']
38
+ call a function and print the answer
39
+ xflow functions logs [name] function crashes: stack and console output
40
+ xflow schedules list what runs on a timer
41
+ xflow schedules set <name> "0 3 ? * * *"
42
+ run a function on a schedule (UTC)
43
+ xflow schedules rm <name> take a schedule off
44
+ xflow env [check] function variables: what is stored, what is missing
45
+ xflow env set NAME=value store a variable
46
+ xflow env rm NAME delete a variable
47
+
48
+ ${(0, ui_1.bold)('Database')}
49
+ xflow db status which migrations are applied and which are waiting
53
50
  xflow db migrate [--dry-run] [--allow-destructive]
54
- применить миграции из migrations/*.sql
51
+ apply the migrations from migrations/*.sql
55
52
 
56
- ${(0, ui_1.bold)('Справочное')}
57
- xflow logs [--limit N] ошибки выложенного приложения в браузере
58
- xflow projects list проекты организации
59
- xflow projects get [id] карточка проекта
60
- xflow whoami чей ключ и что он может
61
- xflow logout забыть ключ
53
+ ${(0, ui_1.bold)('Reference')}
54
+ xflow logs [--limit N] browser errors from the released application
55
+ xflow projects list projects of the organization
56
+ xflow projects get [id] project card
57
+ xflow whoami whose key this is and what it can do
58
+ xflow logout forget the key
62
59
 
63
- ${(0, ui_1.bold)('Окружение')}
64
- XFLOW_TOKEN ключ доступа (для CI: вместо xflow login)
65
- XFLOW_API_URL адрес платформы, если он не app.getxflow.com
60
+ ${(0, ui_1.bold)('Environment')}
61
+ XFLOW_TOKEN access key (for CI, instead of xflow login)
62
+ XFLOW_API_URL platform address when it is not app.getxflow.com
66
63
 
67
- Подробнее о команде: xflow help <команда>`);
64
+ More about one command: xflow help <command>`);
68
65
  }
69
66
  const TOPICS = {
70
- db: `${(0, ui_1.bold)('xflow db migrate')} применить миграции
67
+ db: `${(0, ui_1.bold)('xflow db migrate')}: apply migrations
71
68
 
72
- Файлы лежат в репозитории: ${(0, ui_1.bold)('migrations/0001_init.sql')}, ${(0, ui_1.bold)('migrations/0002_orders.sql')} и так
73
- далее. Порядок по имени файла, поэтому номер в начале обязателен. Каждая миграция
74
- применяется своей транзакцией, история хранится в самой базе, и повторно применённое
75
- не запускается.
69
+ The files live in the repository: ${(0, ui_1.bold)('migrations/0001_init.sql')}, ${(0, ui_1.bold)('migrations/0002_orders.sql')}
70
+ and so on. The order comes from the file name, which is why the leading number is
71
+ required. Every migration runs in its own transaction, the history is kept in the
72
+ database itself, and anything already applied is not run again.
76
73
 
77
- --dry-run показать, что применится, и не трогать базу
78
- --allow-destructive разрешить операции, уничтожающие данные
74
+ --dry-run report what would be applied without touching the database
75
+ --allow-destructive allow operations that destroy data
79
76
 
80
- Про разрушающие. Истории базы платформа не хранит и бэкапов не делает, поэтому
81
- ${(0, ui_1.bold)('DROP TABLE')}, ${(0, ui_1.bold)('DROP COLUMN')}, ${(0, ui_1.bold)('TRUNCATE')} и ${(0, ui_1.bold)('DELETE FROM')} без условия
82
- отклоняются без явного флага. С флагом содержимое затронутых таблиц уезжает в дамп
83
- и хранится 7 суток это страховка «сразу заметил», а не резервная копия.
84
- ${(0, ui_1.bold)('DROP DATABASE')} не пропускается никогда: база общая для организации.
77
+ About destructive ones. The platform keeps no database history and makes no backups,
78
+ 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
79
+ condition are rejected unless the flag is given. With the flag, the contents of the
80
+ affected tables are dumped and kept for 7 days: that is a "caught it right away"
81
+ safety net, not a backup. ${(0, ui_1.bold)('DROP DATABASE')} is never allowed, because the database
82
+ is shared across the organization.
85
83
 
86
- Изменять уже применённый файл бесполезно: сверка идёт по имени, а не по
87
- содержимому. ${(0, ui_1.bold)('xflow db status')} такой файл покажет отдельно заводите новую
88
- миграцию.
84
+ Editing an already applied file achieves nothing: the comparison is by name, not by
85
+ content. ${(0, ui_1.bold)('xflow db status')} lists such a file separately, so write a new migration
86
+ instead.
89
87
 
90
- Одна логическая база может быть привязана к нескольким проектам, поэтому в истории
91
- попадаются миграции, которых нет в вашем репозитории. Это нормально, но означает,
92
- что ваша миграция может сломать чужое приложение.`,
93
- logs: `${(0, ui_1.bold)('xflow logs')} и ${(0, ui_1.bold)('xflow functions logs')} ошибки проекта
88
+ One logical database can be attached to several projects, so the history may hold
89
+ migrations that are not in your repository. That is normal, but it also means your
90
+ migration can break somebody else's application.`,
91
+ logs: `${(0, ui_1.bold)('xflow logs')} and ${(0, ui_1.bold)('xflow functions logs')}: project errors
94
92
 
95
- Один поток на проект: падения выложенного приложения в браузере и падения
96
- облачных функций. Хранятся последние 200 записей, дальше вытесняются.
93
+ One stream per project: crashes of the released application in the browser and
94
+ crashes of the cloud functions. The last 200 entries are kept, older ones fall off.
97
95
 
98
- --limit N сколько показать (по умолчанию 10)
96
+ --limit N how many to show (10 by default)
99
97
 
100
- Функция сообщает о падении сама: свои логи Yandex наружу отдаёт только по gRPC,
101
- поэтому обёртка ловит исключение, уносит вместе с ним хвост консоли (последние 40
102
- строк ${(0, ui_1.bold)('console.log')} этого вызова) и отправляет на платформу. Успешные вызовы не
103
- пишут ничего: иначе каждый запрос платил бы за это задержкой.
98
+ A function reports its own crashes: Yandex exposes its logs only over gRPC, so the
99
+ wrapper catches the exception, carries the tail of the console with it (the last 40
100
+ ${(0, ui_1.bold)('console.log')} lines of that call) and sends it to the platform. Successful calls
101
+ write nothing, otherwise every request would pay for it in latency.
104
102
 
105
- Не попадёт сюда: падение на старте модуля (функция не успевает дойти до обёртки),
106
- превышение 30 секунд и нехватка памяти. Такое видно ответом на
107
- ${(0, ui_1.bold)('xflow functions invoke')}.
103
+ What never reaches this list: a crash while the module is starting (the function
104
+ never gets as far as the wrapper), going over 90 seconds, and running out of memory.
105
+ Those show up in the answer to ${(0, ui_1.bold)('xflow functions invoke')}.
108
106
 
109
- Ошибки браузера собирает ${(0, ui_1.bold)('src/utils/error-logger.ts')} шаблона и только с
110
- выложенных адресов: локальный ${(0, ui_1.bold)('npm run dev')} сюда не пишет.`,
111
- env: `${(0, ui_1.bold)('xflow env')} переменные окружения функций
107
+ Browser errors are collected by ${(0, ui_1.bold)('src/utils/error-logger.ts')} of the template and
108
+ only from released addresses: a local ${(0, ui_1.bold)('npm run dev')} writes nothing here.`,
109
+ env: `${(0, ui_1.bold)('xflow env')}: environment variables of the functions
112
110
 
113
- Ключи, пароли и адреса чужих сервисов хранит платформа, а не репозиторий.
114
- Значение уходит наверх и обратно не возвращается: увидеть его можно только внутри
115
- функции. Так что своё же значение придётся хранить там, где вы его взяли.
111
+ Keys, passwords and third-party addresses are kept by the platform, not by the
112
+ repository. A value goes up and never comes back: the only place to see it is inside
113
+ the function. So keep your own copy wherever you got it from.
116
114
 
117
- xflow env что записано (имена, не значения)
118
- xflow env check чего не хватает функциям этого проекта
119
- xflow env set SMTP_PASSWORD=… записать
120
- xflow env rm SMTP_PASSWORD удалить
115
+ xflow env what is stored (names, not values)
116
+ xflow env check what the functions of this project are missing
117
+ xflow env set SMTP_PASSWORD=… store
118
+ xflow env rm SMTP_PASSWORD delete
121
119
 
122
- --scope project видно только этому проекту, а не всей организации
120
+ --scope project visible to this project only, not the whole organization
123
121
 
124
- ${(0, ui_1.bold)('check')} читает исходники в ${(0, ui_1.bold)('functions/')} и ищет обращения ${(0, ui_1.bold)('process.env.ИМЯ')}. Это же
125
- правило действует на выкатке: функция получает только те переменные, которые сама
126
- упоминает по имени. Имя, собранное выражением (${(0, ui_1.bold)("process.env['KEY_' + n]")}), в окружение
127
- не попадёт читайте переменные буквально.
122
+ ${(0, ui_1.bold)('check')} reads the sources in ${(0, ui_1.bold)('functions/')} and looks for ${(0, ui_1.bold)('process.env.NAME')}
123
+ references. The same rule applies on deploy: a function receives only the variables it
124
+ mentions by name. A name assembled from an expression (${(0, ui_1.bold)("process.env['KEY_' + n]")})
125
+ never reaches the environment, so read variables literally.
128
126
 
129
- Значение доезжает до функции на выкатке, а не в момент записи: после ${(0, ui_1.bold)('env set')} нужно
130
- ${(0, ui_1.bold)('xflow functions deploy')} затронутых функций. То же и после удаления: в уже выложенной
131
- функции старое значение останется до следующей выкатки.`,
132
- schedules: `${(0, ui_1.bold)('xflow schedules')} запуск функций по времени
127
+ The value reaches the function on deploy, not at the moment it is stored: after
128
+ ${(0, ui_1.bold)('env set')} you need ${(0, ui_1.bold)('xflow functions deploy')} for the functions involved. The same
129
+ after a 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
133
131
 
134
- Расписание это таймер-триггер Yandex: он зовёт функцию сам, без участия
135
- приложения и браузера.
132
+ A schedule is a Yandex timer trigger: it calls the function itself, with no
133
+ application and no browser involved.
136
134
 
137
- xflow schedules set отчёт "0 3 ? * * *" каждый день в 03:00
138
- xflow schedules set сводка "0 */4 ? * * *" каждые 4 часа
139
- xflow schedules rm отчёт снять все расписания функции
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
140
138
 
141
- Формат из шести полей: ${(0, ui_1.bold)('минуты часы день-месяца месяц день-недели год')}. Ровно одно
142
- из полей «День месяца» и «День недели» должно быть ${(0, ui_1.bold)('?')} это не наша причуда, так
143
- устроен Яндекс. ${(0, ui_1.bold)('Время всегда UTC')}, местное он не знает.
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.
144
142
 
145
- --payload '{"режим":"полный"}' тело, которое получит функция
143
+ --payload '{"mode":"full"}' the body the function will receive
146
144
 
147
- Запуск по расписанию приходит хендлеру как POST без заголовков, и токен проекта
148
- у него не проверяется: снаружи такое событие не подделать. Функция при этом должна
149
- быть уже выложена расписание ссылается на неё, а не наоборот.
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.
150
148
 
151
- Упавший запуск виден в ${(0, ui_1.bold)('xflow functions logs')}: по расписанию за функцией никто не
152
- смотрит, поэтому обёртка отчитывается о падении так же, как при обычном вызове.`,
153
- invoke: `${(0, ui_1.bold)('xflow functions invoke')} <имя> вызвать функцию
149
+ A failed run shows up in ${(0, ui_1.bold)('xflow functions logs')}: nobody is watching a scheduled
150
+ function, so the wrapper reports a crash the same way it does on an ordinary call.`,
151
+ invoke: `${(0, ui_1.bold)('xflow functions invoke')} <name>: call a function
154
152
 
155
- Вызывает так же, как её вызывает приложение: с заголовком X-Project-Token.
156
- Токен берётся из карточки проекта на платформе, локальный .env не нужен.
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.
157
155
 
158
- --data '{"a":1}' тело запроса (по умолчанию метод становится POST)
159
- --method GET другой метод
156
+ --data '{"a":1}' request body (the method becomes POST by default)
157
+ --method GET a different method
160
158
 
161
- Печатает статус, время ответа и тело. Ненулевой код возврата на статусе 4xx и
162
- 5xx: в CI такой вызов должен ронять шаг. Причину падения показывает
163
- ${(0, ui_1.bold)('xflow functions logs <имя>')}.`,
164
- mcp: `${(0, ui_1.bold)('xflow mcp install')} подключить агента к платформе напрямую
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
161
+ ${(0, ui_1.bold)('xflow functions logs <name>')}.`,
162
+ mcp: `${(0, ui_1.bold)('xflow mcp install')}: connect the agent to the platform directly
165
163
 
166
- Прописывает сервер MCP в конфигурацию клиента, у которого есть своя команда
167
- подключения: сегодня это Claude Code, остальным команда покажет, что вписать
168
- руками. После этого агент управляет платформой без терминала: смотрит базу,
169
- применяет миграции, выкладывает и вызывает функции, ставит расписания, читает
170
- логи. Команды остаются: код, сборку и выкладку по-прежнему делает CLI, гнать
171
- исходники через MCP значит забивать контекст агента и тратить ваши деньги.
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.
172
170
 
173
- Ключ доступа команда берёт из вашего входа и на экран не выводит: обычно её
174
- запускает сам агент, а всё напечатанное попадает в его контекст и в историю
175
- переписки.
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.
176
174
 
177
- --show-token всё же показать ключ (не под агентом)
175
+ --show-token print the key anyway (not while under an agent)
178
176
 
179
- Ключ один на человека в организации, и область у него не сужается: агент видит
180
- все проекты организации и называет нужный сам. Проект он берёт из аргумента
181
- ${(0, ui_1.bold)('project_id')}, а не из вашей папки папки он не видит.`,
182
- skills: `${(0, ui_1.bold)('xflow skills')} инструкция о платформе для ИИ-агента
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
179
+ project from the ${(0, ui_1.bold)('project_id')} argument, not from your folder, which it cannot see.`,
180
+ skills: `${(0, ui_1.bold)('xflow skills')}: platform instructions for an AI agent
183
181
 
184
- Про XFlow агенту знать неоткуда: в его обучении платформы нет. Инструкция объясняет,
185
- как выкладывать и публиковать, откуда брать компоненты дизайн-системы, где смотреть
186
- ошибки прода. ${(0, ui_1.bold)('init')} и ${(0, ui_1.bold)('link')} раскладывают её сами — эта команда нужна, чтобы
187
- обновить её после обновления CLI: они едут одной версией.
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.
188
187
 
189
- Формат общий (agentskills.io), а папки у инструментов разные, поэтому кладём во все:
188
+ The format is shared (agentskills.io) but the folders differ per tool, so we write all:
190
189
 
191
190
  .claude/skills/xflow/SKILL.md Claude Code
192
191
  .agents/skills/xflow/SKILL.md Codex, OpenClaw
193
- .cursor/rules/xflow.mdc Cursor: общий формат он не читает
194
-
195
- --global то же в домашней папке, тогда скилл виден во всех проектах.
196
- Там добавляется ~/.codex/skills: общий каталог Codex смотрит
197
- только внутри репозитория. Правило Cursor и AGENTS.md остаются
198
- проектными, глобальных аналогов у них нет
199
-
200
- Плюс несколько строк-указателей в ${(0, ui_1.bold)('AGENTS.md')}. Скилл подхватывается лениво, только
201
- когда описание совпало с задачей, и на «добавь таблицу клиентов» он не сработает.
202
- AGENTS.md агент читает всегда, поэтому указатель дописывается туда в конец файла и
203
- один раз. ${(0, ui_1.bold)('CLAUDE.md')} не трогаем: Claude Code и так читает .claude/skills.`,
204
- push: `${(0, ui_1.bold)('xflow push')} отправить исходники
205
-
206
- Уходит вся рабочая копия целиком, одной ревизией. Не отправляются: node_modules,
207
- .git, dist, build, .next, любые .env, а также всё, что перечислено в .xflowignore
208
- и в поле ignore файла xflow.json.
209
-
210
- Если на сервере ревизия новее той, от которой вы работали, push отклоняется.
211
- Это значит, что кто-то запушил раньше: заберите его правки рядом
212
- (${(0, ui_1.bold)('xflow pull --into ./server-copy')}), сведите их у себя в git и повторите.
213
-
214
- --force перетирает серверную ревизию. Перед этим CLI показывает, чью работу вы
215
- затираете, и просит подтверждение вводом имени проекта. В неинтерактивном режиме
216
- (CI, запуск агентом) подтвердить нельзя: расхождение версий должно ронять сборку,
217
- а не молча уничтожать чужую работу.`,
218
- pull: `${(0, ui_1.bold)('xflow pull')} забрать исходники
219
-
220
- По умолчанию забирает последнюю ревизию в папку проекта и отказывается писать в
221
- непустую: слить изменения платформа не умеет, это работа git.
222
-
223
- --into <папка> выгрузить рядом, чтобы сравнить
224
- --revision <N> конкретная ревизия (список: xflow deployments)
225
- --force перезаписать папку целиком`,
226
- deploy: `${(0, ui_1.bold)('xflow deploy')} собрать и выложить
227
-
228
- Два шага: отправка исходников и сборка на платформе. Команда сборки и каталог
229
- результата берутся из xflow.json (по умолчанию npm run build и dist).
230
-
231
- --no-push не отправлять исходники, собрать из последней серверной ревизии
232
- --force разрешить перезапись серверной ревизии при отправке
233
-
234
- Собирает платформа, в чистой песочнице с одинаковой для всех версией Node,
235
- поэтому «у меня работало» больше не зависит от вашей машины. Перед сборкой
236
- проект проверяется на соответствие шаблону: несоответствия печатаются списком,
237
- и сборка не запускается вовсе.
238
-
239
- Собранную версию видно на странице проекта, ссылку CLI печатает. Посетителям после
240
- ${(0, ui_1.bold)('xflow publish')}. Адрес самой сборки не печатается: в нём зашит номер версии, и
241
- после следующей публикации такая ссылка молча отдаёт старую копию.`,
242
- init: `${(0, ui_1.bold)('xflow init')} [папка] — новый проект
243
-
244
- Разворачивает шаблон платформы: React на Vite, Tailwind, набор компонентов
245
- (${(0, ui_1.bold)('src/components/ui')}) и готовые блоки (${(0, ui_1.bold)('src/components/blocks')}) таблица,
246
- форма, фильтры, канбан, графики. Тот же шаблон получают проекты, созданные в вебе,
247
- поэтому приложения выглядят одинаково.
248
-
249
- Строить интерфейс поверх этих компонентов и токенов из ${(0, ui_1.bold)('src/index.css')} — не
250
- формальность: своя палитра поверх них выглядит чужеродно внутри платформы.
251
-
252
- --name <имя> имя проекта (по умолчанию — имя папки)
253
- --template <id> другой шаблон (список: xflow templates)
254
- --database <id> привязать логическую базу организации
255
-
256
- Порядок: проект заводится на платформе первым, потому что без него неоткуда взять
257
- токен для .env. Если запись файлов сорвётся, проект останется пустым, и CLI скажет,
258
- как подобрать его командой link.`,
259
- rollback: `${(0, ui_1.bold)('xflow rollback')} <номер версии> вернуть прошлую сборку
260
-
261
- Переключает проект на выбранную сборку. Возвращается только она: исходники
262
- остаются на своей ревизии, и вернуть их отдельное решение
263
- (${(0, ui_1.bold)('xflow pull --revision N --into ./старая-версия')}).
264
-
265
- Посетители продолжают видеть опубликованную версию, пока не выполнен
266
- ${(0, ui_1.bold)('xflow publish')}.`,
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
202
+ the file and once. ${(0, ui_1.bold)('CLAUDE.md')} is left alone: Claude Code reads .claude/skills anyway.`,
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
216
+ to fail the build rather than silently destroy somebody else's work.`,
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)
224
+ --force overwrite the folder completely`,
225
+ deploy: `${(0, ui_1.bold)('xflow deploy')}: build and release
226
+
227
+ Two steps: sending the sources and building on the platform. The build command and the
228
+ output directory come from xflow.json (npm run build and dist by default).
229
+
230
+ --no-push do not send sources, build from the latest server revision
231
+ --force allow overwriting the server revision while sending
232
+
233
+ The platform builds, in a clean sandbox on one Node version for everybody, so "it
234
+ worked on my machine" no longer depends on your machine. Before the build the project
235
+ is checked against the template: mismatches are printed as a list and the build does
236
+ not start at all.
237
+
238
+ The built version is visible on the project page, and the CLI prints the link. Visitors
239
+ see it after ${(0, ui_1.bold)('xflow publish')}. The address of the build itself is not printed:
240
+ it carries the version number, and after the next publish such a link quietly serves an
241
+ old copy.`,
242
+ init: `${(0, ui_1.bold)('xflow init')} [dir]: a new project
243
+
244
+ Lays down the platform template: React on Vite, Tailwind, a set of components
245
+ (${(0, ui_1.bold)('src/components/ui')}) and ready-made blocks (${(0, ui_1.bold)('src/components/blocks')}): table,
246
+ form, filters, kanban, charts. Projects created in the web get the same template, which
247
+ is what makes the applications look alike.
248
+
249
+ Building the interface on top of these components and the tokens in ${(0, ui_1.bold)('src/index.css')}
250
+ is not a formality: your own palette on top of them looks foreign inside the platform.
251
+
252
+ --name <name> project name (the folder name by default)
253
+ --template <id> a different template (the list: xflow templates)
254
+ --database <id> attach a logical database of the organization
255
+
256
+ The order: the project is created on the platform first, because without it there is
257
+ nowhere to get the token for .env. If writing the files fails, the project stays empty
258
+ and the CLI explains how to pick it up with the link command.`,
259
+ rollback: `${(0, ui_1.bold)('xflow rollback')} <version number>: return an earlier build
260
+
261
+ Switches the project to the chosen build. Only the build comes back: the sources stay
262
+ at their own revision, and returning them is a separate decision
263
+ (${(0, ui_1.bold)('xflow pull --revision N --into ./old-version')}).
264
+
265
+ Visitors keep seeing the published version until ${(0, ui_1.bold)('xflow publish')} is run.`,
267
266
  };
package/dist/limits.js ADDED
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ /** Plan limits in CLI output: whoami and command refusals. */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.limitLine = limitLine;
5
+ exports.quotaRows = quotaRows;
6
+ const DENIAL_LABELS = {
7
+ limit_projects: 'projects',
8
+ limit_functions: 'cloud functions',
9
+ limit_builds: 'builds this month',
10
+ limit_seats: 'seats in the organization',
11
+ limit_schedule_interval: 'schedule frequency',
12
+ db_write_locked: 'database volume, writes are off',
13
+ plan_no_schedules: 'schedules',
14
+ subscription_blocked: 'the subscription is unpaid',
15
+ billing_unavailable: 'plan check',
16
+ };
17
+ /** Why repeating the same command will not help. */
18
+ function limitLine(detail) {
19
+ const label = DENIAL_LABELS[detail.code] ?? detail.code;
20
+ const numbers = detail.used !== undefined && detail.limit !== undefined
21
+ ? `, ${detail.used} of ${detail.limit} used`
22
+ : '';
23
+ return `Plan limit: ${label}${numbers}. Repeating the same command will not help.`;
24
+ }
25
+ const QUOTA_ORDER = [
26
+ 'projects',
27
+ 'functions',
28
+ 'developers',
29
+ 'members',
30
+ 'database_mb',
31
+ 'files_mb',
32
+ 'function_minutes',
33
+ 'builds',
34
+ ];
35
+ const QUOTA_LABELS = {
36
+ projects: 'Projects',
37
+ functions: 'Cloud functions',
38
+ developers: 'Developers',
39
+ members: 'Staff',
40
+ database_mb: 'Database',
41
+ files_mb: 'Files',
42
+ function_minutes: 'Function minutes',
43
+ builds: 'Builds this month',
44
+ };
45
+ /** Decimal units, 1000 MB per GB: matches how volumes are sold. */
46
+ function sizeLabel(mb) {
47
+ if (mb < 1000)
48
+ return `${Math.round(mb)} MB`;
49
+ const gb = mb / 1000;
50
+ return `${Number.isInteger(gb) ? String(gb) : gb.toFixed(1)} GB`;
51
+ }
52
+ function quotaValue(key, use) {
53
+ // Same unit on both sides: "0.2 of 15 GB", not "245 of 15360".
54
+ if (key.endsWith('_mb')) {
55
+ const used = use.limit >= 1000 ? (use.used / 1000).toFixed(1) : String(use.used);
56
+ return `${used} of ${sizeLabel(use.limit)}`;
57
+ }
58
+ return `${use.used} of ${use.limit}`;
59
+ }
60
+ /** Unknown keys stay visible: the server may add new ones. */
61
+ function quotaRows(quotas) {
62
+ const known = QUOTA_ORDER.filter((key) => quotas[key]);
63
+ const rest = Object.keys(quotas).filter((key) => !QUOTA_ORDER.includes(key));
64
+ return [...known, ...rest].map((key) => [
65
+ ` ${QUOTA_LABELS[key] ?? key}`,
66
+ quotaValue(key, quotas[key]),
67
+ ]);
68
+ }