@getxflow/cli 0.11.2 → 0.12.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/bin.js +1 -1
- package/dist/commands/db.js +38 -3
- package/dist/commands/projects.js +33 -10
- package/dist/flags.js +16 -9
- package/dist/help.js +38 -11
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/skills/xflow/SKILL.md +6 -4
package/dist/bin.js
CHANGED
|
@@ -170,7 +170,7 @@ async function run(args) {
|
|
|
170
170
|
return;
|
|
171
171
|
}
|
|
172
172
|
if (second === undefined || second === 'status') {
|
|
173
|
-
await (0, db_1.dbStatus)();
|
|
173
|
+
await (0, db_1.dbStatus)({ ...args, words: args.words.slice(2) });
|
|
174
174
|
return;
|
|
175
175
|
}
|
|
176
176
|
throw new errors_1.CliError(`Unknown command: db ${second}`, 'Available: status, list, schema, query and migrate');
|
package/dist/commands/db.js
CHANGED
|
@@ -39,7 +39,22 @@ function requireMigrations(root) {
|
|
|
39
39
|
}
|
|
40
40
|
return migrations;
|
|
41
41
|
}
|
|
42
|
-
async function dbStatus() {
|
|
42
|
+
async function dbStatus(args) {
|
|
43
|
+
// Named by id there is no migrations/ directory to compare against: the answer
|
|
44
|
+
// is the applied history alone, which does not depend on any working copy.
|
|
45
|
+
if ((0, args_1.flagString)(args, 'project') !== undefined) {
|
|
46
|
+
const { projectId, client } = await (0, session_1.projectTarget)(args);
|
|
47
|
+
const history = await (0, api_1.apiJson)(client, `/api/v1/projects/${projectId}/db/migrate`);
|
|
48
|
+
(0, ui_1.out)(`Schema: ${(0, ui_1.bold)(history.schema)}`);
|
|
49
|
+
(0, ui_1.out)('');
|
|
50
|
+
if (history.applied.length === 0) {
|
|
51
|
+
(0, ui_1.note)('No migrations have been applied yet');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
(0, ui_1.table)(history.applied.map((row) => [row.name, (0, ui_1.formatAge)(row.applied_at)]));
|
|
55
|
+
(0, ui_1.note)((0, ui_1.dim)(' Applied history only: comparing against migrations/ needs the project folder'));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
43
58
|
const { root, config } = (0, config_1.requireProject)();
|
|
44
59
|
const client = (0, session_1.connect)(config);
|
|
45
60
|
const local = readMigrations(root);
|
|
@@ -106,10 +121,13 @@ async function dbList() {
|
|
|
106
121
|
}
|
|
107
122
|
/** Long values would tear the columns apart; the cut is marked, never silent. */
|
|
108
123
|
const CELL_LIMIT = 60;
|
|
109
|
-
function
|
|
124
|
+
function plain(value) {
|
|
110
125
|
if (value === null || value === undefined)
|
|
111
126
|
return '-';
|
|
112
|
-
|
|
127
|
+
return typeof value === 'object' ? JSON.stringify(value) : String(value);
|
|
128
|
+
}
|
|
129
|
+
function cell(value) {
|
|
130
|
+
const text = plain(value);
|
|
113
131
|
return text.length > CELL_LIMIT ? `${text.slice(0, CELL_LIMIT - 1)}…` : text;
|
|
114
132
|
}
|
|
115
133
|
/**
|
|
@@ -171,11 +189,28 @@ async function dbQuery(args) {
|
|
|
171
189
|
body: { sql, limit: (0, args_1.flagNumber)(args, 'limit') },
|
|
172
190
|
timeoutMs: 60_000,
|
|
173
191
|
});
|
|
192
|
+
// The whole values, machine-readable, on stdout alone: the notes go to stderr.
|
|
193
|
+
if ((0, args_1.flagBool)(args, 'json')) {
|
|
194
|
+
(0, ui_1.out)(JSON.stringify({
|
|
195
|
+
schema: data.schema,
|
|
196
|
+
columns: data.columns,
|
|
197
|
+
rows: data.rows,
|
|
198
|
+
truncated: data.truncated,
|
|
199
|
+
limit: data.limit,
|
|
200
|
+
limit_max: data.limit_max,
|
|
201
|
+
}, null, 2));
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
174
204
|
if (data.rows.length === 0) {
|
|
175
205
|
(0, ui_1.note)('No rows');
|
|
176
206
|
return;
|
|
177
207
|
}
|
|
178
208
|
(0, ui_1.table)([data.columns, ...data.rows.map((row) => data.columns.map((column) => cell(row[column])))]);
|
|
209
|
+
// Two different cuts, named apart: this one is about the width of a cell, the
|
|
210
|
+
// one below is about the number of rows.
|
|
211
|
+
if (data.rows.some((row) => data.columns.some((column) => plain(row[column]).length > CELL_LIMIT))) {
|
|
212
|
+
(0, ui_1.note)((0, ui_1.dim)(` Values longer than ${CELL_LIMIT} characters are cut to keep the columns readable: --json prints them whole`));
|
|
213
|
+
}
|
|
179
214
|
if (data.truncated) {
|
|
180
215
|
// No exact total here: the platform stops reading at limit + 1 rows, so all
|
|
181
216
|
// it knows is that more exist. The "raise --limit" advice shows only while
|
|
@@ -10,6 +10,7 @@ const node_path_1 = require("node:path");
|
|
|
10
10
|
const api_1 = require("../api");
|
|
11
11
|
const args_1 = require("../args");
|
|
12
12
|
const config_1 = require("../config");
|
|
13
|
+
const credentials_1 = require("../credentials");
|
|
13
14
|
const errors_1 = require("../errors");
|
|
14
15
|
const session_1 = require("../session");
|
|
15
16
|
const skills_1 = require("./skills");
|
|
@@ -123,6 +124,9 @@ async function link(args) {
|
|
|
123
124
|
if (!projectId) {
|
|
124
125
|
throw new errors_1.CliError('A project identifier is required', 'The list: xflow projects list');
|
|
125
126
|
}
|
|
127
|
+
if (!(0, session_1.isProjectId)(projectId)) {
|
|
128
|
+
throw new errors_1.CliError(`link takes a project id, got "${projectId}"`, 'The ids: xflow projects list');
|
|
129
|
+
}
|
|
126
130
|
let card;
|
|
127
131
|
try {
|
|
128
132
|
card = await (0, api_1.apiJson)(client, `/api/v1/projects/${projectId}`);
|
|
@@ -192,25 +196,44 @@ async function list() {
|
|
|
192
196
|
const root = (0, config_1.findProjectRoot)();
|
|
193
197
|
const client = (0, session_1.connect)(root ? (0, config_1.readConfig)(root) : undefined);
|
|
194
198
|
const { projects } = await (0, api_1.apiJson)(client, '/api/v1/projects');
|
|
195
|
-
if (projects.length
|
|
199
|
+
if (projects.length > 0) {
|
|
200
|
+
(0, ui_1.table)(projects.map((p) => [
|
|
201
|
+
p.id,
|
|
202
|
+
p.name,
|
|
203
|
+
p.live_deploy_id ? 'published' : p.dev_deploy_id ? 'dev only' : 'no build',
|
|
204
|
+
(0, ui_1.formatAge)(p.updated_at),
|
|
205
|
+
]));
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
196
208
|
(0, ui_1.note)('No projects. To create one: xflow init');
|
|
197
|
-
return;
|
|
198
209
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
210
|
+
// One organization answers, and with several signed in that is worth saying:
|
|
211
|
+
// a project that "is not here" may simply live in another one. Not under an
|
|
212
|
+
// env key or a folder binding, where "active" would be the wrong word.
|
|
213
|
+
const orgs = (0, credentials_1.listOrgs)(client.apiUrl);
|
|
214
|
+
if (client.source === 'active' && orgs.length > 1) {
|
|
215
|
+
const name = orgs.find((org) => org.active)?.name;
|
|
216
|
+
(0, ui_1.note)((0, ui_1.dim)(` Projects of ${name ? `"${name}"` : 'the active organization'} alone: the rest of xflow org answer after org switch`));
|
|
217
|
+
}
|
|
205
218
|
}
|
|
206
219
|
async function get(args) {
|
|
207
220
|
const root = (0, config_1.findProjectRoot)();
|
|
208
221
|
const config = root ? (0, config_1.readConfig)(root) : undefined;
|
|
209
|
-
const
|
|
222
|
+
const explicit = args.words[0];
|
|
223
|
+
if (explicit !== undefined && !(0, session_1.isProjectId)(explicit)) {
|
|
224
|
+
throw new errors_1.CliError(`projects get takes a project id, got "${explicit}"`, 'The ids: xflow projects list');
|
|
225
|
+
}
|
|
226
|
+
const projectId = explicit ?? config?.projectId;
|
|
210
227
|
if (!projectId) {
|
|
211
228
|
throw new errors_1.CliError('A project identifier is required', 'Or run the command inside the project folder');
|
|
212
229
|
}
|
|
213
|
-
|
|
230
|
+
// An id typed by hand is looked for across every signed-in organization and
|
|
231
|
+
// ignores the folder whole, xflow.json included, the way --project does it.
|
|
232
|
+
// Named by the folder, the project of an unbound one gets probed and the
|
|
233
|
+
// binding written, the way publish and pull do it.
|
|
234
|
+
const client = explicit !== undefined
|
|
235
|
+
? await (0, session_1.connectProject)(explicit, null)
|
|
236
|
+
: await (0, session_1.connectProject)(projectId, root, config);
|
|
214
237
|
const card = await (0, api_1.apiJson)(client, `/api/v1/projects/${projectId}`);
|
|
215
238
|
(0, ui_1.out)(`${(0, ui_1.bold)(card.name)} ${(0, ui_1.dim)(card.id)}`);
|
|
216
239
|
if (card.description)
|
package/dist/flags.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KNOWN = void 0;
|
|
3
4
|
exports.checkFlags = checkFlags;
|
|
4
5
|
const args_1 = require("./args");
|
|
5
6
|
const errors_1 = require("./errors");
|
|
@@ -17,11 +18,17 @@ const help_1 = require("./help");
|
|
|
17
18
|
* whoever is at the keyboard gets the flags of this very command back.
|
|
18
19
|
*
|
|
19
20
|
* `--project` is a flag like any other here, and it is written against a command
|
|
20
|
-
* only when that command
|
|
21
|
-
* pull, status, db migrate,
|
|
21
|
+
* only when that command can answer without the working copy. The ones that cannot
|
|
22
|
+
* (deploy, pull, status, db migrate, env check, env set, connections unlink) have
|
|
22
23
|
* a folder to stand in, and naming a project they cannot read would be a lie.
|
|
24
|
+
* `db status` sits halfway: named by id it answers with the applied history alone,
|
|
25
|
+
* without the comparison against migrations/.
|
|
26
|
+
*
|
|
27
|
+
* flags-sync.test.ts holds this table against the code: a flag listed here has to
|
|
28
|
+
* be read by the handler of its command, and a flag the handler reads has to be
|
|
29
|
+
* listed here. A new command or a new way of reading flags shows up there first.
|
|
23
30
|
*/
|
|
24
|
-
|
|
31
|
+
exports.KNOWN = {
|
|
25
32
|
help: [],
|
|
26
33
|
login: [],
|
|
27
34
|
logout: ['all'],
|
|
@@ -59,11 +66,11 @@ const KNOWN = {
|
|
|
59
66
|
'schedules set': ['payload', 'payload-file', 'project'],
|
|
60
67
|
'schedules rm': ['project'],
|
|
61
68
|
'schedules remove': ['project'],
|
|
62
|
-
db: [],
|
|
63
|
-
'db status': [],
|
|
69
|
+
db: ['project'],
|
|
70
|
+
'db status': ['project'],
|
|
64
71
|
'db list': [],
|
|
65
72
|
'db schema': ['project'],
|
|
66
|
-
'db query': ['limit', 'project'],
|
|
73
|
+
'db query': ['limit', 'json', 'project'],
|
|
67
74
|
'db migrate': ['dry-run', 'allow-destructive'],
|
|
68
75
|
storage: ['json', 'project'],
|
|
69
76
|
'storage ls': ['json', 'project'],
|
|
@@ -103,18 +110,18 @@ function commandOf(words) {
|
|
|
103
110
|
if (!first)
|
|
104
111
|
return null;
|
|
105
112
|
if (second !== undefined) {
|
|
106
|
-
if (Object.hasOwn(KNOWN, `${first} ${second}`))
|
|
113
|
+
if (Object.hasOwn(exports.KNOWN, `${first} ${second}`))
|
|
107
114
|
return `${first} ${second}`;
|
|
108
115
|
if (NAMESPACES.has(first))
|
|
109
116
|
return null;
|
|
110
117
|
}
|
|
111
|
-
return Object.hasOwn(KNOWN, first) ? first : null;
|
|
118
|
+
return Object.hasOwn(exports.KNOWN, first) ? first : null;
|
|
112
119
|
}
|
|
113
120
|
function checkFlags(args) {
|
|
114
121
|
const command = commandOf(args.words);
|
|
115
122
|
if (command === null)
|
|
116
123
|
return;
|
|
117
|
-
const allowed = KNOWN[command];
|
|
124
|
+
const allowed = exports.KNOWN[command];
|
|
118
125
|
const topic = command.split(' ')[0];
|
|
119
126
|
// Not every command has a page, and sending the reader to "No page for login"
|
|
120
127
|
// would be a second dead end in the same breath as the first.
|
package/dist/help.js
CHANGED
|
@@ -101,8 +101,8 @@ ${(0, ui_1.bold)('Reference')}
|
|
|
101
101
|
xflow --version which version is installed
|
|
102
102
|
|
|
103
103
|
${(0, ui_1.bold)('Outside a project folder')}
|
|
104
|
-
--project <id> name the project by id, for every command that
|
|
105
|
-
|
|
104
|
+
--project <id> name the project by id, for every command that can
|
|
105
|
+
answer without the local files (xflow help project)
|
|
106
106
|
|
|
107
107
|
${(0, ui_1.bold)('Environment')}
|
|
108
108
|
XFLOW_TOKEN access key (for CI, instead of xflow login)
|
|
@@ -152,21 +152,26 @@ no working copy of its own:
|
|
|
152
152
|
xflow db query --project 3f2a… "select id, title from tasks where done = false"
|
|
153
153
|
xflow functions invoke kanban --project 3f2a… --data-file card.json
|
|
154
154
|
|
|
155
|
-
Which commands: ${(0, ui_1.bold)('db query')}, ${(0, ui_1.bold)('db schema')}, ${(0, ui_1.bold)('functions list')},
|
|
155
|
+
Which commands: ${(0, ui_1.bold)('db status')}, ${(0, ui_1.bold)('db query')}, ${(0, ui_1.bold)('db schema')}, ${(0, ui_1.bold)('functions list')},
|
|
156
156
|
${(0, ui_1.bold)('functions invoke')}, ${(0, ui_1.bold)('functions logs')}, ${(0, ui_1.bold)('logs')}, ${(0, ui_1.bold)('env')}, ${(0, ui_1.bold)('env rm')},
|
|
157
157
|
${(0, ui_1.bold)('connections')}, ${(0, ui_1.bold)('connections link')}, ${(0, ui_1.bold)('schedules')}, ${(0, ui_1.bold)('schedules set')},
|
|
158
158
|
${(0, ui_1.bold)('schedules rm')}, ${(0, ui_1.bold)('storage ls')}, ${(0, ui_1.bold)('storage push')}, ${(0, ui_1.bold)('storage rm')},
|
|
159
|
-
${(0, ui_1.bold)('deployments')}, ${(0, ui_1.bold)('publish')}, ${(0, ui_1.bold)('rollback')}.
|
|
159
|
+
${(0, ui_1.bold)('deployments')}, ${(0, ui_1.bold)('publish')}, ${(0, ui_1.bold)('rollback')}. ${(0, ui_1.bold)('db status')} answers with less
|
|
160
|
+
under the flag: the applied history alone, because comparing it against the local
|
|
161
|
+
${(0, ui_1.bold)('migrations/')} directory needs the folder.
|
|
160
162
|
|
|
161
|
-
The rest
|
|
162
|
-
${(0, ui_1.bold)('deploy')}, ${(0, ui_1.bold)('pull')}, ${(0, ui_1.bold)('status')}, ${(0, ui_1.bold)('db migrate')}, ${(0, ui_1.bold)('
|
|
163
|
-
${(0, ui_1.bold)('env
|
|
164
|
-
|
|
163
|
+
The rest cannot answer without the working copy and have nothing to do with an id
|
|
164
|
+
instead: ${(0, ui_1.bold)('deploy')}, ${(0, ui_1.bold)('pull')}, ${(0, ui_1.bold)('status')}, ${(0, ui_1.bold)('db migrate')}, ${(0, ui_1.bold)('env check')},
|
|
165
|
+
${(0, ui_1.bold)('env set')}, ${(0, ui_1.bold)('connections unlink')}. They answer about the files next to them, so
|
|
166
|
+
they keep asking for a folder. ${(0, ui_1.bold)('projects get')} and ${(0, ui_1.bold)('link')} take no flag either:
|
|
167
|
+
they name the project by its id as an argument.
|
|
165
168
|
|
|
166
169
|
The folder is then ignored whole, and that includes its ${(0, ui_1.bold)('xflow.json')}: the address
|
|
167
170
|
is app.getxflow.com, or ${(0, ui_1.bold)('XFLOW_API_URL')} when it is set, and never the ${(0, ui_1.bold)('api')} field
|
|
168
171
|
of a config that happens to be lying around. Standing in the folder of a self-hosted
|
|
169
|
-
project, the flag therefore goes somewhere else than the folder does.
|
|
172
|
+
project, the flag therefore goes somewhere else than the folder does. An id handed to
|
|
173
|
+
${(0, ui_1.bold)('projects get')} or ${(0, ui_1.bold)('link')} behaves the same way: typed by hand, it is not redirected
|
|
174
|
+
by a config lying around either.
|
|
170
175
|
|
|
171
176
|
The id is echoed back on every run. It is the one place a project is named by hand,
|
|
172
177
|
and a wrong id answers "not found" on a read but works on a write.
|
|
@@ -175,6 +180,24 @@ With several organizations signed in, the one holding the project is looked for
|
|
|
175
180
|
every call: there is no folder to write the answer into, and a second place to
|
|
176
181
|
remember it would be a second thing to keep true. One ${(0, ui_1.bold)('xflow org switch')} to the
|
|
177
182
|
right organization spares that search.`,
|
|
183
|
+
projects: `${(0, ui_1.bold)('xflow projects')}: the projects of the organization
|
|
184
|
+
|
|
185
|
+
xflow projects list every project, its versions state and age
|
|
186
|
+
xflow projects get [id] one card: versions, database, functions, schedules
|
|
187
|
+
|
|
188
|
+
Both name the project positionally: the id is an argument, not a --project flag.
|
|
189
|
+
Without one, ${(0, ui_1.bold)('get')} answers about the project of the folder it stands in.
|
|
190
|
+
|
|
191
|
+
An id typed by hand is looked for across every signed-in organization, and the
|
|
192
|
+
folder around the command is ignored whole, the way the ${(0, ui_1.bold)('--project')} flag does it
|
|
193
|
+
(${(0, ui_1.bold)('xflow help project')}). So "not found" means the id is wrong, or its organization
|
|
194
|
+
is missing from ${(0, ui_1.bold)('xflow org')}: never that the active key was simply the wrong one.
|
|
195
|
+
|
|
196
|
+
${(0, ui_1.bold)('list')} stays within one organization: the bound one inside a project folder, the
|
|
197
|
+
active one outside. With several signed in, a line under the table says whose
|
|
198
|
+
projects these are.
|
|
199
|
+
|
|
200
|
+
${(0, ui_1.bold)('xflow link <id>')} binds the current folder to a project; ${(0, ui_1.bold)('xflow init')} makes a new one.`,
|
|
178
201
|
update: `${(0, ui_1.bold)('xflow update')}: bring the CLI up to date
|
|
179
202
|
|
|
180
203
|
Installs the published version and, if anything changed, rewrites the platform
|
|
@@ -203,7 +226,8 @@ will work.`,
|
|
|
203
226
|
xflow db status what is applied and what is waiting
|
|
204
227
|
xflow db list databases of the organization, and who is on them
|
|
205
228
|
xflow db schema [table] tables of the project, or the columns of one
|
|
206
|
-
xflow db query "select ..." read data (--limit N: 50 by default, 300 at most
|
|
229
|
+
xflow db query "select ..." read data (--limit N: 50 by default, 300 at most;
|
|
230
|
+
--json prints the values whole)
|
|
207
231
|
xflow db migrate apply migrations/*.sql
|
|
208
232
|
|
|
209
233
|
${(0, ui_1.bold)('list')} is about the organization, not about this project: it names the logical
|
|
@@ -213,7 +237,10 @@ attached to each one. That last column is the one to read before a migration.
|
|
|
213
237
|
${(0, ui_1.bold)('schema')} answers a different question than the ${(0, ui_1.bold)('migrations/')} directory: one logical
|
|
214
238
|
database is shared by several projects, so the files say what you did, and the schema
|
|
215
239
|
says what is actually in there. ${(0, ui_1.bold)('query')} runs inside a READ ONLY transaction, so
|
|
216
|
-
writes are rejected by the database itself, not by us reading your SQL.
|
|
240
|
+
writes are rejected by the database itself, not by us reading your SQL. Its table cuts
|
|
241
|
+
long values to keep the columns readable; ${(0, ui_1.bold)('--json')} prints them whole, together with
|
|
242
|
+
the applied limit and its ceiling. ${(0, ui_1.bold)('status')} named by id (${(0, ui_1.bold)('--project')}) answers with
|
|
243
|
+
the applied history alone: comparing against ${(0, ui_1.bold)('migrations/')} needs the folder.
|
|
217
244
|
|
|
218
245
|
${(0, ui_1.bold)('migrate')}: the files live in the repository (${(0, ui_1.bold)('migrations/0001_init.sql')},
|
|
219
246
|
${(0, ui_1.bold)('migrations/0002_orders.sql')} and so on). The order comes from the file name, which
|
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.12.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
|
@@ -24,7 +24,7 @@ contains the fix.
|
|
|
24
24
|
|
|
25
25
|
## Keeping these instructions current
|
|
26
26
|
|
|
27
|
-
These instructions ship with xflow CLI 0.
|
|
27
|
+
These instructions ship with xflow CLI 0.12.0. They travel inside the package, so the copy
|
|
28
28
|
you are reading can be older than the CLI answering your commands, and nothing about that
|
|
29
29
|
is visible in the text itself.
|
|
30
30
|
|
|
@@ -424,7 +424,8 @@ organization and the projects on each, which is where you find out who else is o
|
|
|
424
424
|
For the same reason `migrations/` is not the
|
|
425
425
|
schema. It says what you did; `xflow db schema [table]` says what is in the database right
|
|
426
426
|
now, and `xflow db query "select ..."` reads it inside a READ ONLY transaction (50 rows by
|
|
427
|
-
default, `--limit` raises that to 300).
|
|
427
|
+
default, `--limit` raises that to 300). The table cuts long values to keep the columns
|
|
428
|
+
readable; `--json` prints them whole. Look before
|
|
428
429
|
you write a migration against a shared database.
|
|
429
430
|
|
|
430
431
|
## File storage
|
|
@@ -525,8 +526,9 @@ One key per organization, stored side by side rather than replacing each other:
|
|
|
525
526
|
Inside a project folder there is nothing to switch: commands follow the organization the
|
|
526
527
|
folder is bound to, whatever the active one is. That is what lets two projects of two
|
|
527
528
|
organizations work side by side. The exception is `--project <id>`, which every command
|
|
528
|
-
that
|
|
529
|
-
folder whole, so a second project needs no second checkout.
|
|
529
|
+
that can answer without the working copy takes (`xflow help project` lists them): it ignores
|
|
530
|
+
the folder whole, so a second project needs no second checkout. `projects get` and `link`
|
|
531
|
+
take no flag and name the project by its id as an argument instead.
|
|
530
532
|
|
|
531
533
|
A "not found" on a project you know exists usually means the key belongs to another
|
|
532
534
|
organization, not that the project is gone: ids are unique across the platform, so a
|