@motionode/cli 0.1.0 → 0.1.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/bin/motionode.js +9 -3
- package/lib/commands.js +2 -2
- package/lib/config.js +30 -10
- package/package.json +1 -1
package/bin/motionode.js
CHANGED
|
@@ -15,9 +15,12 @@ const HELP = `motionode ${version} — work Motionode tickets with your own loca
|
|
|
15
15
|
motionode work <ticket> start a ticket locally and check out its branches (e.g. T11-518)
|
|
16
16
|
motionode finish <ticket> [--force] [--note] you're done: Motionode checks your pushed commits and moves it on
|
|
17
17
|
motionode status what each linked folder has checked out and changed
|
|
18
|
+
motionode where which Motionode this terminal talks to
|
|
18
19
|
motionode logout disconnect this computer
|
|
19
20
|
|
|
20
|
-
Git stays yours: fetch, commit and push use your own git credentials and identity
|
|
21
|
+
Git stays yours: fetch, commit and push use your own git credentials and identity.
|
|
22
|
+
Talks to production (https://api.motionode.com) unless MOTIONODE_API_URL or --api says otherwise;
|
|
23
|
+
each Motionode keeps its own connection.`;
|
|
21
24
|
|
|
22
25
|
function flags(argv) {
|
|
23
26
|
const out = { _: [] };
|
|
@@ -37,8 +40,8 @@ function flags(argv) {
|
|
|
37
40
|
async function main() {
|
|
38
41
|
const [command, ...rest] = process.argv.slice(2);
|
|
39
42
|
const args = flags(rest);
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
// --api (or MOTIONODE_API_URL) picks the Motionode — and its own saved connection.
|
|
44
|
+
const config = Config.load(typeof args.api === 'string' ? args.api : null);
|
|
42
45
|
switch (command) {
|
|
43
46
|
case 'connect':
|
|
44
47
|
return Commands.connect(config, { apiUrl: args.api || null, name: args.name || null, service: args.service || null, relogin: !!args.relogin });
|
|
@@ -56,6 +59,9 @@ async function main() {
|
|
|
56
59
|
return Commands.finish(config, args._[0], { force: !!args.force, note: typeof args.note === 'string' ? args.note : null });
|
|
57
60
|
case 'status':
|
|
58
61
|
return Commands.status(config);
|
|
62
|
+
case 'where':
|
|
63
|
+
console.log(`${config.apiUrl}${config.token ? ` — connected as ${config.user?.name || config.user?.email || 'you'} (${config.machine?.name || 'this computer'})` : ' — not connected'}`);
|
|
64
|
+
return null;
|
|
59
65
|
case 'logout':
|
|
60
66
|
return Commands.logout(config);
|
|
61
67
|
case '--version':
|
package/lib/commands.js
CHANGED
|
@@ -21,8 +21,8 @@ function openBrowser(url) {
|
|
|
21
21
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
22
22
|
|
|
23
23
|
/** Device-code login: approve this computer in the browser once. */
|
|
24
|
-
async function login(config, {
|
|
25
|
-
|
|
24
|
+
async function login(config, { name = null } = {}) {
|
|
25
|
+
// config.apiUrl was already chosen (--api / MOTIONODE_API_URL / production).
|
|
26
26
|
const api = client({ ...config, token: null });
|
|
27
27
|
const machineName = name || os.hostname();
|
|
28
28
|
const start = await api.post('/device/start', { machineName, platform: `${process.platform}-${process.arch}` });
|
package/lib/config.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* ~/.motionode/
|
|
5
|
-
*
|
|
4
|
+
* ~/.motionode/ — the machine token, the API it talks to, and which local
|
|
5
|
+
* folders are which Motionode repositories. Folder paths never leave this
|
|
6
6
|
* machine except as the label shown to their owner in Motionode.
|
|
7
|
+
*
|
|
8
|
+
* One connection per Motionode: production lives in config.json; any other API
|
|
9
|
+
* (MOTIONODE_API_URL or --api, e.g. a local API behind a tunnel) gets its own
|
|
10
|
+
* config.<host>.json, so switching between them never disconnects the other.
|
|
7
11
|
*/
|
|
8
12
|
|
|
9
13
|
const fs = require('fs');
|
|
@@ -11,27 +15,43 @@ const os = require('os');
|
|
|
11
15
|
const path = require('path');
|
|
12
16
|
|
|
13
17
|
const DIR = process.env.MOTIONODE_HOME || path.join(os.homedir(), '.motionode');
|
|
14
|
-
const
|
|
15
|
-
const DEFAULT_API = process.env.MOTIONODE_API_URL || 'https://api.motionode.com';
|
|
18
|
+
const PRODUCTION_API = 'https://api.motionode.com';
|
|
16
19
|
|
|
17
|
-
function
|
|
20
|
+
function normalizeApi(url) {
|
|
21
|
+
return String(url || '').trim().replace(/\/+$/, '') || null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** The API this run talks to: --api, then MOTIONODE_API_URL, then production. */
|
|
25
|
+
function resolveApi(override = null) {
|
|
26
|
+
return normalizeApi(override) || normalizeApi(process.env.MOTIONODE_API_URL) || PRODUCTION_API;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function fileFor(apiUrl) {
|
|
30
|
+
if (normalizeApi(apiUrl) === PRODUCTION_API) return path.join(DIR, 'config.json');
|
|
31
|
+
let host = 'custom';
|
|
32
|
+
try { host = new URL(apiUrl).host; } catch (_e) { /* keep "custom" */ }
|
|
33
|
+
return path.join(DIR, `config.${host.replace(/[^a-zA-Z0-9.-]/g, '_')}.json`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function load(apiOverride = null) {
|
|
37
|
+
const apiUrl = resolveApi(apiOverride);
|
|
18
38
|
try {
|
|
19
|
-
const raw = JSON.parse(fs.readFileSync(
|
|
39
|
+
const raw = JSON.parse(fs.readFileSync(fileFor(apiUrl), 'utf8'));
|
|
20
40
|
return {
|
|
21
|
-
apiUrl
|
|
41
|
+
apiUrl,
|
|
22
42
|
token: raw.token || null,
|
|
23
43
|
machine: raw.machine || null,
|
|
24
44
|
user: raw.user || null,
|
|
25
45
|
workspaces: Array.isArray(raw.workspaces) ? raw.workspaces : [],
|
|
26
46
|
};
|
|
27
47
|
} catch (_e) {
|
|
28
|
-
return { apiUrl
|
|
48
|
+
return { apiUrl, token: null, machine: null, user: null, workspaces: [] };
|
|
29
49
|
}
|
|
30
50
|
}
|
|
31
51
|
|
|
32
52
|
function save(config) {
|
|
33
53
|
fs.mkdirSync(DIR, { recursive: true });
|
|
34
|
-
fs.writeFileSync(
|
|
54
|
+
fs.writeFileSync(fileFor(config.apiUrl), JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
35
55
|
}
|
|
36
56
|
|
|
37
57
|
function samePath(a, b) {
|
|
@@ -43,4 +63,4 @@ function workspaceForPath(config, dir) {
|
|
|
43
63
|
return (config.workspaces || []).find((w) => samePath(w.path, dir)) || null;
|
|
44
64
|
}
|
|
45
65
|
|
|
46
|
-
module.exports = { DIR,
|
|
66
|
+
module.exports = { DIR, PRODUCTION_API, resolveApi, fileFor, load, save, samePath, workspaceForPath };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@motionode/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Connect your local repositories to Motionode: work tickets in Cursor, VS Code or any editor while Motionode keeps the work tied to the ticket, its git branches, verification and delivery.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"bin": {
|