@getxflow/cli 0.9.1 → 0.10.1
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 +41 -1
- package/dist/commands/skills.js +119 -21
- package/dist/commands/storage.js +370 -0
- package/dist/commands/update.js +6 -4
- package/dist/help.js +71 -2
- package/dist/state.js +36 -2
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/skills/xflow/SKILL.md +170 -134
package/dist/help.js
CHANGED
|
@@ -3,10 +3,29 @@ 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
|
+
* A page keyed by something other than the command that leads to it. `xflow help invoke`
|
|
8
|
+
* is nobody's first guess when the command is `xflow functions invoke`, and a page that
|
|
9
|
+
* covers a neighbour answers for it too.
|
|
10
|
+
*/
|
|
11
|
+
const ALIASES = {
|
|
12
|
+
functions: 'invoke',
|
|
13
|
+
publish: 'deploy',
|
|
14
|
+
deployments: 'rollback',
|
|
15
|
+
};
|
|
6
16
|
/** Help text. For agents this is the reference documentation. */
|
|
7
17
|
function help(topic) {
|
|
8
|
-
if (topic
|
|
9
|
-
|
|
18
|
+
if (topic) {
|
|
19
|
+
const page = TOPICS[ALIASES[topic] ?? topic];
|
|
20
|
+
if (page) {
|
|
21
|
+
(0, ui_1.out)(page);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
// Falling through to the full help here is what this used to do, and it reads as an
|
|
25
|
+
// answer: the agent asked about one command and got the whole screen back, with no
|
|
26
|
+
// sign that the page it asked for does not exist.
|
|
27
|
+
(0, ui_1.out)(`No page for "${topic}". Pages: ${Object.keys(TOPICS).sort().join(', ')}
|
|
28
|
+
Every command with a line of its own: xflow help`);
|
|
10
29
|
return;
|
|
11
30
|
}
|
|
12
31
|
(0, ui_1.out)(`${(0, ui_1.bold)('xflow')} ${(0, ui_1.dim)(version_1.CLI_VERSION)} XFlow application hosting
|
|
@@ -24,6 +43,14 @@ ${(0, ui_1.bold)('Sources')}
|
|
|
24
43
|
xflow pull [--into dir] [--revision N]
|
|
25
44
|
fetch the sources (the latest revision by default)
|
|
26
45
|
|
|
46
|
+
${(0, ui_1.bold)('Files')}
|
|
47
|
+
xflow storage ls [folder] what the file storage of the project holds
|
|
48
|
+
xflow storage push <dir> [--to folder] [--replace]
|
|
49
|
+
upload heavy static assets: photos, video, PDFs
|
|
50
|
+
xflow storage rm <address> delete one file
|
|
51
|
+
xflow storage rm --folder <path> --yes
|
|
52
|
+
delete a folder with everything in it
|
|
53
|
+
|
|
27
54
|
${(0, ui_1.bold)('Releasing')}
|
|
28
55
|
xflow deploy [--no-push] send the code, ship the functions, build on the platform
|
|
29
56
|
xflow publish show the dev version to visitors
|
|
@@ -64,6 +91,7 @@ ${(0, ui_1.bold)('Reference')}
|
|
|
64
91
|
xflow whoami whose key this is and what it can do
|
|
65
92
|
xflow logout [--all] forget the key of the active organization (--all: every one)
|
|
66
93
|
xflow update update the CLI itself, and the skill that ships with it
|
|
94
|
+
xflow --version which version is installed
|
|
67
95
|
|
|
68
96
|
${(0, ui_1.bold)('Environment')}
|
|
69
97
|
XFLOW_TOKEN access key (for CI, instead of xflow login)
|
|
@@ -179,6 +207,47 @@ Those show up in the answer to ${(0, ui_1.bold)('xflow functions invoke')}.
|
|
|
179
207
|
|
|
180
208
|
Browser errors are collected by ${(0, ui_1.bold)('src/utils/error-logger.ts')} of the template and
|
|
181
209
|
only from released addresses: a local ${(0, ui_1.bold)('npm run dev')} writes nothing here.`,
|
|
210
|
+
storage: `${(0, ui_1.bold)('xflow storage')}: files of the application
|
|
211
|
+
|
|
212
|
+
The sources archive is capped at 10 MB and is rebuilt and re-uploaded on every
|
|
213
|
+
deploy, so photos, video and PDFs do not belong in the repository. File storage
|
|
214
|
+
is the place for them: the files sit beside the versions, survive a build, and
|
|
215
|
+
are metered against the plan of the organization.
|
|
216
|
+
|
|
217
|
+
xflow storage ls [folder] what is there, with addresses and sizes
|
|
218
|
+
xflow storage push ./media --to media
|
|
219
|
+
upload a folder, keeping its structure
|
|
220
|
+
xflow storage rm <address> delete one file
|
|
221
|
+
xflow storage rm --folder photos --yes
|
|
222
|
+
delete a folder with everything in it
|
|
223
|
+
|
|
224
|
+
--to <folder> where to put it in the project storage
|
|
225
|
+
--replace upload files whose size differs from the stored one
|
|
226
|
+
--json machine-readable output, with every address
|
|
227
|
+
|
|
228
|
+
${(0, ui_1.bold)('push')} sends only what is missing: a file already stored under the same path and
|
|
229
|
+
size is skipped, so running the command again after a broken connection is cheap.
|
|
230
|
+
A file that differs is left alone unless ${(0, ui_1.bold)('--replace')} is given. Bytes go straight
|
|
231
|
+
into the bucket, they do not pass through the platform.
|
|
232
|
+
|
|
233
|
+
Addresses are permanent and belong to the record, not to the bytes: replacing a
|
|
234
|
+
file keeps its address, so the links in the code and in the tables of the
|
|
235
|
+
application keep working. That is why there is no delete-and-upload-again.
|
|
236
|
+
|
|
237
|
+
The address is not public: it is a page of the platform that checks the rights of
|
|
238
|
+
the viewer on every request, so a closed project stops serving its pictures too.
|
|
239
|
+
Use the address as it came back, in ${(0, ui_1.bold)('<img src>')} and in the data of the application.
|
|
240
|
+
|
|
241
|
+
Dot entries and symbolic links are skipped, and ${(0, ui_1.bold)('.xflowignore')} is not read here:
|
|
242
|
+
those are the rules for the sources, and a media folder is exactly what one is
|
|
243
|
+
asked to put there so it stays out of the archive.
|
|
244
|
+
|
|
245
|
+
${(0, ui_1.bold)('rm')} needs a right of its own (${(0, ui_1.bold)('Delete files')} in the Developers section of the
|
|
246
|
+
platform), off by default. The reason is the neighbourhood: whatever the users of
|
|
247
|
+
the application uploaded lives in the same folders, and there is no undo. A folder
|
|
248
|
+
first answers with the number of files it holds and deletes only on ${(0, ui_1.bold)('--yes')}.
|
|
249
|
+
|
|
250
|
+
There is no command back: sources return with ${(0, ui_1.bold)('xflow pull')}, files do not.`,
|
|
182
251
|
env: `${(0, ui_1.bold)('xflow env')}: environment variables of the functions
|
|
183
252
|
|
|
184
253
|
Keys, passwords and third-party addresses are kept by the platform, not by the
|
package/dist/state.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.shouldNudge = shouldNudge;
|
|
4
|
+
exports.shouldNudgeSkill = shouldNudgeSkill;
|
|
5
|
+
exports.markSkillNudged = markSkillNudged;
|
|
4
6
|
exports.markNudged = markNudged;
|
|
5
7
|
const node_fs_1 = require("node:fs");
|
|
6
8
|
const node_os_1 = require("node:os");
|
|
@@ -33,11 +35,43 @@ function shouldNudge(version) {
|
|
|
33
35
|
return true;
|
|
34
36
|
return Date.now() - at > NUDGE_INTERVAL_MS;
|
|
35
37
|
}
|
|
36
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* Same window, but per stale copy rather than per version. The key carries the CLI
|
|
40
|
+
* version, so the day a release actually changes the skill the notice speaks again
|
|
41
|
+
* instead of staying quiet on yesterday's answer.
|
|
42
|
+
*
|
|
43
|
+
* A window and not a single "already said it": the copy stays wrong until somebody
|
|
44
|
+
* refreshes it, and an agent whose session starts tomorrow has to hear about it too.
|
|
45
|
+
*/
|
|
46
|
+
function shouldNudgeSkill(key) {
|
|
47
|
+
if (process.env.CI)
|
|
48
|
+
return false;
|
|
49
|
+
const at = read().skillNudgedAt?.[key];
|
|
50
|
+
const said = at ? Date.parse(at) : Number.NaN;
|
|
51
|
+
if (!Number.isFinite(said))
|
|
52
|
+
return true;
|
|
53
|
+
return Date.now() - said > NUDGE_INTERVAL_MS;
|
|
54
|
+
}
|
|
55
|
+
/** Entries pile up across versions and folders, so the stale ones go on every write. */
|
|
56
|
+
function markSkillNudged(key) {
|
|
57
|
+
const now = Date.now();
|
|
58
|
+
const kept = {};
|
|
59
|
+
for (const [seen, at] of Object.entries(read().skillNudgedAt ?? {})) {
|
|
60
|
+
const said = Date.parse(at);
|
|
61
|
+
if (Number.isFinite(said) && now - said < NUDGE_INTERVAL_MS * 30)
|
|
62
|
+
kept[seen] = at;
|
|
63
|
+
}
|
|
64
|
+
kept[key] = new Date(now).toISOString();
|
|
65
|
+
write({ skillNudgedAt: kept });
|
|
66
|
+
}
|
|
37
67
|
function markNudged(version) {
|
|
68
|
+
write({ nudgedVersion: version, nudgedAt: new Date().toISOString() });
|
|
69
|
+
}
|
|
70
|
+
/** Best effort: a read-only home folder must not break the command that just worked. */
|
|
71
|
+
function write(patch) {
|
|
38
72
|
try {
|
|
39
73
|
(0, node_fs_1.mkdirSync)((0, node_path_1.join)((0, node_os_1.homedir)(), '.xflow'), { recursive: true, mode: 0o700 });
|
|
40
|
-
const state = { ...read(),
|
|
74
|
+
const state = { ...read(), ...patch };
|
|
41
75
|
(0, node_fs_1.writeFileSync)(statePath(), `${JSON.stringify(state, null, 2)}\n`, 'utf-8');
|
|
42
76
|
}
|
|
43
77
|
catch {
|
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.10.1';
|
|
6
6
|
/** Overridden by XFLOW_API_URL or the `api` field in xflow.json. */
|
|
7
7
|
exports.DEFAULT_API_URL = 'https://app.getxflow.com';
|