@getxflow/cli 0.6.5 → 0.7.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/api.js +28 -4
- package/dist/bin.js +25 -2
- package/dist/commands/auth.js +58 -30
- package/dist/commands/connections.js +89 -2
- package/dist/commands/deploy.js +7 -7
- package/dist/commands/env.js +1 -0
- package/dist/commands/mcp.js +21 -0
- package/dist/commands/org.js +80 -0
- package/dist/commands/projects.js +28 -2
- package/dist/commands/sources.js +18 -6
- package/dist/config.js +20 -1
- package/dist/credentials.js +203 -17
- package/dist/help.js +379 -328
- package/dist/session.js +68 -5
- package/dist/version.js +1 -1
- package/package.json +23 -23
- package/skills/xflow/SKILL.md +485 -451
package/dist/session.js
CHANGED
|
@@ -2,21 +2,84 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.connect = connect;
|
|
4
4
|
exports.anonymous = anonymous;
|
|
5
|
+
exports.findProjectOrg = findProjectOrg;
|
|
6
|
+
exports.connectProject = connectProject;
|
|
7
|
+
const api_1 = require("./api");
|
|
5
8
|
const config_1 = require("./config");
|
|
6
9
|
const credentials_1 = require("./credentials");
|
|
7
10
|
const errors_1 = require("./errors");
|
|
11
|
+
const ui_1 = require("./ui");
|
|
8
12
|
/** Platform connection: address plus key. */
|
|
9
13
|
function connect(config) {
|
|
10
14
|
const apiUrl = (0, config_1.apiUrlFor)(config);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
const root = (0, config_1.findProjectRoot)();
|
|
16
|
+
const picked = (0, credentials_1.pickKey)({
|
|
17
|
+
env: process.env.XFLOW_TOKEN?.trim() || null,
|
|
18
|
+
// XFLOW_TOKEN never follows an address taken from the repo's xflow.json:
|
|
19
|
+
// a cloned config must not be able to redirect the key elsewhere.
|
|
20
|
+
envAllowed: apiUrl === (0, config_1.apiUrlFor)(),
|
|
21
|
+
folderOrg: root ? ((0, config_1.readState)(root).organizationId ?? null) : null,
|
|
22
|
+
...(0, credentials_1.storedKeyInputs)(apiUrl),
|
|
23
|
+
});
|
|
24
|
+
if (!picked) {
|
|
15
25
|
throw new errors_1.CliError(`No access key for ${apiUrl}`, 'Sign in: xflow login. In CI pass the key in the XFLOW_TOKEN variable');
|
|
16
26
|
}
|
|
17
|
-
|
|
27
|
+
if ('missing' in picked) {
|
|
28
|
+
throw new errors_1.CliError(`This folder is bound to the organization ${picked.organizationId}, and there is no stored key for it`, 'Sign in to that organization: xflow login. The stored ones: xflow org');
|
|
29
|
+
}
|
|
30
|
+
return { apiUrl, token: picked.token, source: picked.source, organizationId: picked.organizationId };
|
|
18
31
|
}
|
|
19
32
|
/** Sign-in only. */
|
|
20
33
|
function anonymous(config) {
|
|
21
34
|
return { apiUrl: (0, config_1.apiUrlFor)(config), token: '' };
|
|
22
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Which signed-in organization serves the project. Project ids are unique
|
|
38
|
+
* across the platform, so a 404 means "not this one" and exactly one
|
|
39
|
+
* organization can answer. A dead key must not end the search: the answer may
|
|
40
|
+
* sit behind the next one.
|
|
41
|
+
*/
|
|
42
|
+
async function findProjectOrg(apiUrl, projectId) {
|
|
43
|
+
const dead = [];
|
|
44
|
+
for (const candidate of (0, credentials_1.listOrgs)(apiUrl)) {
|
|
45
|
+
try {
|
|
46
|
+
await (0, api_1.apiJson)({ apiUrl, token: candidate.token }, `/api/v1/projects/${projectId}`);
|
|
47
|
+
return { hit: candidate, dead };
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
if (e instanceof api_1.ApiError && (e.status === 401 || e.status === 403)) {
|
|
51
|
+
dead.push(candidate);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (e instanceof api_1.ApiError && e.status === 404)
|
|
55
|
+
continue;
|
|
56
|
+
throw e;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return { hit: null, dead };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Connection for project commands. A folder not yet bound to an organization
|
|
63
|
+
* gets probed once: the organization that serves the project is found and
|
|
64
|
+
* written into .xflow/state.json, so every later command resolves the key
|
|
65
|
+
* synchronously. With one stored key there is nothing to probe.
|
|
66
|
+
*/
|
|
67
|
+
async function connectProject(root, config) {
|
|
68
|
+
const session = connect(config);
|
|
69
|
+
if (session.source !== 'active' || session.organizationId === null)
|
|
70
|
+
return session;
|
|
71
|
+
const orgs = (0, credentials_1.listOrgs)(session.apiUrl);
|
|
72
|
+
if (orgs.length <= 1)
|
|
73
|
+
return session;
|
|
74
|
+
const { hit, dead } = await findProjectOrg(session.apiUrl, config.projectId);
|
|
75
|
+
if (!hit) {
|
|
76
|
+
throw new errors_1.CliError(`No signed-in organization holds the project ${config.projectId}`, dead.length > 0
|
|
77
|
+
? `The key of ${dead.map((d) => d.name ?? d.organizationId).join(', ')} is not working (sign in again: xflow login); the project may live there`
|
|
78
|
+
: 'Check projectId in xflow.json, or sign in to the organization that owns it: xflow login');
|
|
79
|
+
}
|
|
80
|
+
(0, config_1.writeState)(root, { organizationId: hit.organizationId });
|
|
81
|
+
if (hit.organizationId !== session.organizationId) {
|
|
82
|
+
(0, ui_1.note)((0, ui_1.dim)(` The project belongs to "${hit.name ?? hit.organizationId}", not to the active organization: the folder is bound to it`));
|
|
83
|
+
}
|
|
84
|
+
return { apiUrl: session.apiUrl, token: hit.token, source: 'folder', organizationId: hit.organizationId };
|
|
85
|
+
}
|
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.7.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
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@getxflow/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "CLI for the XFlow platform: source sync, deployment and publishing of applications",
|
|
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
|
-
"build": "tsc -p tsconfig.json",
|
|
18
|
-
"prepublishOnly": "npm run build"
|
|
19
|
-
},
|
|
20
|
-
"publishConfig": {
|
|
21
|
-
"access": "public"
|
|
22
|
-
}
|
|
23
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@getxflow/cli",
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "CLI for the XFlow platform: source sync, deployment and publishing of applications",
|
|
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
|
+
"build": "tsc -p tsconfig.json",
|
|
18
|
+
"prepublishOnly": "npm run build"
|
|
19
|
+
},
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
}
|
|
23
|
+
}
|