@getxflow/cli 1.0.7 → 1.0.8

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/session.js CHANGED
@@ -55,12 +55,18 @@ function anonymous(config) {
55
55
  */
56
56
  async function findOrgFor(apiUrl, path) {
57
57
  const dead = [];
58
+ let frozen = null;
58
59
  for (const candidate of (0, credentials_1.listOrgs)(apiUrl)) {
59
60
  try {
60
61
  await (0, api_1.apiJson)({ apiUrl, token: candidate.token }, path);
61
62
  return { hit: candidate, dead };
62
63
  }
63
64
  catch (e) {
65
+ // A frozen organization refuses every path, so it may or may not hold this one.
66
+ if (e instanceof api_1.ApiError && e.limit?.code === 'subscription_blocked') {
67
+ frozen ??= e;
68
+ continue;
69
+ }
64
70
  if (e instanceof api_1.ApiError && (e.status === 401 || e.status === 403)) {
65
71
  dead.push(candidate);
66
72
  continue;
@@ -70,6 +76,8 @@ async function findOrgFor(apiUrl, path) {
70
76
  throw e;
71
77
  }
72
78
  }
79
+ if (frozen)
80
+ throw frozen;
73
81
  return { hit: null, dead };
74
82
  }
75
83
  function findProjectOrg(apiUrl, projectId) {
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.7';
5
+ exports.CLI_VERSION = '1.0.8';
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.7",
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.8",
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.7, so the copy you are reading can be older than
26
+ These instructions ship with xflow CLI 1.0.8, 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
@@ -121,6 +121,10 @@ halfway through the work into a sentence said before it starts.
121
121
  deleting it.
122
122
  - Function minutes are shared by the whole organization: every app, page and schedule spends
123
123
  them. When they run out, every function of the organization stops (`functions_stopped`).
124
+ - An unpaid plan stops the functions at once, and five days later freezes the organization:
125
+ every command except `xflow whoami`, and every MCP tool, answers `subscription_blocked`,
126
+ apps and pages show a "suspended" page. Nothing is deleted and no code fixes it: tell the
127
+ person that the owner pays the invoice in the organization settings, section «Тариф» (Plan).
124
128
  - The cap on how long one function call may run is written into the function when it is
125
129
  deployed, so a plan change reaches the cloud only with the next `xflow deploy functions` of
126
130
  each project, in both directions.
@@ -240,8 +244,8 @@ A person sets this in the web interface; you cannot, and you should not ask for
240
244
  - The plan decides which levels exist: on Free an app opens only inside xflow, Start adds the
241
245
  direct address and "Anyone with the link".
242
246
  - At the closed level the platform signs people in, and a function receives the person in
243
- `event.xflow.user` (`id`, `role` in the organization, `name`). Keep roles inside the app
244
- (doctor, receptionist) in your own table keyed by `user.id`.
247
+ `event.xflow.user` (`id`, `role` in the organization, `name`, `access` to the project).
248
+ Keep roles inside the app (doctor, receptionist) in your own table keyed by `user.id`.
245
249
  - People outside the organization (patients, pickers, clients) are not xflow accounts: make the
246
250
  app "Anyone with the link" and give it its own sign-in, see below.
247
251
  - At "Anyone with the link" the platform signs nobody in: `event.xflow.user` is null for
@@ -395,8 +399,8 @@ exports.handler = async (event) => {
395
399
  // project), 'external' (an outside service with a key), 'schedule',
396
400
  // 'platform' (a person through xflow functions invoke or the web interface)
397
401
  // slug: the address name of the calling app (<id>--<org>) or page, null otherwise
398
- // user: { id, role, name } of a signed-in person,
399
- // null for anonymous visitors, pages, keys
402
+ // user: { id, role, name, access } of a signed-in person, null for anonymous
403
+ // visitors, pages, keys. access: 'view' | 'edit' on this project
400
404
  // ip: the visitor's address, null for a schedule
401
405
  }
402
406
  ```
@@ -404,6 +408,18 @@ exports.handler = async (event) => {
404
408
  Never trust an identity that arrives in the body or in a header of the request: the page lives
405
409
  on someone else's computer. `event.xflow` is the only identity the platform stands behind.
406
410
 
411
+ Project access is separate from the role: a person who only views the project (`access:
412
+ 'view'`) still reaches every function. A function that writes refuses them:
413
+
414
+ ```js
415
+ if (event.xflow.user && event.xflow.user.access !== 'edit') {
416
+ return { statusCode: 403, body: JSON.stringify({ error: 'Read-only access' }) }
417
+ }
418
+ ```
419
+
420
+ To remember who did something, store `user.id` and `user.name` in the row: the name is never
421
+ empty, and the app has no other way to look up other people.
422
+
407
423
  A function that changes data should say who may call it instead of checking the role by hand:
408
424
 
409
425
  ```js