@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 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
- const config = Config.load();
41
- if (args.api) config.apiUrl = String(args.api).replace(/\/+$/, '');
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, { apiUrl = null, name = null } = {}) {
25
- if (apiUrl) config.apiUrl = apiUrl.replace(/\/+$/, '');
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/config.json — the machine token, the API it talks to, and which
5
- * local folders are which Motionode repositories. Folder paths never leave this
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 FILE = path.join(DIR, 'config.json');
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 load() {
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(FILE, 'utf8'));
39
+ const raw = JSON.parse(fs.readFileSync(fileFor(apiUrl), 'utf8'));
20
40
  return {
21
- apiUrl: raw.apiUrl || DEFAULT_API,
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: DEFAULT_API, token: null, machine: null, user: null, workspaces: [] };
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(FILE, JSON.stringify(config, null, 2), { mode: 0o600 });
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, FILE, DEFAULT_API, load, save, samePath, workspaceForPath };
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.0",
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": {