@getxflow/cli 0.1.8 → 0.1.9

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.
@@ -66,12 +66,12 @@ async function fetchVariables() {
66
66
  async function envList() {
67
67
  const { rows } = await fetchVariables();
68
68
  if (rows.length === 0) {
69
- (0, ui_1.note)('Переменных нет');
70
- (0, ui_1.note)((0, ui_1.dim)(' Записать: xflow env set SMTP_PASSWORD=секрет'));
69
+ (0, ui_1.note)('No variables');
70
+ (0, ui_1.note)((0, ui_1.dim)(' To store one: xflow env set SMTP_PASSWORD=secret'));
71
71
  return;
72
72
  }
73
- (0, ui_1.table)(rows.map((row) => [row.name, row.scope === 'project' ? 'только этот проект' : 'вся организация']));
74
- (0, ui_1.note)((0, ui_1.dim)(' Значения платформа не отдаёт: их видно только внутри функции'));
73
+ (0, ui_1.table)(rows.map((row) => [row.name, row.scope === 'project' ? 'this project only' : 'the whole organization']));
74
+ (0, ui_1.note)((0, ui_1.dim)(' The platform never returns values: they are visible only inside the function'));
75
75
  }
76
76
  /**
77
77
  * Сверить, хватает ли функциям переменных.
@@ -83,14 +83,14 @@ async function envCheck() {
83
83
  const { names, root } = await fetchVariables();
84
84
  const { needed, provided } = referencedByFunctions(root);
85
85
  if (needed.size === 0 && provided.size === 0) {
86
- (0, ui_1.note)('Функции проекта не читают переменных окружения');
86
+ (0, ui_1.note)('The functions of this project read no environment variables');
87
87
  return;
88
88
  }
89
89
  // Платформенные показываем отдельной таблицей, а не молчим о них: раньше
90
90
  // функция, читающая только DATABASE_URL, получала ответ «переменных не
91
91
  // читают», то есть команда делала ложное утверждение о собственном коде.
92
92
  if (provided.size > 0) {
93
- (0, ui_1.out)((0, ui_1.bold)('Даёт платформа:'));
93
+ (0, ui_1.out)((0, ui_1.bold)('Provided by the platform:'));
94
94
  (0, ui_1.table)([...provided.entries()].sort().map(([name, users]) => [name, users.join(', ')]));
95
95
  if (needed.size > 0)
96
96
  (0, ui_1.out)('');
@@ -105,24 +105,24 @@ async function envCheck() {
105
105
  missing.push(row);
106
106
  }
107
107
  if (present.length > 0) {
108
- (0, ui_1.out)((0, ui_1.bold)('Записаны на платформе:'));
108
+ (0, ui_1.out)((0, ui_1.bold)('Stored on the platform:'));
109
109
  (0, ui_1.table)(present);
110
110
  }
111
111
  if (missing.length === 0) {
112
- (0, ui_1.ok)('Всем функциям хватает переменных');
112
+ (0, ui_1.ok)('Every function has the variables it needs');
113
113
  return;
114
114
  }
115
115
  (0, ui_1.out)('');
116
- (0, ui_1.fail)('Нет на платформе:');
116
+ (0, ui_1.fail)('Missing on the platform:');
117
117
  (0, ui_1.table)(missing);
118
- throw new errors_1.CliError(`Переменных не хватает: ${missing.length}`, 'Записать: xflow env set ИМЯ=значение. Пока их нет, функция получит undefined');
118
+ throw new errors_1.CliError(`Missing variables: ${missing.length}`, 'To store them: xflow env set NAME=value. Until then the function receives undefined');
119
119
  }
120
120
  async function envSet(args) {
121
121
  const { root, config } = (0, config_1.requireProject)();
122
122
  const client = (0, session_1.connect)(config);
123
123
  const pair = args.words[1];
124
124
  if (!pair || !pair.includes('=')) {
125
- throw new errors_1.CliError('Нужна пара ИМЯ=значение', 'Например: xflow env set SMTP_PASSWORD=секрет');
125
+ throw new errors_1.CliError('A NAME=value pair is required', 'For example: xflow env set SMTP_PASSWORD=secret');
126
126
  }
127
127
  const name = pair.slice(0, pair.indexOf('=')).trim();
128
128
  const value = pair.slice(pair.indexOf('=') + 1);
@@ -130,12 +130,12 @@ async function envSet(args) {
130
130
  method: 'POST',
131
131
  body: { name, value, scope: (0, args_1.flagString)(args, 'scope') === 'project' ? 'project' : 'organization' },
132
132
  });
133
- (0, ui_1.ok)(`${(0, ui_1.bold)(result.name)} записана (${result.scope === 'project' ? 'только этот проект' : 'вся организация'})`);
133
+ (0, ui_1.ok)(`${(0, ui_1.bold)(result.name)} stored (${result.scope === 'project' ? 'this project only' : 'the whole organization'})`);
134
134
  // Значение попадает в функцию на выкатке, а не в момент записи: пока функцию
135
135
  // не передеплоили, в её окружении лежит прежнее.
136
136
  const users = referencedByFunctions(root).needed.get(name);
137
137
  if (users && users.length > 0) {
138
- (0, ui_1.note)((0, ui_1.dim)(` Чтобы значение доехало, перевыложите: xflow functions deploy ${users.join(' && xflow functions deploy ')}`));
138
+ (0, ui_1.note)((0, ui_1.dim)(` For the value to arrive, redeploy: xflow functions deploy ${users.join(' && xflow functions deploy ')}`));
139
139
  }
140
140
  }
141
141
  async function envRemove(args) {
@@ -143,12 +143,12 @@ async function envRemove(args) {
143
143
  const client = (0, session_1.connect)(config);
144
144
  const name = args.words[1];
145
145
  if (!name)
146
- throw new errors_1.CliError('Нужно имя переменной', 'Что записано: xflow env');
146
+ throw new errors_1.CliError('A variable name is required', 'What is stored: xflow env');
147
147
  const result = await (0, api_1.apiJson)(client, `/api/v1/projects/${config.projectId}/env?name=${encodeURIComponent(name)}`, { method: 'DELETE' });
148
148
  if (!result.removed) {
149
- (0, ui_1.note)(`Переменной ${name} нет`);
149
+ (0, ui_1.note)(`There is no variable ${name}`);
150
150
  return;
151
151
  }
152
- (0, ui_1.ok)(`${name} удалена`);
153
- (0, ui_1.note)((0, ui_1.dim)(' В уже выложенных функциях значение останется до следующей выкатки'));
152
+ (0, ui_1.ok)(`${name} deleted`);
153
+ (0, ui_1.note)((0, ui_1.dim)(' Functions already deployed keep the value until their next deploy'));
154
154
  }
@@ -48,7 +48,7 @@ function bundle(root, entry) {
48
48
  esbuild = require(require.resolve('esbuild', { paths: [root] }));
49
49
  }
50
50
  catch {
51
- throw new errors_1.CliError('Не нашёл esbuild в проекте', 'Установите его: npm i -D esbuild. Обычно он уже стоит вместе с Vite');
51
+ throw new errors_1.CliError('Could not find esbuild in the project', 'Install it: npm i -D esbuild. It usually comes with Vite already');
52
52
  }
53
53
  const result = esbuild.buildSync({
54
54
  entryPoints: [entry],
@@ -62,7 +62,7 @@ function bundle(root, entry) {
62
62
  });
63
63
  const text = result.outputFiles[0]?.text;
64
64
  if (!text)
65
- throw new errors_1.CliError(`Сборка ${entry} не дала результата`);
65
+ throw new errors_1.CliError(`Building ${entry} produced nothing`);
66
66
  return text;
67
67
  }
68
68
  async function functionsList() {
@@ -70,13 +70,13 @@ async function functionsList() {
70
70
  const client = (0, session_1.connect)(config);
71
71
  const data = await (0, api_1.apiJson)(client, `/api/v1/projects/${config.projectId}/functions`);
72
72
  if (data.functions.length === 0) {
73
- (0, ui_1.note)(`Функций нет. Положите код в ${FUNCTIONS_DIR}/<имя>/index.ts и выполните xflow functions deploy`);
73
+ (0, ui_1.note)(`No functions. Put the code in ${FUNCTIONS_DIR}/<name>/index.ts and run xflow functions deploy`);
74
74
  return;
75
75
  }
76
76
  (0, ui_1.table)(data.functions.map((fn) => [
77
77
  fn.name,
78
- fn.status === 'deployed' ? 'выложена' : fn.status === 'failed' ? 'ошибка' : fn.status,
79
- fn.last_deployed_at ? (0, ui_1.formatAge)(fn.last_deployed_at) : '',
78
+ fn.status === 'deployed' ? 'deployed' : fn.status === 'failed' ? 'failed' : fn.status,
79
+ fn.last_deployed_at ? (0, ui_1.formatAge)(fn.last_deployed_at) : '-',
80
80
  fn.error_message ?? '',
81
81
  ]));
82
82
  }
@@ -120,13 +120,13 @@ async function functionsInvoke(args) {
120
120
  const client = (0, session_1.connect)(config);
121
121
  const name = args.words[1];
122
122
  if (!name)
123
- throw new errors_1.CliError('Нужно имя функции', 'Что выложено: xflow functions list');
123
+ throw new errors_1.CliError('A function name is required', 'What is deployed: xflow functions list');
124
124
  const card = await (0, api_1.apiJson)(client, `/api/v1/projects/${config.projectId}`);
125
125
  const fn = card.functions.find((item) => item.name === name);
126
126
  if (!fn || !fn.invoke_url) {
127
- throw new errors_1.CliError(`Функция ${name} не выложена`, card.functions.length > 0
128
- ? `Выложены: ${card.functions.map((item) => item.name).join(', ')}`
129
- : `Выкатить: xflow functions deploy ${name}`);
127
+ throw new errors_1.CliError(`Function ${name} is not deployed`, card.functions.length > 0
128
+ ? `Deployed: ${card.functions.map((item) => item.name).join(', ')}`
129
+ : `To deploy it: xflow functions deploy ${name}`);
130
130
  }
131
131
  const data = (0, args_1.flagString)(args, 'data');
132
132
  const method = ((0, args_1.flagString)(args, 'method') ?? (data ? 'POST' : 'GET')).toUpperCase();
@@ -141,21 +141,21 @@ async function functionsInvoke(args) {
141
141
  'X-Project-Token': card.project_token ?? '',
142
142
  },
143
143
  body: sendsBody ? (data ?? '{}') : undefined,
144
- // У функции свой потолок в 30 секунд: ждём чуть дольше, чтобы увидеть её
144
+ // У функции свой потолок в 90 секунд: ждём чуть дольше, чтобы увидеть её
145
145
  // собственный таймаут, а не свой.
146
- signal: AbortSignal.timeout(40_000),
146
+ signal: AbortSignal.timeout(100_000),
147
147
  });
148
148
  }
149
149
  catch (e) {
150
- throw new errors_1.CliError(`Функция не ответила: ${e instanceof Error ? e.message : String(e)}`, 'Проверьте, что она выложена: xflow functions list');
150
+ throw new errors_1.CliError(`The function did not answer: ${e instanceof Error ? e.message : String(e)}`, 'Check that it is deployed: xflow functions list');
151
151
  }
152
152
  const elapsed = Date.now() - started;
153
153
  const text = await response.text();
154
- (0, ui_1.note)((0, ui_1.dim)(`${response.status} ${response.statusText} за ${elapsed} мс`));
154
+ (0, ui_1.note)((0, ui_1.dim)(`${response.status} ${response.statusText} in ${elapsed} ms`));
155
155
  if (text)
156
156
  (0, ui_1.out)(prettyBody(text));
157
157
  if (!response.ok) {
158
- throw new errors_1.CliError(`Функция ответила ${response.status}`, `Стек и вывод консоли: xflow functions logs ${name}`);
158
+ throw new errors_1.CliError(`The function answered ${response.status}`, `The stack and console output: xflow functions logs ${name}`);
159
159
  }
160
160
  }
161
161
  async function functionsDeploy(args) {
@@ -164,23 +164,23 @@ async function functionsDeploy(args) {
164
164
  const wanted = args.words[1];
165
165
  const names = wanted ? [wanted] : discover(root);
166
166
  if (names.length === 0) {
167
- throw new errors_1.CliError( проекте нет функций`, `Создайте ${FUNCTIONS_DIR}/<имя>/index.ts с экспортом handler и повторите`);
167
+ throw new errors_1.CliError(`The project has no functions`, `Create ${FUNCTIONS_DIR}/<name>/index.ts exporting handler and try again`);
168
168
  }
169
169
  for (const name of names) {
170
170
  const entry = entryFor(root, name);
171
171
  if (!entry) {
172
- throw new errors_1.CliError(`Не нашёл ${FUNCTIONS_DIR}/${name}/index.ts`, `Доступные функции: ${discover(root).join(', ') || 'нет ни одной'}`);
172
+ throw new errors_1.CliError(`Could not find ${FUNCTIONS_DIR}/${name}/index.ts`, `Available functions: ${discover(root).join(', ') || 'none at all'}`);
173
173
  }
174
- (0, ui_1.step)(`Собираю ${name}`);
174
+ (0, ui_1.step)(`Building ${name}`);
175
175
  const code = bundle(root, entry);
176
- (0, ui_1.step)(`Выкладываю ${name}`);
176
+ (0, ui_1.step)(`Deploying ${name}`);
177
177
  const result = await (0, api_1.apiUpload)(client, `/api/v1/projects/${config.projectId}/functions?name=${encodeURIComponent(name)}`, Buffer.from(code, 'utf-8'), { 'Content-Type': 'application/javascript' });
178
- (0, ui_1.ok)(`${(0, ui_1.bold)(result.name)} выложена`);
178
+ (0, ui_1.ok)(`${(0, ui_1.bold)(result.name)} deployed`);
179
179
  (0, ui_1.out)(result.url);
180
180
  if (result.secrets.length > 0) {
181
- (0, ui_1.note)((0, ui_1.dim)(` Секреты организации в окружении: ${result.secrets.join(', ')}`));
181
+ (0, ui_1.note)((0, ui_1.dim)(` Organization secrets in the environment: ${result.secrets.join(', ')}`));
182
182
  }
183
183
  }
184
184
  const available = await refreshFunctionsEnv(root, client, config.projectId);
185
- (0, ui_1.note)((0, ui_1.dim)(` Адреса в .env обновлены (${available.join(', ')}). Во фронтенде: xflow.functions.invoke('${names[0]}')`));
185
+ (0, ui_1.note)((0, ui_1.dim)(` Addresses in .env updated (${available.join(', ')}). In the frontend: xflow.functions.invoke('${names[0]}')`));
186
186
  }
@@ -27,14 +27,14 @@ async function fetchLogs(source, name, args) {
27
27
  /** Свежее — внизу: так последняя ошибка оказывается перед глазами, а не уезжает вверх. */
28
28
  function render(rows) {
29
29
  for (const row of [...rows].reverse()) {
30
- const label = row.source === 'function' ? (row.function ?? 'функция') : 'браузер';
30
+ const label = row.source === 'function' ? (row.function ?? 'function') : 'browser';
31
31
  (0, ui_1.out)(`${(0, ui_1.dim)(stamp(row.timestamp))} ${(0, ui_1.bold)(label)} ${row.message}`);
32
32
  if (row.stack) {
33
33
  const lines = row.stack.split('\n');
34
34
  for (const line of lines.slice(0, STACK_MAX_LINES))
35
35
  (0, ui_1.out)((0, ui_1.dim)(` ${line}`));
36
36
  if (lines.length > STACK_MAX_LINES)
37
- (0, ui_1.out)((0, ui_1.dim)(` … ещё строк: ${lines.length - STACK_MAX_LINES}`));
37
+ (0, ui_1.out)((0, ui_1.dim)(` … ${lines.length - STACK_MAX_LINES} more lines`));
38
38
  }
39
39
  (0, ui_1.out)('');
40
40
  }
@@ -42,8 +42,8 @@ function render(rows) {
42
42
  async function logs(args) {
43
43
  const rows = await fetchLogs('client', undefined, args);
44
44
  if (rows.length === 0) {
45
- (0, ui_1.note)('Ошибок из браузера нет');
46
- (0, ui_1.note)((0, ui_1.dim)(' Сюда попадают падения выложенного приложения, а не локального npm run dev'));
45
+ (0, ui_1.note)('No browser errors');
46
+ (0, ui_1.note)((0, ui_1.dim)(' This collects crashes of the released application, not of a local npm run dev'));
47
47
  return;
48
48
  }
49
49
  render(rows);
@@ -52,8 +52,8 @@ async function functionsLogs(args) {
52
52
  const name = args.words[1];
53
53
  const rows = await fetchLogs('function', name, args);
54
54
  if (rows.length === 0) {
55
- (0, ui_1.note)(name ? `Функция ${name} не падала` : 'Функции не падали');
56
- (0, ui_1.note)((0, ui_1.dim)(' Сюда попадают только неудачные вызовы: успешные ничего не пишут'));
55
+ (0, ui_1.note)(name ? `Function ${name} has not crashed` : 'No function has crashed');
56
+ (0, ui_1.note)((0, ui_1.dim)(' Only failed calls land here: successful ones write nothing'));
57
57
  return;
58
58
  }
59
59
  render(rows);
@@ -58,42 +58,42 @@ async function mcpInstall(args) {
58
58
  const client = (0, session_1.connect)(root ? (0, config_1.readConfig)(root) : undefined);
59
59
  const url = `${client.apiUrl.replace(/\/+$/, '')}/api/mcp`;
60
60
  const identity = await (0, api_1.apiJson)(client, '/api/v1/me');
61
- (0, ui_1.out)(`${(0, ui_1.bold)('Сервер MCP')} ${url}`);
62
- (0, ui_1.note)((0, ui_1.dim)(` Организация: ${identity.organization.name ?? 'без названия'}`));
63
- (0, ui_1.note)((0, ui_1.dim)(' Ключ на всю организацию: проект агент называет сам'));
61
+ (0, ui_1.out)(`${(0, ui_1.bold)('MCP server')} ${url}`);
62
+ (0, ui_1.note)((0, ui_1.dim)(` Organization: ${identity.organization.name ?? 'unnamed'}`));
63
+ (0, ui_1.note)((0, ui_1.dim)(' The key covers the whole organization: the agent names the project itself'));
64
64
  let found = 0;
65
65
  let installed = 0;
66
66
  for (const target of CLIENTS) {
67
67
  if (!hasBinary(target.binary))
68
68
  continue;
69
69
  found++;
70
- (0, ui_1.step)(`Прописываю в ${target.label}`);
70
+ (0, ui_1.step)(`Writing into ${target.label}`);
71
71
  execute(target.binary, [...target.reset]);
72
72
  const result = execute(target.binary, target.args(url, client.token));
73
73
  if (result.status === 0) {
74
74
  installed++;
75
- (0, ui_1.ok)(`${target.label}: сервер xflow подключён`);
75
+ (0, ui_1.ok)(`${target.label}: the xflow server is connected`);
76
76
  }
77
77
  else {
78
78
  const reason = `${result.stderr ?? ''}${result.stdout ?? ''}`.trim();
79
- (0, ui_1.note)(`${target.label}: команда завершилась с ошибкой`);
79
+ (0, ui_1.note)(`${target.label}: the command exited with an error`);
80
80
  if (reason)
81
- (0, ui_1.out)((0, ui_1.dim)(` ${reason.split(client.token).join('<ключ>').split('\n').slice(0, 5).join('\n ')}`));
81
+ (0, ui_1.out)((0, ui_1.dim)(` ${reason.split(client.token).join('<key>').split('\n').slice(0, 5).join('\n ')}`));
82
82
  }
83
83
  }
84
84
  if (installed === 0) {
85
- (0, ui_1.note)(found === 0 ? 'Не нашёл клиентов со своей командой подключения.' : 'Добавьте сервер вручную:');
86
- (0, ui_1.out)(` тип: HTTP (streamable)`);
87
- (0, ui_1.out)(` адрес: ${url}`);
88
- (0, ui_1.out)(` заголовок: Authorization: Bearer <ключ>`);
85
+ (0, ui_1.note)(found === 0 ? 'Found no client with its own connect command.' : 'Add the server by hand:');
86
+ (0, ui_1.out)(` type: HTTP (streamable)`);
87
+ (0, ui_1.out)(` address: ${url}`);
88
+ (0, ui_1.out)(` header: Authorization: Bearer <key>`);
89
89
  }
90
90
  // Ключ печатаем только по явной просьбе: см. комментарий в шапке файла.
91
91
  if ((0, args_1.flagBool)(args, 'show-token')) {
92
92
  (0, ui_1.out)('');
93
- (0, ui_1.out)(`${(0, ui_1.bold)('Ключ')} ${client.token}`);
94
- (0, ui_1.note)((0, ui_1.dim)(' Не оставляйте его в переписке с агентом'));
93
+ (0, ui_1.out)(`${(0, ui_1.bold)('Key')} ${client.token}`);
94
+ (0, ui_1.note)((0, ui_1.dim)(' Do not leave it in the chat with the agent'));
95
95
  }
96
96
  else {
97
- (0, ui_1.note)((0, ui_1.dim)(` Ключ лежит в ${(0, node_path_1.join)((0, node_os_1.homedir)(), '.xflow', 'credentials.json')} и на экран не выводится`));
97
+ (0, ui_1.note)((0, ui_1.dim)(` The key lives in ${(0, node_path_1.join)((0, node_os_1.homedir)(), '.xflow', 'credentials.json')} and is not printed`));
98
98
  }
99
99
  }
@@ -41,10 +41,10 @@ async function templates() {
41
41
  const client = (0, session_1.connect)();
42
42
  const { templates: list } = await (0, api_1.apiJson)(client, '/api/v1/templates');
43
43
  if (list.length === 0) {
44
- (0, ui_1.note)('Шаблонов нет');
44
+ (0, ui_1.note)('No templates');
45
45
  return;
46
46
  }
47
- (0, ui_1.table)(list.map((t) => [t.id, t.name, t.is_default ? 'по умолчанию' : '', t.description ?? '']));
47
+ (0, ui_1.table)(list.map((t) => [t.id, t.name, t.is_default ? 'default' : '', t.description ?? '']));
48
48
  }
49
49
  /**
50
50
  * Создать проект: шаблон платформы, локальная папка, проект на платформе.
@@ -62,19 +62,19 @@ async function init(args) {
62
62
  const name = (0, args_1.flagString)(args, 'name') ?? (0, node_path_1.basename)(target);
63
63
  const client = (0, session_1.connect)();
64
64
  if (!isEmptyEnough(target)) {
65
- throw new errors_1.CliError(`Каталог ${target} не пуст`, 'Создайте проект в пустой папке: xflow init my-app. Связать существующую папку с проектом: xflow link <id>');
65
+ throw new errors_1.CliError(`The directory ${target} is not empty`, 'Create the project in an empty folder: xflow init my-app. To link an existing folder to a project: xflow link <id>');
66
66
  }
67
67
  let templateId = (0, args_1.flagString)(args, 'template');
68
68
  if (!templateId) {
69
69
  const { templates: list } = await (0, api_1.apiJson)(client, '/api/v1/templates');
70
70
  templateId = (list.find((t) => t.is_default) ?? list[0])?.id;
71
71
  if (!templateId)
72
- throw new errors_1.CliError('На платформе нет ни одного шаблона');
72
+ throw new errors_1.CliError('The platform has no templates at all');
73
73
  }
74
- (0, ui_1.step)('Забираю шаблон');
74
+ (0, ui_1.step)('Fetching the template');
75
75
  const archive = await (0, api_1.apiBinary)(client, `/api/v1/templates/${templateId}/archive`);
76
76
  const files = (0, zip_1.zipRead)(archive);
77
- (0, ui_1.step)('Создаю проект на платформе');
77
+ (0, ui_1.step)('Creating the project on the platform');
78
78
  const project = await (0, api_1.apiJson)(client, '/api/v1/projects', {
79
79
  method: 'POST',
80
80
  body: { name, database_id: (0, args_1.flagString)(args, 'database') ?? null },
@@ -94,18 +94,18 @@ async function init(args) {
94
94
  (0, config_1.ignoreStateInGit)(target);
95
95
  }
96
96
  catch (e) {
97
- (0, ui_1.note)((0, ui_1.dim)(` Проект «${project.name}» уже создан (${project.id}).`));
98
- (0, ui_1.note)((0, ui_1.dim)(' Разберитесь с папкой и подберите его: xflow link ' + project.id));
97
+ (0, ui_1.note)((0, ui_1.dim)(` The project "${project.name}" is already created (${project.id}).`));
98
+ (0, ui_1.note)((0, ui_1.dim)(' Sort the folder out and pick it up: xflow link ' + project.id));
99
99
  throw e;
100
100
  }
101
101
  (0, skills_1.installSkillQuietly)(target);
102
- (0, ui_1.ok)(`Проект «${project.name}» создан: ${files.length} файлов шаблона`);
102
+ (0, ui_1.ok)(`Project "${project.name}" created: ${files.length} template files`);
103
103
  (0, ui_1.out)('');
104
- (0, ui_1.out)(` ${(0, ui_1.bold)('Дальше:')}`);
104
+ (0, ui_1.out)(` ${(0, ui_1.bold)('Next:')}`);
105
105
  (0, ui_1.out)(` cd ${(0, node_path_1.basename)(target)}`);
106
106
  (0, ui_1.out)(' npm install');
107
- (0, ui_1.out)(' npm run dev # разработка');
108
- (0, ui_1.out)(' xflow deploy # отправить код, собрать и выложить');
107
+ (0, ui_1.out)(' npm run dev # develop');
108
+ (0, ui_1.out)(' xflow deploy # send the code, build and release');
109
109
  }
110
110
  /** Связать текущую папку с уже существующим проектом. */
111
111
  async function link(args) {
@@ -114,7 +114,7 @@ async function link(args) {
114
114
  const client = (0, session_1.connect)(existing);
115
115
  const projectId = args.words[0];
116
116
  if (!projectId) {
117
- throw new errors_1.CliError('Нужен идентификатор проекта', 'Список: xflow projects list');
117
+ throw new errors_1.CliError('A project identifier is required', 'The list: xflow projects list');
118
118
  }
119
119
  const card = await (0, api_1.apiJson)(client, `/api/v1/projects/${projectId}`);
120
120
  const dir = existing ? root : process.cwd();
@@ -128,10 +128,10 @@ async function link(args) {
128
128
  // молча теряет доступ к облачным функциям. Восстанавливаем, но чужой не трогаем.
129
129
  if (!(0, node_fs_1.existsSync)((0, node_path_1.join)(dir, '.env')) && card.project_token) {
130
130
  write(dir, '.env', (0, template_1.envFile)(card.project_token, client.apiUrl, card.functions));
131
- (0, ui_1.note)((0, ui_1.dim)(' Создан .env с токеном проекта и адресами функций'));
131
+ (0, ui_1.note)((0, ui_1.dim)(' Created .env with the project token and the function addresses'));
132
132
  }
133
133
  (0, skills_1.installSkillQuietly)(dir);
134
- (0, ui_1.ok)(`Папка связана с проектом «${card.name}»`);
134
+ (0, ui_1.ok)(`The folder is linked to the project "${card.name}"`);
135
135
  (0, ui_1.note)((0, ui_1.dim)(` ${(0, node_path_1.join)(dir, config_1.CONFIG_FILE)}`));
136
136
  }
137
137
  async function list() {
@@ -139,13 +139,13 @@ async function list() {
139
139
  const client = (0, session_1.connect)(root ? (0, config_1.readConfig)(root) : undefined);
140
140
  const { projects } = await (0, api_1.apiJson)(client, '/api/v1/projects');
141
141
  if (projects.length === 0) {
142
- (0, ui_1.note)('Проектов нет. Создать: xflow init');
142
+ (0, ui_1.note)('No projects. To create one: xflow init');
143
143
  return;
144
144
  }
145
145
  (0, ui_1.table)(projects.map((p) => [
146
146
  p.id,
147
147
  p.name,
148
- p.live_deploy_id ? 'опубликован' : p.dev_deploy_id ? 'только dev' : 'без сборки',
148
+ p.live_deploy_id ? 'published' : p.dev_deploy_id ? 'dev only' : 'no build',
149
149
  (0, ui_1.formatAge)(p.updated_at),
150
150
  ]));
151
151
  }
@@ -154,7 +154,7 @@ async function get(args) {
154
154
  const config = root ? (0, config_1.readConfig)(root) : undefined;
155
155
  const projectId = args.words[0] ?? config?.projectId;
156
156
  if (!projectId) {
157
- throw new errors_1.CliError('Нужен идентификатор проекта', 'Либо запустите команду в папке проекта');
157
+ throw new errors_1.CliError('A project identifier is required', 'Or run the command inside the project folder');
158
158
  }
159
159
  const client = (0, session_1.connect)(config);
160
160
  const card = await (0, api_1.apiJson)(client, `/api/v1/projects/${projectId}`);
@@ -162,19 +162,19 @@ async function get(args) {
162
162
  if (card.description)
163
163
  (0, ui_1.out)(card.description);
164
164
  (0, ui_1.out)('');
165
- (0, ui_1.out)(`Собранная версия: ${card.dev_deploy_id ?? '(сборки нет)'}`);
166
- (0, ui_1.out)(`Видят посетители: ${card.live_deploy_id ?? '(не публиковался)'}`);
167
- (0, ui_1.out)(`База: ${card.database ? `${card.database.name} (${card.database.schema})` : ''}`);
165
+ (0, ui_1.out)(`Built version: ${card.dev_deploy_id ?? '(no build)'}`);
166
+ (0, ui_1.out)(`Visitors see: ${card.live_deploy_id ?? '(never published)'}`);
167
+ (0, ui_1.out)(`Database: ${card.database ? `${card.database.name} (${card.database.schema})` : '(none)'}`);
168
168
  (0, ui_1.out)('');
169
169
  (0, ui_1.out)(card.project_url);
170
170
  if (card.functions.length > 0) {
171
171
  (0, ui_1.out)('');
172
- (0, ui_1.out)('Функции:');
172
+ (0, ui_1.out)('Functions:');
173
173
  (0, ui_1.table)(card.functions.map((f) => [` ${f.name}`, f.status, (0, ui_1.formatAge)(f.last_deployed_at)]));
174
174
  }
175
175
  if (card.schedules.length > 0) {
176
176
  (0, ui_1.out)('');
177
- (0, ui_1.out)('Расписания:');
177
+ (0, ui_1.out)('Schedules:');
178
178
  (0, ui_1.table)(card.schedules.map((s) => [` ${s.function_name}`, s.cron_expression, s.status]));
179
179
  }
180
180
  }
@@ -14,8 +14,8 @@ async function schedulesList() {
14
14
  const client = (0, session_1.connect)(config);
15
15
  const data = await (0, api_1.apiJson)(client, `/api/v1/projects/${config.projectId}/schedules`);
16
16
  if (data.schedules.length === 0) {
17
- (0, ui_1.note)('Расписаний нет');
18
- (0, ui_1.note)((0, ui_1.dim)(' Запускать функцию по времени: xflow schedules set <функция> "0 3 ? * * *"'));
17
+ (0, ui_1.note)('No schedules');
18
+ (0, ui_1.note)((0, ui_1.dim)(' To run a function on a timer: xflow schedules set <function> "0 3 ? * * *"'));
19
19
  return;
20
20
  }
21
21
  (0, ui_1.table)(data.schedules.map((row) => [
@@ -31,21 +31,21 @@ async function schedulesSet(args) {
31
31
  const functionName = args.words[1];
32
32
  const cron = args.words[2];
33
33
  if (!functionName || !cron) {
34
- throw new errors_1.CliError('Нужны имя функции и расписание', 'Например: xflow schedules set nightly-report "0 3 ? * * *" каждый день в 03:00 UTC');
34
+ throw new errors_1.CliError('A function name and a schedule are required', 'For example: xflow schedules set nightly-report "0 3 ? * * *" runs every day at 03:00 UTC');
35
35
  }
36
36
  const row = await (0, api_1.apiJson)(client, `/api/v1/projects/${config.projectId}/schedules`, {
37
37
  method: 'POST',
38
38
  body: { function: functionName, cron, payload: (0, args_1.flagString)(args, 'payload') ?? null },
39
39
  });
40
- (0, ui_1.ok)(`${(0, ui_1.bold)(row.function)} запускается ${row.description}`);
41
- (0, ui_1.note)((0, ui_1.dim)(` Время в UTC. Проверить вручную: xflow functions invoke ${row.function}`));
40
+ (0, ui_1.ok)(`${(0, ui_1.bold)(row.function)} runs ${row.description}`);
41
+ (0, ui_1.note)((0, ui_1.dim)(` The time is UTC. To check by hand: xflow functions invoke ${row.function}`));
42
42
  }
43
43
  async function schedulesRemove(args) {
44
44
  const { config } = (0, config_1.requireProject)();
45
45
  const client = (0, session_1.connect)(config);
46
46
  const functionName = args.words[1];
47
47
  if (!functionName) {
48
- throw new errors_1.CliError('Нужно имя функции', 'Что запускается по времени: xflow schedules list');
48
+ throw new errors_1.CliError('A function name is required', 'What runs on a timer: xflow schedules list');
49
49
  }
50
50
  const cron = args.words[2];
51
51
  const query = new URLSearchParams({ function: functionName });
@@ -53,8 +53,8 @@ async function schedulesRemove(args) {
53
53
  query.set('cron', cron);
54
54
  const result = await (0, api_1.apiJson)(client, `/api/v1/projects/${config.projectId}/schedules?${query.toString()}`, { method: 'DELETE' });
55
55
  if (result.removed === 0) {
56
- (0, ui_1.note)( функции ${functionName} нет такого расписания`);
56
+ (0, ui_1.note)(`Function ${functionName} has no such schedule`);
57
57
  return;
58
58
  }
59
- (0, ui_1.ok)(`Снято расписаний: ${result.removed}`);
59
+ (0, ui_1.ok)(`Schedules removed: ${result.removed}`);
60
60
  }
@@ -46,7 +46,7 @@ environment variables or production logs. Command list: \`xflow help\`.
46
46
  function skillSource() {
47
47
  const path = (0, node_path_1.join)(__dirname, '..', '..', 'skills', 'xflow', 'SKILL.md');
48
48
  if (!(0, node_fs_1.existsSync)(path)) {
49
- throw new errors_1.CliError('В пакете CLI нет файла скилла', 'Переустановите @getxflow/cli');
49
+ throw new errors_1.CliError('The CLI package has no skill file', 'Reinstall @getxflow/cli');
50
50
  }
51
51
  return (0, node_fs_1.readFileSync)(path, 'utf-8');
52
52
  }
@@ -109,11 +109,11 @@ function install(base, global) {
109
109
  function installSkillQuietly(base) {
110
110
  try {
111
111
  if (install(base, false).length > 0) {
112
- (0, ui_1.note)((0, ui_1.dim)(' Инструкция о платформе разложена для ИИ-агента'));
112
+ (0, ui_1.note)((0, ui_1.dim)(' The platform instructions are laid out for the AI agent'));
113
113
  }
114
114
  }
115
115
  catch {
116
- (0, ui_1.note)((0, ui_1.dim)(' Инструкцию для ИИ-агента положить не удалось: xflow skills'));
116
+ (0, ui_1.note)((0, ui_1.dim)(' Could not lay out the AI agent instructions: xflow skills'));
117
117
  }
118
118
  }
119
119
  function skills(args) {
@@ -122,7 +122,7 @@ function skills(args) {
122
122
  for (const path of touched)
123
123
  (0, ui_1.out)(path);
124
124
  if (touched.length === 0)
125
- (0, ui_1.note)((0, ui_1.dim)(' всё уже актуально'));
126
- (0, ui_1.ok)(global ? 'Скилл xflow доступен во всех проектах' : 'Скилл xflow на месте');
127
- (0, ui_1.note)((0, ui_1.dim)(' После обновления CLI повторите команду: скилл обновляется вместе с ним'));
125
+ (0, ui_1.note)((0, ui_1.dim)(' everything is already up to date'));
126
+ (0, ui_1.ok)(global ? 'The xflow skill is available in every project' : 'The xflow skill is in place');
127
+ (0, ui_1.note)((0, ui_1.dim)(' After a CLI update run the command again: the skill ships with it'));
128
128
  }