ticketlens 0.9.17 → 0.9.18

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ticketlens",
3
- "version": "0.9.17",
3
+ "version": "0.9.18",
4
4
  "description": "Jira CLI for developers — fetch ticket context, triage your queue, and stop tab-switching. Zero dependencies, all local.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,4 +1,4 @@
1
- <!-- jtb-skill-version: 0.9.16 -->
1
+ <!-- jtb-skill-version: 0.9.18 -->
2
2
  ---
3
3
  name: jtb
4
4
  description: Fetch a Jira ticket's full context (description, comments, linked issues, code references) and assemble a structured TicketBrief for implementation planning. Use when user types /jtb, mentions a Jira ticket key, or wants to plan work from a Jira ticket.
@@ -3,6 +3,7 @@ import { randomBytes } from 'node:crypto';
3
3
  import { spawn } from 'node:child_process';
4
4
  import { hostname as osHostname } from 'node:os';
5
5
  import { getConsoleBase } from './sync.mjs';
6
+ import { readLicense } from './license.mjs';
6
7
 
7
8
  const PORT_MIN = 49152;
8
9
  const PORT_MAX = 65535;
@@ -102,20 +103,34 @@ export function startLocalServer(port, expectedState, timeoutMs = TIMEOUT_MS) {
102
103
  /**
103
104
  * Full browser login flow. Opens the authorize page in the default browser,
104
105
  * waits for the callback, and returns the plaintext CLI token.
106
+ *
107
+ * @param {object} [opts]
108
+ * @param {Function} [opts.readLicenseFn] - Injectable license reader (default: readLicense)
109
+ * @param {Function} [opts.openBrowserFn] - Injectable browser opener (default: openBrowser)
110
+ * @param {Function} [opts.startServerFn] - Injectable local server (default: startLocalServer)
105
111
  */
106
- export async function browserLogin() {
112
+ export async function browserLogin({
113
+ readLicenseFn = () => { try { return readLicense(); } catch { return null; } },
114
+ openBrowserFn = openBrowser,
115
+ startServerFn = (port, state) => startLocalServer(port, state),
116
+ } = {}) {
107
117
  const state = generateState();
108
118
  const port = pickPort();
109
119
  const hostname = osHostname();
110
120
 
111
121
  const consoleBase = getConsoleBase();
112
- const url = `${consoleBase}/console/auth/cli`
122
+ let url = `${consoleBase}/console/auth/cli`
113
123
  + `?port=${port}`
114
124
  + `&state=${encodeURIComponent(state)}`
115
125
  + `&hostname=${encodeURIComponent(hostname)}`;
116
126
 
117
- const tokenPromise = startLocalServer(port, state);
118
- openBrowser(url);
127
+ const license = readLicenseFn();
128
+ if (license?.email) {
129
+ url += `&email=${encodeURIComponent(license.email)}`;
130
+ }
131
+
132
+ const tokenPromise = startServerFn(port, state);
133
+ openBrowserFn(url);
119
134
 
120
135
  return tokenPromise;
121
136
  }
@@ -2,7 +2,7 @@ import { readCliToken } from './cli-auth.mjs';
2
2
  import { formatCollisions } from './collision-reporter.mjs';
3
3
  import { DEFAULT_CONFIG_DIR } from './config.mjs';
4
4
  import { apiBase, warnIfInsecure } from './api-utils.mjs';
5
- import { red, yellow, bold, cyan } from './ansi.mjs';
5
+ import { red, yellow, bold, cyan, dim } from './ansi.mjs';
6
6
 
7
7
  const COLLISIONS_PATH = '/v1/triage/collisions';
8
8
 
@@ -41,11 +41,11 @@ export async function runCollisions(args = [], opts = {}) {
41
41
 
42
42
  if (!res.ok) {
43
43
  if (res.status === 401) {
44
- print(` ${red('✗')} Session expired. Run ${cyan('ticketlens login')} to reconnect.\n`);
44
+ print(` ${red('✗')} Session expired — run ${cyan('ticketlens login')} to reconnect.\n\n`);
45
45
  return { ok: false, status: 401 };
46
46
  }
47
47
  if (res.status === 403) {
48
- print(` ${red('✗')} ${bold('collisions')} requires a Team license\n`);
48
+ print(` ${red('✗')} ${bold('collisions')} requires a Team license.\n ${dim('→ Log in with a team account:')} ${cyan('ticketlens login')}\n\n`);
49
49
  return { ok: false, status: 403 };
50
50
  }
51
51
  print(` ${yellow('⚠')} Failed to fetch collisions (${res.status})\n`);
@@ -107,7 +107,7 @@ export async function runScheduleWizard({
107
107
 
108
108
  if (!res.ok) {
109
109
  if (res.status === 401) {
110
- print(` ${red('✗')} Session expired. Run ${cyan('ticketlens login')} to reconnect.\n`);
110
+ print(` ${red('✗')} Session expired — run ${cyan('ticketlens login')} to reconnect.\n\n`);
111
111
  return { ok: false, status: 401 };
112
112
  }
113
113
  if (res.status === 403) {
@@ -8,7 +8,7 @@ import { isLicensed } from './license.mjs';
8
8
  import { DEFAULT_CONFIG_DIR } from './config.mjs';
9
9
  import { apiBase, warnIfInsecure } from './api-utils.mjs';
10
10
  import { buildTicketPayload } from './ticket-payload.mjs';
11
- import { red, green, yellow, bold, cyan } from './ansi.mjs';
11
+ import { red, green, yellow, bold, cyan, dim } from './ansi.mjs';
12
12
 
13
13
  const PUSH_PATH = '/v1/triage/push';
14
14
 
@@ -109,12 +109,12 @@ export async function pushTriageSnapshot({
109
109
  }
110
110
 
111
111
  if (res.status === 401) {
112
- print(` ${red('✗')} Session expired. Run ${cyan('ticketlens login')} to reconnect.\n`);
112
+ print(` ${red('✗')} Session expired.\n ${dim('→ Run')} ${cyan('ticketlens login')} ${dim('to reconnect.')}\n\n`);
113
113
  return { ok: false, status: res.status };
114
114
  }
115
115
 
116
116
  if (res.status === 403) {
117
- print(` ${red('✗')} ${bold('--push')} requires a Team license\n`);
117
+ print(` ${red('✗')} ${bold('--push')} requires a Team license.\n ${dim('→ Log in with a team account:')} ${cyan('ticketlens login')}\n\n`);
118
118
  return { ok: false, status: res.status };
119
119
  }
120
120
 
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { apiBase, warnIfInsecure } from './api-utils.mjs';
7
- import { red, green, yellow, bold, cyan } from './ansi.mjs';
7
+ import { red, green, yellow, bold, cyan, dim } from './ansi.mjs';
8
8
 
9
9
  const SHARE_PATH = '/v1/triage/share';
10
10
 
@@ -77,12 +77,12 @@ export async function shareTriageSnapshot({
77
77
  }
78
78
 
79
79
  if (res.status === 401) {
80
- print(` ${red('✗')} Session expired. Run ${cyan('ticketlens login')} to reconnect.\n`);
80
+ print(` ${red('✗')} Session expired — run ${cyan('ticketlens login')} to reconnect.\n\n`);
81
81
  return { ok: false, status: res.status };
82
82
  }
83
83
 
84
84
  if (res.status === 403) {
85
- print(` ${red('✗')} ${bold('--share')} requires a Team license\n`);
85
+ print(` ${red('✗')} ${bold('--share')} requires a Team license.\n ${dim('→ Log in with a team account:')} ${cyan('ticketlens login')}\n\n`);
86
86
  return { ok: false, status: res.status };
87
87
  }
88
88