@getxflow/cli 0.24.0 → 0.25.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/auth.js +2 -5
- package/dist/commands/org.js +1 -10
- package/dist/credentials.js +43 -64
- package/dist/help.js +4 -2
- package/dist/session.js +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/skills/xflow/SKILL.md +3 -2
package/dist/commands/auth.js
CHANGED
|
@@ -92,13 +92,10 @@ function logout(args) {
|
|
|
92
92
|
return;
|
|
93
93
|
}
|
|
94
94
|
const orgs = (0, credentials_1.listOrgs)(apiUrl);
|
|
95
|
+
// The pointer always lands on a stored key, so no active one means no keys.
|
|
95
96
|
const active = orgs.find((org) => org.active);
|
|
96
97
|
if (!active) {
|
|
97
|
-
|
|
98
|
-
if ((0, credentials_1.forgetAllOrgs)(apiUrl))
|
|
99
|
-
(0, ui_1.ok)(`The key for ${apiUrl} is deleted`);
|
|
100
|
-
else
|
|
101
|
-
(0, ui_1.warn)(`There is no stored key for ${apiUrl}`);
|
|
98
|
+
(0, ui_1.warn)(`There is no stored key for ${apiUrl}`);
|
|
102
99
|
return;
|
|
103
100
|
}
|
|
104
101
|
const { nextActive } = (0, credentials_1.forgetOrg)(apiUrl, active.organizationId);
|
package/dist/commands/org.js
CHANGED
|
@@ -18,13 +18,7 @@ function orgList() {
|
|
|
18
18
|
const apiUrl = (0, config_1.apiUrlFor)((0, config_1.localConfig)());
|
|
19
19
|
const orgs = (0, credentials_1.listOrgs)(apiUrl);
|
|
20
20
|
if (orgs.length === 0) {
|
|
21
|
-
|
|
22
|
-
(0, ui_1.note)(`One key is stored for ${apiUrl}, its organization is unknown`);
|
|
23
|
-
(0, ui_1.note)((0, ui_1.dim)(' Sign in again to refresh it: xflow login'));
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
(0, ui_1.note)(`No keys for ${apiUrl}. Sign in: xflow login`);
|
|
27
|
-
}
|
|
21
|
+
(0, ui_1.note)(`No keys for ${apiUrl}. Sign in: xflow login`);
|
|
28
22
|
return;
|
|
29
23
|
}
|
|
30
24
|
(0, ui_1.table)(orgs.map((org) => [org.active ? '*' : ' ', org.name ?? '(unnamed)', org.organizationId]));
|
|
@@ -62,9 +56,6 @@ function orgSwitch(args) {
|
|
|
62
56
|
const apiUrl = (0, config_1.apiUrlFor)((0, config_1.localConfig)());
|
|
63
57
|
const orgs = (0, credentials_1.listOrgs)(apiUrl);
|
|
64
58
|
if (orgs.length === 0) {
|
|
65
|
-
if ((0, credentials_1.storedKeyInputs)(apiUrl).legacyToken) {
|
|
66
|
-
throw new errors_1.CliError(`The stored key for ${apiUrl} names no organization`, 'Sign in again to refresh it: xflow login');
|
|
67
|
-
}
|
|
68
59
|
throw new errors_1.CliError(`No keys for ${apiUrl}`, 'Sign in: xflow login');
|
|
69
60
|
}
|
|
70
61
|
const byId = orgs.find((org) => org.organizationId === wanted);
|
package/dist/credentials.js
CHANGED
|
@@ -47,56 +47,38 @@ function writeStore(store) {
|
|
|
47
47
|
function isLegacy(value) {
|
|
48
48
|
return !!value && typeof value.token === 'string';
|
|
49
49
|
}
|
|
50
|
-
/** The mirror for older CLIs: the key of the active organization, or nothing. */
|
|
51
|
-
function syncLegacy(store, apiUrl) {
|
|
52
|
-
const address = store.orgs?.[apiUrl];
|
|
53
|
-
if (!address)
|
|
54
|
-
return;
|
|
55
|
-
const active = address.active ? address.keys[address.active] : undefined;
|
|
56
|
-
if (active && address.active) {
|
|
57
|
-
store[apiUrl] = { token: active.token, organizationId: address.active, savedAt: active.savedAt };
|
|
58
|
-
address.mirror = address.active;
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
delete store[apiUrl];
|
|
62
|
-
address.mirror = null;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
50
|
/**
|
|
66
|
-
* Fold
|
|
67
|
-
*
|
|
68
|
-
*
|
|
51
|
+
* Fold a record predating the map into it and erase the record. A record naming
|
|
52
|
+
* no organization cannot be placed in the map and is dropped: the answer is one
|
|
53
|
+
* xflow login, while leaving it would leave the key where the old proxy looks.
|
|
69
54
|
* Returns true when the store changed and has to be written back.
|
|
70
55
|
*/
|
|
71
56
|
function reconcile(store, apiUrl) {
|
|
72
57
|
const legacy = store[apiUrl];
|
|
73
|
-
let address = store.orgs?.[apiUrl];
|
|
74
58
|
let changed = false;
|
|
75
|
-
if (isLegacy(legacy)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
orgs.keys[legacy.organizationId]
|
|
80
|
-
|
|
59
|
+
if (isLegacy(legacy)) {
|
|
60
|
+
if (legacy.organizationId) {
|
|
61
|
+
const orgs = store.orgs?.[apiUrl] ?? { active: null, keys: {} };
|
|
62
|
+
// The map wins: it is what this CLI wrote last.
|
|
63
|
+
if (!orgs.keys[legacy.organizationId]) {
|
|
64
|
+
orgs.keys[legacy.organizationId] = { token: legacy.token, savedAt: legacy.savedAt };
|
|
65
|
+
}
|
|
81
66
|
store.orgs = store.orgs ?? {};
|
|
82
67
|
store.orgs[apiUrl] = orgs;
|
|
83
|
-
address = orgs;
|
|
84
|
-
changed = true;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
if (!isLegacy(legacy) && address?.active && address.keys[address.active]) {
|
|
88
|
-
if (address.mirror === address.active) {
|
|
89
|
-
// The mirror we wrote for this key is gone: an older CLI logged out.
|
|
90
|
-
delete address.keys[address.active];
|
|
91
|
-
address.active = null;
|
|
92
68
|
}
|
|
93
|
-
|
|
94
|
-
// restored file): deleting it would lose a key nobody asked to forget.
|
|
95
|
-
// Either way the store changed: the mirror has to be rebuilt.
|
|
69
|
+
delete store[apiUrl];
|
|
96
70
|
changed = true;
|
|
97
71
|
}
|
|
98
|
-
|
|
72
|
+
const address = store.orgs?.[apiUrl];
|
|
99
73
|
if (address) {
|
|
74
|
+
// The field the erased record was tracked by. Left in place, a 0.24.0 still on
|
|
75
|
+
// the machine reads "the mirror I wrote is gone" as a logout and drops the key.
|
|
76
|
+
const stale = address;
|
|
77
|
+
if ('mirror' in stale) {
|
|
78
|
+
delete stale.mirror;
|
|
79
|
+
changed = true;
|
|
80
|
+
}
|
|
81
|
+
// The pointer must land on a stored key, or on nothing.
|
|
100
82
|
const ids = Object.keys(address.keys);
|
|
101
83
|
const active = address.active && address.keys[address.active] ? address.active : (ids[0] ?? null);
|
|
102
84
|
if (address.active !== active) {
|
|
@@ -104,8 +86,6 @@ function reconcile(store, apiUrl) {
|
|
|
104
86
|
changed = true;
|
|
105
87
|
}
|
|
106
88
|
}
|
|
107
|
-
if (changed)
|
|
108
|
-
syncLegacy(store, apiUrl);
|
|
109
89
|
return changed;
|
|
110
90
|
}
|
|
111
91
|
/** Read the store with the drift of older CLIs healed, persisting the healing. */
|
|
@@ -116,7 +96,6 @@ function load(apiUrl) {
|
|
|
116
96
|
return store;
|
|
117
97
|
}
|
|
118
98
|
function commit(store, apiUrl) {
|
|
119
|
-
syncLegacy(store, apiUrl);
|
|
120
99
|
const address = store.orgs?.[apiUrl];
|
|
121
100
|
if (address && Object.keys(address.keys).length === 0) {
|
|
122
101
|
delete store.orgs?.[apiUrl];
|
|
@@ -158,32 +137,38 @@ function saveOrgCredential(apiUrl, organizationId, token, name) {
|
|
|
158
137
|
/** Cache the display name; a stale one is refreshed by any login or whoami. */
|
|
159
138
|
function rememberOrgName(apiUrl, organizationId, name) {
|
|
160
139
|
const store = readStore();
|
|
161
|
-
reconcile(store, apiUrl);
|
|
140
|
+
let changed = reconcile(store, apiUrl);
|
|
162
141
|
const entry = store.orgs?.[apiUrl]?.keys[organizationId];
|
|
163
|
-
if (
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
142
|
+
if (entry && entry.name !== name) {
|
|
143
|
+
entry.name = name;
|
|
144
|
+
changed = true;
|
|
145
|
+
}
|
|
146
|
+
if (changed)
|
|
147
|
+
commit(store, apiUrl);
|
|
167
148
|
}
|
|
168
149
|
function setActiveOrg(apiUrl, organizationId) {
|
|
169
150
|
const store = readStore();
|
|
170
|
-
reconcile(store, apiUrl);
|
|
151
|
+
let changed = reconcile(store, apiUrl);
|
|
171
152
|
const orgs = store.orgs?.[apiUrl];
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (orgs.active !== organizationId) {
|
|
153
|
+
const known = !!orgs?.keys[organizationId];
|
|
154
|
+
if (orgs && known && orgs.active !== organizationId) {
|
|
175
155
|
orgs.active = organizationId;
|
|
176
|
-
|
|
156
|
+
changed = true;
|
|
177
157
|
}
|
|
178
|
-
|
|
158
|
+
if (changed)
|
|
159
|
+
commit(store, apiUrl);
|
|
160
|
+
return known;
|
|
179
161
|
}
|
|
180
162
|
/** Forget one organization; the pointer moves to any remaining one. */
|
|
181
163
|
function forgetOrg(apiUrl, organizationId) {
|
|
182
164
|
const store = readStore();
|
|
183
|
-
reconcile(store, apiUrl);
|
|
165
|
+
const healed = reconcile(store, apiUrl);
|
|
184
166
|
const orgs = store.orgs?.[apiUrl];
|
|
185
|
-
if (!orgs?.keys[organizationId])
|
|
167
|
+
if (!orgs?.keys[organizationId]) {
|
|
168
|
+
if (healed)
|
|
169
|
+
commit(store, apiUrl);
|
|
186
170
|
return { removed: false, nextActive: null };
|
|
171
|
+
}
|
|
187
172
|
delete orgs.keys[organizationId];
|
|
188
173
|
if (orgs.active === organizationId)
|
|
189
174
|
orgs.active = Object.keys(orgs.keys)[0] ?? null;
|
|
@@ -199,8 +184,9 @@ function forgetOrg(apiUrl, organizationId) {
|
|
|
199
184
|
/** Forget every key of the address; other addresses are untouched. */
|
|
200
185
|
function forgetAllOrgs(apiUrl) {
|
|
201
186
|
const store = readStore();
|
|
202
|
-
reconcile
|
|
203
|
-
|
|
187
|
+
// A record erased by reconcile was a key as well: counted here, or the address
|
|
188
|
+
// looks empty and the erasure never reaches the file.
|
|
189
|
+
const had = reconcile(store, apiUrl) || !!store.orgs?.[apiUrl];
|
|
204
190
|
if (!had)
|
|
205
191
|
return false;
|
|
206
192
|
if (store.orgs) {
|
|
@@ -236,16 +222,9 @@ function pickKey(inputs) {
|
|
|
236
222
|
const active = activeId ? inputs.orgs?.keys[activeId] : undefined;
|
|
237
223
|
if (active && activeId)
|
|
238
224
|
return { token: active.token, source: 'active', organizationId: activeId };
|
|
239
|
-
if (inputs.legacyToken)
|
|
240
|
-
return { token: inputs.legacyToken, source: 'active', organizationId: null };
|
|
241
225
|
return null;
|
|
242
226
|
}
|
|
243
227
|
/** The store-backed half of pickKey's inputs; the caller adds env and the folder binding. */
|
|
244
228
|
function storedKeyInputs(apiUrl) {
|
|
245
|
-
|
|
246
|
-
const legacy = store[apiUrl];
|
|
247
|
-
return {
|
|
248
|
-
orgs: store.orgs?.[apiUrl] ?? null,
|
|
249
|
-
legacyToken: isLegacy(legacy) ? legacy.token : null,
|
|
250
|
-
};
|
|
229
|
+
return { orgs: load(apiUrl).orgs?.[apiUrl] ?? null };
|
|
251
230
|
}
|
package/dist/help.js
CHANGED
|
@@ -547,8 +547,10 @@ so the cloud functions answer here the way they do on the platform. The key is p
|
|
|
547
547
|
as for every other command (${(0, ui_1.bold)('xflow help org')}): with several organizations signed
|
|
548
548
|
in, the right one is found and the folder is bound to it.
|
|
549
549
|
|
|
550
|
-
Use it instead of a bare ${(0, ui_1.bold)('npm run dev')}
|
|
551
|
-
|
|
550
|
+
Use it instead of a bare ${(0, ui_1.bold)('npm run dev')}: that one is handed no key, so its calls to
|
|
551
|
+
cloud functions fail. In a project made before this command the failure reads "No access
|
|
552
|
+
key for the platform: run xflow login", and signing in will not help, because the key is
|
|
553
|
+
no longer left in a file for that dev server to pick up.
|
|
552
554
|
|
|
553
555
|
The platform address comes from the CLI too, not from .env, so the key goes to the
|
|
554
556
|
platform that issued it and nowhere else. It never reaches the browser: the page gets
|
package/dist/session.js
CHANGED
|
@@ -84,7 +84,7 @@ async function findProjectOrg(apiUrl, projectId) {
|
|
|
84
84
|
*/
|
|
85
85
|
async function connectProject(projectId, root, config) {
|
|
86
86
|
const session = connect(config, { ignoreFolder: root === null });
|
|
87
|
-
if (session.source !== 'active'
|
|
87
|
+
if (session.source !== 'active')
|
|
88
88
|
return session;
|
|
89
89
|
const orgs = (0, credentials_1.listOrgs)(session.apiUrl);
|
|
90
90
|
if (orgs.length <= 1)
|
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.25.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.25.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
|
|
@@ -602,7 +602,8 @@ own devtools.
|
|
|
602
602
|
|
|
603
603
|
Run the app locally with `xflow dev`, not a bare `npm run dev`: it hands the dev server
|
|
604
604
|
the key of the project's organization, which cloud functions need to answer, and their
|
|
605
|
-
current addresses, including functions someone else deployed.
|
|
605
|
+
current addresses, including functions someone else deployed. A bare `npm run dev` gets
|
|
606
|
+
no key at all, so its function calls fail whatever the error says. The server answers
|
|
606
607
|
`127.0.0.1` alone, because it holds that key: a phone on the same network cannot open it,
|
|
607
608
|
and neither can anybody else on that network.
|
|
608
609
|
|