@getxflow/cli 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -223,11 +223,15 @@ async function run(args) {
223
223
  await (0, pages_1.pagesRollback)(rest);
224
224
  return;
225
225
  }
226
+ if (second === 'rm' || second === 'remove') {
227
+ await (0, pages_1.pagesRemove)(rest);
228
+ return;
229
+ }
226
230
  if (second === undefined || second === 'list') {
227
231
  await (0, pages_1.pagesList)(rest);
228
232
  return;
229
233
  }
230
- throw new errors_1.CliError(`Unknown command: pages ${second}`, 'Available: list, create, deploy, status and rollback');
234
+ throw new errors_1.CliError(`Unknown command: pages ${second}`, 'Available: list, create, deploy, status, rollback and rm');
231
235
  case 'status':
232
236
  await (0, sources_1.status)();
233
237
  return;
@@ -5,6 +5,7 @@ exports.pagesCreate = pagesCreate;
5
5
  exports.pagesDeploy = pagesDeploy;
6
6
  exports.pagesStatus = pagesStatus;
7
7
  exports.pagesRollback = pagesRollback;
8
+ exports.pagesRemove = pagesRemove;
8
9
  const node_fs_1 = require("node:fs");
9
10
  const node_path_1 = require("node:path");
10
11
  const api_1 = require("../api");
@@ -231,3 +232,27 @@ async function pagesRollback(args) {
231
232
  (0, ui_1.warn)(`The page is switched off: version ${revision} is current and goes live when it is switched on`);
232
233
  (0, ui_1.out)(page.url);
233
234
  }
