@openluxeco/cli 0.5.1 → 0.6.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/bin/openluxe.js CHANGED
@@ -1,13 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import { run } from '../src/cli.js';
4
+ import { load } from '../src/config.js';
5
+
3
6
  // Opt-in escape hatch for self-signed certs (local Herd / staging boxes).
4
- // Default stays fully secure — only relaxes when explicitly requested.
5
- if (process.env.OPENLUXE_INSECURE === '1' || process.argv.includes('--insecure')) {
7
+ // Default stays fully secure — relaxes only when explicitly requested, or when
8
+ // this device was logged in with the flag (remembered per saved base URL).
9
+ if (process.env.OPENLUXE_INSECURE === '1' || process.argv.includes('--insecure') || load().insecure) {
6
10
  process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
7
11
  }
8
12
 
9
- import { run } from '../src/cli.js';
10
-
11
13
  run(process.argv.slice(2)).catch((e) => {
12
14
  console.error(`\x1b[31m✗ ${e?.message || e}\x1b[0m`);
13
15
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openluxeco/cli",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "Official OpenLuxe command-line client — drive the OpenLuxe v1 API from your terminal or an AI agent.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/api.js CHANGED
@@ -21,7 +21,8 @@ function networkErrorMessage(e, url) {
21
21
 
22
22
  let msg = `Network error: ${causeMsg || e.message}${origin}`;
23
23
  if (/CERT|certificate|SSL|TLS/i.test(code + ' ' + causeMsg)) {
24
- msg += '\n Untrusted/self-signed certificate — for a local dev server, retry with OPENLUXE_INSECURE=1';
24
+ msg += '\n Untrusted/self-signed certificate — for a local dev server, retry with OPENLUXE_INSECURE=1'
25
+ + '\n (logging in with it set remembers the choice for that server)';
25
26
  } else if (code === 'ENOTFOUND' || code === 'EAI_AGAIN') {
26
27
  msg += '\n Host not found — check the API base (OPENLUXE_API_URL, or `openluxe auth status` for the stored base)';
27
28
  } else if (code === 'ECONNREFUSED') {
package/src/auth.js CHANGED
@@ -50,10 +50,15 @@ export async function login({ base } = {}) {
50
50
  const poll = await postPublic(apiBase, '/cli/auth/poll', { device_code });
51
51
 
52
52
  if (poll.ok && poll.data?.status === 'authorized') {
53
- save({ base: apiBase, token: poll.data.token, user: poll.data.user });
53
+ // Logging in with TLS verification relaxed (OPENLUXE_INSECURE=1 /
54
+ // --insecure) remembers that choice for this base, so follow-up
55
+ // commands against the same dev server don't need the flag.
56
+ const insecure = process.env.NODE_TLS_REJECT_UNAUTHORIZED === '0';
57
+ save({ base: apiBase, token: poll.data.token, user: poll.data.user, insecure });
54
58
  const who = poll.data.user?.email || poll.data.user?.name || 'your account';
55
59
  console.log(`\x1b[32m✓ Signed in as ${who}\x1b[0m`);
56
60
  console.log(` Token stored at ${credentialsPath}`);
61
+ if (insecure) console.log(' TLS verification stays relaxed for this server (remembered from login).');
57
62
  console.log('');
58
63
  console.log(' Your API use is governed by the OpenLuxe API & CLI Terms');
59
64
  console.log(` and all referenced policies — see \x1b[36m${apiBase}/api-terms\x1b[0m`);
package/src/banner.js ADDED
@@ -0,0 +1,68 @@
1
+ /**
2
+ * The OpenLuxe splash — the diamond estate mark stacked above the wordmark,
3
+ * washed in champagne gold. Shown at the top of `openluxe` / `openluxe help`.
4
+ *
5
+ * Zero-dep: hand-set ASCII + 256-color ANSI. Color is applied only on an
6
+ * interactive stdout without NO_COLOR, so piped output stays clean.
7
+ */
8
+ import { VERSION } from './config.js';
9
+
10
+ // The mark: nested shield/gem outline (public/images/mark.svg) — apex, angled
11
+ // shoulders, vertical sides, chamfered flat base, hollow double-walled center.
12
+ const MARK = [
13
+ ' ╱╲',
14
+ ' ╱ ╲',
15
+ ' ╱ ╱╲ ╲',
16
+ ' ╱ ╱ ╲ ╲',
17
+ ' ╱ ╱ ╲ ╲',
18
+ ' ╱ ╱ ╲ ╲',
19
+ ' │ │ │ │',
20
+ ' │ │ │ │',
21
+ ' │ ╰────────╯ │',
22
+ ' ╲ ╱',
23
+ ' ╲──────────╱',
24
+ ];
25
+
26
+ const WORDMARK = [
27
+ ' ██████╗ ██████╗ ███████╗███╗ ██╗██╗ ██╗ ██╗██╗ ██╗███████╗',
28
+ '██╔═══██╗██╔══██╗██╔════╝████╗ ██║██║ ██║ ██║╚██╗██╔╝██╔════╝',
29
+ '██║ ██║██████╔╝█████╗ ██╔██╗ ██║██║ ██║ ██║ ╚███╔╝ █████╗ ',
30
+ '██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║██║ ██║ ██║ ██╔██╗ ██╔══╝ ',
31
+ '╚██████╔╝██║ ███████╗██║ ╚████║███████╗╚██████╔╝██╔╝ ██╗███████╗',
32
+ ' ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝',
33
+ ];
34
+
35
+ const WIDTH = 68; // wordmark width — everything centers against it
36
+
37
+ // Champagne ramp, light → deep gold (xterm 256).
38
+ const MARK_RAMP = [230, 230, 223, 223, 222, 221, 220, 220, 178, 178, 172];
39
+ const WORD_RAMP = [223, 222, 220, 220, 178, 172];
40
+
41
+ function paint(line, color, on) {
42
+ return on ? `\x1b[38;5;${color}m${line}\x1b[0m` : line;
43
+ }
44
+
45
+ function center(line, width = WIDTH) {
46
+ const pad = Math.max(0, Math.floor((width - line.length) / 2));
47
+ return ' '.repeat(pad) + line;
48
+ }
49
+
50
+ export function banner() {
51
+ const on = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
52
+ const dim = (s) => (on ? `\x1b[2m${s}\x1b[0m` : s);
53
+
54
+ const markPad = ' '.repeat(Math.floor((WIDTH - 20) / 2) - 2);
55
+ const lines = [
56
+ '',
57
+ ...MARK.map((l, i) => markPad + paint(l, MARK_RAMP[i], on)),
58
+ '',
59
+ ...WORDMARK.map((l, i) => paint(l, WORD_RAMP[i], on)),
60
+ '',
61
+ center(`v${VERSION} · the platform, from your terminal · openluxe.co/developers`).replace(
62
+ /^(\s*)(.*)$/,
63
+ (_, s, t) => s + dim(t),
64
+ ),
65
+ '',
66
+ ];
67
+ return lines.join('\n');
68
+ }
package/src/cli.js CHANGED
@@ -3,6 +3,7 @@ import { RESOURCES } from './resources.js';
3
3
  import * as auth from './auth.js';
4
4
  import { load, VERSION } from './config.js';
5
5
  import { serve as mcpServe } from './mcp.js';
6
+ import { banner } from './banner.js';
6
7
 
7
8
  const C = {
8
9
  dim: (s) => `\x1b[2m${s}\x1b[0m`,
@@ -198,6 +199,7 @@ async function callApi(method, path, { positionals = [], flags = {}, body }) {
198
199
 
199
200
  function topHelp() {
200
201
  const { token, user, base } = load();
202
+ console.log(banner());
201
203
  console.log(`
202
204
  ${C.bold('openluxe')} — OpenLuxe API command-line client
203
205
 
package/src/config.js CHANGED
@@ -14,7 +14,7 @@ const DEFAULT_BASE = process.env.OPENLUXE_API_URL || 'https://openluxe.co';
14
14
 
15
15
  export function load() {
16
16
  if (!existsSync(FILE)) {
17
- return { base: DEFAULT_BASE, token: null, user: null };
17
+ return { base: DEFAULT_BASE, token: null, user: null, insecure: false };
18
18
  }
19
19
  try {
20
20
  const data = JSON.parse(readFileSync(FILE, 'utf8'));
@@ -22,15 +22,19 @@ export function load() {
22
22
  base: process.env.OPENLUXE_API_URL || data.base || DEFAULT_BASE,
23
23
  token: data.token || null,
24
24
  user: data.user || null,
25
+ // The remembered insecure opt-in is scoped to the base it was saved
26
+ // for — pointing OPENLUXE_API_URL elsewhere must stay fully secure.
27
+ insecure: data.insecure === true
28
+ && (!process.env.OPENLUXE_API_URL || process.env.OPENLUXE_API_URL === data.base),
25
29
  };
26
30
  } catch {
27
- return { base: DEFAULT_BASE, token: null, user: null };
31
+ return { base: DEFAULT_BASE, token: null, user: null, insecure: false };
28
32
  }
29
33
  }
30
34
 
31
- export function save({ base, token, user }) {
35
+ export function save({ base, token, user, insecure }) {
32
36
  mkdirSync(DIR, { recursive: true });
33
- writeFileSync(FILE, JSON.stringify({ base, token, user }, null, 2));
37
+ writeFileSync(FILE, JSON.stringify({ base, token, user, ...(insecure ? { insecure: true } : {}) }, null, 2));
34
38
  // Credentials file holds a bearer token — lock it down to the owner.
35
39
  try { chmodSync(FILE, 0o600); } catch { /* best effort (e.g. Windows) */ }
36
40
  }