@getxflow/cli 0.31.0 → 0.32.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/commands/functions.js +9 -5
- package/dist/help.js +9 -8
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/skills/xflow/SKILL.md +34 -14
|
@@ -9,11 +9,12 @@ const errors_1 = require("../errors");
|
|
|
9
9
|
const json_arg_1 = require("../json-arg");
|
|
10
10
|
const session_1 = require("../session");
|
|
11
11
|
const ui_1 = require("../ui");
|
|
12
|
-
/** Who can call the function:
|
|
12
|
+
/** Who can call the function: anyone, an outside service holding a key, or the app alone. */
|
|
13
13
|
function accessLabel(fn) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
const keys = fn.external_keys ? `external (${fn.external_keys} key${fn.external_keys > 1 ? 's' : ''})` : '';
|
|
15
|
+
if (fn.is_public)
|
|
16
|
+
return keys ? `public, ${keys}` : 'public';
|
|
17
|
+
return keys || 'in-app only';
|
|
17
18
|
}
|
|
18
19
|
const FUNCTIONS_DIR = 'functions';
|
|
19
20
|
async function functionsList(args) {
|
|
@@ -28,6 +29,9 @@ async function functionsList(args) {
|
|
|
28
29
|
fn.status === 'deployed' ? 'deployed' : fn.status === 'failed' ? 'failed' : fn.status,
|
|
29
30
|
accessLabel(fn),
|
|
30
31
|
fn.last_deployed_at ? (0, ui_1.formatAge)(fn.last_deployed_at) : '-',
|
|
32
|
+
// The address is not a secret (the app bundle carries it): a page or a
|
|
33
|
+
// webhook needs it, and asking the user to copy it out of the UI was a detour.
|
|
34
|
+
fn.invoke_url ?? '',
|
|
31
35
|
fn.error_message ?? '',
|
|
32
36
|
]));
|
|
33
37
|
}
|
|
@@ -50,7 +54,7 @@ function prettyBody(text) {
|
|
|
50
54
|
* this package ships without dependencies and cannot reach into the platform. One test
|
|
51
55
|
* holds the two halves together.
|
|
52
56
|
*/
|
|
53
|
-
const WRAPPER_REASONS = ['no_pass', 'invalid', 'expired', 'no_access', 'unavailable', 'role'];
|
|
57
|
+
const WRAPPER_REASONS = ['no_pass', 'invalid', 'expired', 'no_access', 'unavailable', 'role', 'access'];
|
|
54
58
|
/** What to look at next, when the function did not answer with a success. */
|
|
55
59
|
function failureHint(name, status, body) {
|
|
56
60
|
let reason;
|
package/dist/help.js
CHANGED
|
@@ -64,7 +64,7 @@ ${(0, ui_1.bold)('Releasing')}
|
|
|
64
64
|
xflow deployments version history
|
|
65
65
|
|
|
66
66
|
${(0, ui_1.bold)('Functions')}
|
|
67
|
-
xflow functions list what is deployed
|
|
67
|
+
xflow functions list what is deployed, who may call it, the address
|
|
68
68
|
xflow functions invoke <name> [--data '{"a":1}']
|
|
69
69
|
call a function and print the answer
|
|
70
70
|
xflow functions logs [name] function crashes: stack and console output
|
|
@@ -261,13 +261,14 @@ build it and upload what the build writes. Dot entries are skipped. At most 5 MB
|
|
|
261
261
|
unpacked and 1000 files.
|
|
262
262
|
|
|
263
263
|
The page is public, so a file holding a secret key is refused whole: every visitor would read
|
|
264
|
-
it. A
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
and deleting it are for a person, in the
|
|
264
|
+
it. A page that needs a backend (a form, a lookup) calls a cloud function that declares
|
|
265
|
+
${(0, ui_1.bold)("exports.access = 'public'")}: such a function answers without a pass and without a
|
|
266
|
+
key, and the page calls its address directly (${(0, ui_1.bold)('xflow functions list')} prints it). No
|
|
267
|
+
key is baked into the page. Search engines are asked not to index pages, so do not promise SEO.
|
|
268
|
+
|
|
269
|
+
A page takes the key of the whole organization, the one ${(0, ui_1.bold)('xflow login')} issues: a key
|
|
270
|
+
scoped to projects is refused. Switching a page off and deleting it are for a person, in the
|
|
271
|
+
platform interface.`,
|
|
271
272
|
update: `${(0, ui_1.bold)('xflow update')}: bring the CLI up to date
|
|
272
273
|
|
|
273
274
|
Installs the published version and, if anything changed, rewrites the platform
|
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 = '0.
|
|
5
|
+
exports.CLI_VERSION = '0.32.0';
|
|
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
package/skills/xflow/SKILL.md
CHANGED
|
@@ -22,7 +22,7 @@ contains the fix.
|
|
|
22
22
|
|
|
23
23
|
## Keeping these instructions current
|
|
24
24
|
|
|
25
|
-
These instructions ship with xflow CLI 0.
|
|
25
|
+
These instructions ship with xflow CLI 0.32.0, so the copy you are reading can be older than
|
|
26
26
|
the CLI answering your commands.
|
|
27
27
|
|
|
28
28
|
When the CLI says this file does not match it, run `xflow skills --refresh` and then **read
|
|
@@ -288,7 +288,8 @@ with the platform on every call, and the handler receives the answer in `event.x
|
|
|
288
288
|
exports.handler = async (event) => {
|
|
289
289
|
const { caller, user } = event.xflow
|
|
290
290
|
// caller: 'visitor' (a person), 'service' (another function of this project),
|
|
291
|
-
// 'external' (an outside service with a key), 'schedule' (a timer run)
|
|
291
|
+
// 'external' (an outside service with a key), 'schedule' (a timer run),
|
|
292
|
+
// 'public' (anyone, see below)
|
|
292
293
|
// user: { id, role } for a visitor, null for everything else
|
|
293
294
|
}
|
|
294
295
|
```
|
|
@@ -321,13 +322,30 @@ An outside service (a payment webhook, a bot, a CRM) has no person behind it and
|
|
|
321
322
|
that one function. Keys are not issued by default and the CLI cannot create one: a human issues
|
|
322
323
|
it in the project settings, «Облачные функции» → the function → «Настройки»; a standalone
|
|
323
324
|
function has a screen of its own, «Функции» → the function → «Настройки». Ask the user to do
|
|
324
|
-
that
|
|
325
|
-
`
|
|
326
|
-
without downtime, not to serve a second consumer.
|
|
325
|
+
that — never invent another way in. The service sends the key in the `X-Function-Key` header
|
|
326
|
+
to the address `xflow functions list` prints. A function holds at most two keys, the second
|
|
327
|
+
one to replace the first without downtime, not to serve a second consumer.
|
|
327
328
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
329
|
+
A function behind a public page (a form on a landing, a lookup, a calculator on
|
|
330
|
+
`xflowpage.ru`) has neither a person nor a key: every visitor calls it. Declare that in the
|
|
331
|
+
code and nothing else is needed, no key, no setting, nobody to ask:
|
|
332
|
+
|
|
333
|
+
```js
|
|
334
|
+
exports.access = 'public'
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
The wrapper then lets a call without any credential through, with `caller: 'public'` and
|
|
338
|
+
`user: null`, and never asks the platform. The page calls the address from `xflow functions
|
|
339
|
+
list` directly (CORS is open) and bakes no key into its files. Everything else stays as it
|
|
340
|
+
is: a call that does carry a pass or a key is still checked. Use it only for a function whose
|
|
341
|
+
work is fine for anyone on the internet to trigger: it cannot hold `minRole`, and it must not
|
|
342
|
+
write or delete data on the caller's word, there is no caller to trust. The platform marks
|
|
343
|
+
such a function «Открыта всем»
|
|
344
|
+
in the interface, and removing the line and deploying closes it again.
|
|
345
|
+
|
|
346
|
+
`xflow functions list` shows who can reach each function: `in-app only` (answers only inside
|
|
347
|
+
the application), `external (N keys)` (a human issued external access), `public` (the code
|
|
348
|
+
opened it), and the address of every deployed function. Key values are never shown there.
|
|
331
349
|
|
|
332
350
|
## Secrets and connected accounts
|
|
333
351
|
|
|
@@ -605,15 +623,17 @@ platform: pick a page when the audience is outside the organization.
|
|
|
605
623
|
- Static files only: HTML, CSS, JS, images, fonts, PDF, audio, video. No server code, database
|
|
606
624
|
or functions on a page. Upload what the build writes (`dist`), never a folder with
|
|
607
625
|
`package.json`. At most 5 MB zipped, 25 MB unpacked, 1000 files.
|
|
626
|
+
- A page that needs a backend (a form, a lookup) calls a cloud function of a project that
|
|
627
|
+
declares `exports.access = 'public'` (see «Who is calling»): the page fetches the address
|
|
628
|
+
from `xflow functions list` directly, with no key and no header. The function lives in a
|
|
629
|
+
project, the page does not: deploy the function first, then upload the page.
|
|
608
630
|
- Everything in a page is readable by every visitor. Never put a key, token or password into
|
|
609
|
-
it: a known key format gets the upload refused, anything else simply leaks.
|
|
610
|
-
the exception: a person issues it in the settings of the function, and a form on a page may
|
|
611
|
-
call that function with it. So can every visitor.
|
|
631
|
+
it: a known key format gets the upload refused, anything else simply leaks.
|
|
612
632
|
- Search engines are asked not to index pages: do not promise SEO.
|
|
613
633
|
- The MCP `pages` tool uploads one self-contained HTML document up to 150 KB.
|
|
614
|
-
- Pages take the key of the whole organization, the one `xflow login` issues
|
|
615
|
-
|
|
616
|
-
|
|
634
|
+
- Pages take the key of the whole organization, the one `xflow login` issues; a key scoped to
|
|
635
|
+
projects is refused, and that refusal is not yours to fix. Switching a page off and deleting it
|
|
636
|
+
are for a person, in the platform interface.
|
|
617
637
|
|
|
618
638
|
## Organizations and keys
|
|
619
639
|
|