235
+ async function pagesRemove(args) {
236
+ const ref = args.words[1];
237
+ if (!ref) {
238
+ throw new errors_1.CliError('A page is required', 'The name from its address, or its id. The pages: xflow pages');
239
+ }
240
+ // The page is read first: deleting is confirmed by the name in the address, and the
241
+ // page can be named here by its id.
242
+ const client = pagesClient();
243
+ const { page } = await (0, api_1.apiJson)(client, pagePath(ref));
244
+ const query = (0, args_1.flagBool)(args, 'yes') ? `?confirm=${encodeURIComponent(page.slug)}` : '';
245
+ try {
246
+ await (0, api_1.apiJson)(client, `${pagePath(page.id)}${query}`, { method: 'DELETE' });
247
+ }
248
+ catch (e) {
249
+ // The platform names what goes and refuses until the answer is seen. Said in the
250
+ // words of this command, not of the API.
251
+ if (e instanceof api_1.ApiError && e.code === 'confirm_required') {
252
+ throw new errors_1.CliError(e.message, `To delete it anyway: xflow pages rm ${page.slug} --yes`);
253
+ }
254
+ throw e;
255
+ }
256
+ (0, ui_1.ok)(`The page is deleted: ${page.url} shows nothing now`);
257
+ (0, ui_1.note)((0, ui_1.dim)(` Its versions are gone for good, the name ${page.slug} stays reserved for the organization`));
258
+ }
package/dist/flags.js CHANGED
@@ -95,6 +95,8 @@ exports.KNOWN = {
95
95
  'pages deploy': ['message'],
96
96
  'pages status': ['json'],
97
97
  'pages rollback': [],
98
+ 'pages rm': ['yes'],
99
+ 'pages remove': ['yes'],
98
100
  status: [],
99
101
  dev: [],
100
102
  pull: ['into', 'force', 'revision'],
package/dist/help.js CHANGED
@@ -108,6 +108,7 @@ ${(0, ui_1.bold)('Pages')}
108
108
  upload the site: live at once, there is no publish step
109
109
  xflow pages status <page> the page and its versions
110
110
  xflow pages rollback <page> <N> put an earlier version back on the address
111
+ xflow pages rm <page> --yes delete the page for good: the address empties
111
112
 
112
113
  ${(0, ui_1.bold)('Reference')}
113
114
  xflow logs [--limit N] browser errors of the published apps
@@ -282,6 +283,8 @@ the platform builds, with versions, access levels and a database through functio
282
283
  -m <text> what changed, one short line: it names the version
283
284
  xflow pages status <page> the page and its versions
284
285
  xflow pages rollback <page> <N> put version N back on the address
286
+ xflow pages rm <page> delete the page
287
+ --yes without it the command names what goes and stops
285
288
 
286
289
  ${(0, ui_1.bold)('<path>')} is a folder with index.html at the top, its .zip, or one .html file.
287
290
  ${(0, ui_1.bold)('<page>')} is the name from the address or the id. ${(0, ui_1.bold)('--json')} on list and status
@@ -304,9 +307,14 @@ add the page name there and run ${(0, ui_1.bold)('xflow deploy functions')}. The
304
307
  Such a function is open to the whole internet. Search engines are asked not to index pages,
305
308
  so do not promise SEO.
306
309
 
310
+ ${(0, ui_1.bold)('Deleting is final.')} The address goes empty and every version of the page is gone: there
311
+ is no undo and no copy on the platform side. The name stays reserved for the organization, so
312
+ the same ${(0, ui_1.bold)('--slug')} is free for the next page of yours and taken for everyone else. A
313
+ throwaway page put up to try something is worth deleting the same way it was made.
314
+
307
315
  A page takes the key of the whole organization, the one ${(0, ui_1.bold)('xflow login')} issues: a key
308
- scoped to projects is refused. Switching a page off and deleting it are for a person, in the
309
- platform interface.`,
316
+ scoped to projects is refused. A page is deleted by whoever made it, or by the owner of the
317
+ organization. Switching a page off for a while is for a person, in the platform interface.`,
310
318
  update: `${(0, ui_1.bold)('xflow update')}: bring the CLI up to date
311
319
 
312
320
  Installs the published version and, if anything changed, rewrites the platform
package/dist/limits.js CHANGED
@@ -13,7 +13,7 @@ const DENIAL_LABELS = {
13
13
  limit_functions: 'cloud functions',
14
14
  limit_builds: 'builds this month',
15
15
  limit_builds_concurrent: 'builds running at once',
16
- limit_seats: 'seats in the organization',
16
+ limit_seats: 'people in the organization',
17
17
  limit_schedule_interval: 'schedule frequency',
18
18
  db_write_locked: 'database volume, writes are off',
19
19
  plan_no_schedules: 'schedules',
@@ -38,8 +38,7 @@ const QUOTA_ORDER = [
38
38
  'connections',
39
39
  'pages',
40
40
  'functions',
41
- 'developers',
42
- 'members',
41
+ 'people',
43
42
  'database_mb',
44
43
  'files_mb',
45
44
  'function_minutes',
@@ -50,8 +49,7 @@ const QUOTA_LABELS = {
50
49
  connections: 'Active connections',
51
50
  pages: 'Public pages',
52
51
  functions: 'Cloud functions',
53
- developers: 'Developers',
54
- members: 'Staff',
52
+ people: 'People',
55
53
  database_mb: 'Database',
56
54
  files_mb: 'Files',
57
55
  function_minutes: 'Function minutes',
package/dist/template.js CHANGED
@@ -2,30 +2,30 @@
2
2
  /** Files the CLI lays into a new project root: the app itself comes from the platform template. */
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.projectScaffold = projectScaffold;
5
- const GITIGNORE = `node_modules/
6
- dist/
7
- .xflow/
8
- .env
9
- .env.*
5
+ const GITIGNORE = `node_modules/
6
+ dist/
7
+ .xflow/
8
+ .env
9
+ .env.*
10
10
  `;
11
- const README = `# %NAME%
12
-
13
- A project on XFlow: one database, cloud functions and the apps that use them.
14
-
15
- \`\`\`
16
- xflow.json projectId, and which apps and pages may call each function
17
- package.json dependencies of the functions
18
- functions/<name>/ cloud functions, shared by every app
19
- migrations/ the database of the project
20
- apps/<folder>/ an app: its own code, versions and address
21
- \`\`\`
22
-
23
- \`\`\`bash
24
- xflow deploy functions -m "..." # ship the functions and their callers
25
- xflow deploy apps/<folder> -m "..." # build one app
26
- xflow publish # inside an app folder: show the newest build
27
- xflow status # what is on the server
28
- \`\`\`
11
+ const README = `# %NAME%
12
+
13
+ A project on XFlow: one database, cloud functions and the apps that use them.
14
+
15
+ \`\`\`
16
+ xflow.json projectId, and which apps and pages may call each function
17
+ package.json dependencies of the functions
18
+ functions/<name>/ cloud functions, shared by every app
19
+ migrations/ the database of the project
20
+ apps/<folder>/ an app: its own code, versions and address
21
+ \`\`\`
22
+
23
+ \`\`\`bash
24
+ xflow deploy functions -m "..." # ship the functions and their callers
25
+ xflow deploy apps/<folder> -m "..." # build one app
26
+ xflow publish # inside an app folder: show the newest build
27
+ xflow status # what is on the server
28
+ \`\`\`
29
29
  `;
30
30
  function projectScaffold(projectName) {
31
31
  return [
package/dist/version.js CHANGED
@@ -2,6 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DEFAULT_API_URL = exports.CLI_VERSION = void 0;
4
4
  /** Keep in sync with cli/package.json. */
5
- exports.CLI_VERSION = '1.0.0';
5
+ exports.CLI_VERSION = '1.0.2';
6
6
  /** Overridden by XFLOW_API_URL or the `api` field in xflow.json. */
7
7
  exports.DEFAULT_API_URL = 'https://app.getxflow.com';
package/package.json CHANGED
@@ -1,24 +1,24 @@
1
- {
2
- "name": "@getxflow/cli",
3
- "version": "1.0.0",
4
- "description": "CLI for the XFlow platform: projects with apps, functions and a database; source sync, deployment and publishing",
5
- "license": "UNLICENSED",
6
- "engines": {
7
- "node": ">=20"
8
- },
9
- "bin": {
10
- "xflow": "dist/bin.js"
11
- },
12
- "files": [
13
- "dist",
14
- "skills"
15
- ],
16
- "scripts": {
17
- "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
18
- "build": "npm run clean && tsc -p tsconfig.json",
19
- "prepublishOnly": "npm run build"
20
- },
21
- "publishConfig": {
22
- "access": "public"
23
- }
24
- }
1
+ {
2
+ "name": "@getxflow/cli",
3
+ "version": "1.0.2",
4
+ "description": "CLI for the XFlow platform: projects with apps, functions and a database; source sync, deployment and publishing",
5
+ "license": "UNLICENSED",
6
+ "engines": {
7
+ "node": ">=20"
8
+ },
9
+ "bin": {
10
+ "xflow": "dist/bin.js"
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "skills"
15
+ ],
16
+ "scripts": {
17
+ "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
18
+ "build": "npm run clean && tsc -p tsconfig.json",
19
+ "prepublishOnly": "npm run build"
20
+ },
21
+ "publishConfig": {
22
+ "access": "public"
23
+ }
24
+ }
@@ -23,7 +23,7 @@ contains the fix.
23
23
 
24
24
  ## Keeping these instructions current
25
25
 
26
- These instructions ship with xflow CLI 1.0.0, so the copy you are reading can be older than
26
+ These instructions ship with xflow CLI 1.0.2, so the copy you are reading can be older than
27
27
  the CLI answering your commands.
28
28
 
29
29
  When the CLI says this file does not match it, run `xflow skills --refresh` and then **read
@@ -108,7 +108,7 @@ One folder per project, apps in subfolders:
108
108
  ## Plan limits
109
109
 
110
110
  The organization runs on a plan with limits: apps, functions, pages, active connections,
111
- seats, database and file storage, function minutes, builds per month and at once, schedules
111
+ people in the organization, database and file storage, function minutes, builds per month and at once, schedules
112
112
  and how often they may run, and which access levels an app may have. Most are finite; a plan
113
113
  may leave one uncapped, and then `whoami` prints `no limit` instead of a ceiling.
114
114
  `xflow whoami` prints every one of them next to what is used, and the rights of your own key
@@ -198,7 +198,7 @@ An app:
198
198
  - The platform files stay byte for byte as they arrived: `src/lib/platform-auth.ts`,
199
199
  `src/contexts/platform-auth-context.tsx`, `src/hooks/use-platform-auth.ts`,
200
200
  `src/utils/error-logger.ts`, `src/lib/xflow.ts`. The code keeps calling `initPlatformAuth()`
201
- and `initErrorLogger()`, importing `index.css`, mounting `ThemeProvider`, and the Tailwind
201
+ and `initErrorLogger()` and importing `index.css`, and the Tailwind
202
202
  `content` globs keep covering `src/**`. A rejection names the revision to take the original
203
203
  from: `xflow pull --revision N --into ./original`, then copy the file back.
204
204
 
@@ -214,10 +214,15 @@ Fix the first two before you tell the person the app is done.
214
214
  ## The template is a starter kit
215
215
 
216
216
  - `src/components/ui` and `src/components/blocks` are a starting set: change them, restyle them
217
- or delete what you do not use. Your own colors and inline styles are allowed.
218
- - While any template component is left, the theme token names in `src/index.css` (`:root` and
219
- `.dark`) and their mapping in the Tailwind config stay: those components paint with them.
220
- Values change freely.
217
+ or delete what you do not use. Any colors, your own color scheme, inline styles, a single
218
+ theme without dark mode: all allowed.
219
+ - The template looks like the xflow interface: warm grays, hairline controls (`ctl-solid`,
220
+ `ctl-raised`, `ctl-inset`, `ctl-select`), segments (`seg-track`, `seg-on`) and surfaces
221
+ (`surface-raised`, `surface-pop`, `surface-modal`) in `src/index.css`.
222
+ - While any template component is left, the theme token names in `:root` of `src/index.css`
223
+ and their `hsl(var(--name))` mapping in the Tailwind config stay: those components paint with
224
+ them. Values change freely; they are HSL components (`216 95% 40%`), not `#hex`. Your own
225
+ colors go under new names.
221
226
  - Images and other assets: import them from `src/` (`import logo from './assets/logo.png'`)
222
227
  so the build puts them next to the code. An absolute path into `public/` (`/logo.png`) goes
223
228
  through the access check of the app and fails inside the xflow frame in Safari.
@@ -228,14 +233,19 @@ A person sets this in the web interface; you cannot, and you should not ask for
228
233
 
229
234
  | Level | Who gets in |
230
235
  |---|---|
231
- | Project members | people signed in to xflow who have access to the project |
236
+ | Project members | people who have access to the project: the team through xflow, staff with a login |
232
237
  | By list | the members named for this app |
233
238
  | Anyone with the link | anybody, no sign-in at all |
234
239
 
235
240
  - The plan decides which levels exist: on Free an app opens only inside xflow, Start adds the
236
- direct address and "By list", Business adds "Anyone with the link".
241
+ direct address, "By list" and "Anyone with the link".
237
242
  - At the two closed levels the platform signs people in, and a function receives the person in
238
- `event.xflow.user` (`id`, `role` in the organization).
243
+ `event.xflow.user` (`id`, `role` in the organization, `name`, `login`).
244
+ - Staff (a doctor, a picker) never sign in to xflow: an admin gives them a login and a password,
245
+ they enter at the address of the app, `login` is set for them and null for the team. Keep
246
+ roles inside the app (doctor, receptionist) in your own table keyed by `user.id`.
247
+ - A "Sign out" button: `const r = await fetch('/_xflow/logout', { method: 'POST' })`, then
248
+ `location.href = (await r.json()).next`. It ends every sign-in of the person.
239
249
  - At "Anyone with the link" the platform signs nobody in: `event.xflow.user` is null for
240
250
  visitors. Accounts, sign-in and who sees which rows are the code of your app, see below.
241
251
  - `usePlatformAuth()` in the frontend knows the person only inside the xflow frame, and even
@@ -383,7 +393,8 @@ exports.handler = async (event) => {
383
393
  // project), 'external' (an outside service with a key), 'schedule',
384
394
  // 'platform' (a person through xflow functions invoke or the web interface)
385
395
  // slug: the address name of the calling app (<id>--<org>) or page, null otherwise
386
- // user: { id, role } of a signed-in member, null for anonymous visitors, pages, keys
396
+ // user: { id, role, name, login } of a signed-in person (login only for staff),
397
+ // null for anonymous visitors, pages, keys
387
398
  // ip: the visitor's address, null for a schedule
388
399
  }
389
400
  ```
@@ -399,7 +410,9 @@ exports.minRole = 'developer' // 'member' | 'developer' | 'owner'
399
410
 
400
411
  The platform refuses anything below that role before your code runs; it applies to people
401
412
  (apps, pages, `functions invoke`), not to keys and schedules. `member` is the people who only
402
- use the apps (staff of a clinic, managers), `developer` and `owner` build them. Without the line
413
+ use the apps (staff of a clinic, managers), `developer` and `owner` build them. In the web
414
+ interface these roles are called «Просмотр» (`member`), «Редактор» (`developer`) and «Админ»
415
+ (`owner`). Without the line
403
416
  every member of the project can call the function. Losing access
404
417
  closes the function within a minute.
405
418
 
@@ -512,9 +525,11 @@ and zsh, cmd.exe wants double ones; a query with quotes of its own goes in a `.s
512
525
  the project, `xflow db query ../q.sql`.
513
526
 
514
527
  The platform keeps no database history and no backups, so anything that destroys data
515
- (`DROP TABLE`, `DROP COLUMN`, `TRUNCATE`, `DELETE FROM` without a condition) needs both the
516
- `--allow-destructive` flag and the right to destroy data on the key, which is off by default and
517
- only its owner turns on. When the refusal is about the right, the flag changes nothing: say what
528
+ (`DROP TABLE`, `DROP COLUMN`, `TRUNCATE`, `DELETE`) needs both the `--allow-destructive` flag and
529
+ the right to destroy data on the key, which is off by default and only its owner turns on. It is
530
+ judged by the result, not the text: a migration that dropped or emptied a table, dropped a column
531
+ or deleted rows is rolled back, however the SQL was written. `BEGIN`/`COMMIT` in a migration are
532
+ refused: each one already runs in its own transaction. When the refusal is about the right, the flag changes nothing: say what
518
533
  needs deleting and why, and let the person decide. With both in place the affected tables are
519
534
  dumped first (up to 50 000 rows each) and kept for 7 days; getting a dump back is a request to
520
535
  the person, no command does it. Check with `--dry-run`.
@@ -607,6 +622,10 @@ the address opens it, without signing in.
607
622
  - **Both are public the moment the command finishes**: no draft, no publish step.
608
623
  - `xflow pages status <page>` lists the versions, `xflow pages rollback <page> <N>` puts one back
609
624
  in seconds; the files of the last 5 are kept.
625
+ - `xflow pages rm <page> --yes` deletes the page: the address goes empty and every version is
626
+ gone for good, without `--yes` the command names what goes and stops. The name stays reserved
627
+ for the organization. A page put up to try something out is yours to take down when you are
628
+ done: do not leave it standing.
610
629
  - Names are 3-40 characters. At most 100 pages per organization, 20 new and 200 uploads a day,
611
630
  10 seconds between uploads of one page: batch changes into one upload.
612
631
  - Static files only, what the build writes (`dist`), never a folder with `package.json`. At most
@@ -618,7 +637,8 @@ the address opens it, without signing in.
618
637
  - Search engines are asked not to index pages or apps: do not promise SEO.
619
638
  - The tab icon is the platform's on every page: do not spend a request on a favicon of your own.
620
639
  - The MCP `pages` tool uploads one self-contained HTML document up to 150 KB.
621
- - Switching a page off and deleting it are for a person, in the platform interface.
640
+ - A page is deleted by whoever made it, or by the owner of the organization. Switching a page
641
+ off for a while is for a person, in the platform interface.
622
642
 
623
643
  ## Organizations and keys
624
644
 
@@ -631,6 +651,10 @@ lists the commands that take them). A "not found" on a project you know exists u
631
651
  key belongs to another organization: check `xflow org`; signing in to a missing organization is
632
652
  `xflow login`, and that needs a person with a browser.
633
653
 
654
+ A key has its owner's level in each project. With view-only access reading works (`projects
655
+ get`, logs, `db query`, `pull`), and every change fails with `Your access to this project is
656
+ view-only`. That is not a bug to work around: tell the person, edit access is theirs to ask for.
657
+
634
658
  ## Direct access without the terminal
635
659
 
636
660
  The platform also exposes an MCP server, connected with `xflow mcp install`. When its tools are
@@ -653,6 +677,9 @@ people are set by a person in the web interface.
653
677
 
654
678
  - Take a block from `src/components/blocks` or a primitive from `src/components/ui` before
655
679
  writing your own layout; replace or delete them when the design needs something else.
680
+ - Dropdowns, checkboxes, radios and dates through `Select`, `Checkbox`, `RadioGroup`,
681
+ `DatePicker` from `src/components/ui`: a bare `<select>` opens the system list in Safari
682
+ and Firefox, out of the app's style.
656
683
  - Every screen starts with a clear header; header, filters and content in one column.
657
684
  - Draw all four states: loading, error, empty, data. An empty screen says what appears here and
658
685
  offers an action.