ticketlens 0.10.0 → 0.10.2
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 +1 -1
- package/skills/jtb/SKILL.md +1 -1
- package/skills/jtb/scripts/fetch-my-tickets.mjs +10 -0
- package/skills/jtb/scripts/fetch-ticket.mjs +9 -0
- package/skills/jtb/scripts/lib/api-utils.mjs +5 -0
- package/skills/jtb/scripts/lib/config-wizard.mjs +2 -1
- package/skills/jtb/scripts/lib/license.mjs +4 -2
package/package.json
CHANGED
package/skills/jtb/SKILL.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- jtb-skill-version: 0.10.
|
|
1
|
+
<!-- jtb-skill-version: 0.10.2 -->
|
|
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.
|
|
@@ -108,6 +108,16 @@ export async function run(args, envOrOpts = process.env, fetcher = globalThis.fe
|
|
|
108
108
|
const licensedFn = opts.isLicensed ?? isLicensed;
|
|
109
109
|
const upgradeFn = opts.showUpgradePrompt ?? showUpgradePrompt;
|
|
110
110
|
|
|
111
|
+
// Fail-fast: --push requires a CLI token — check before the triage scan fires
|
|
112
|
+
if (pushFlag) {
|
|
113
|
+
const pushToken = opts.cliToken ?? readCliToken(configDir) ?? null;
|
|
114
|
+
if (!pushToken) {
|
|
115
|
+
process.stderr.write('Error: --push requires authentication. Run `ticketlens auth login` to connect your account.\n');
|
|
116
|
+
process.exitCode = 1;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
111
121
|
// --save=FILE: Pro gate + validate path is not a directory
|
|
112
122
|
if (saveArg) {
|
|
113
123
|
if (!licensedFn('pro', configDir)) {
|
|
@@ -987,6 +987,15 @@ export async function run(args, envOrOpts = process.env, fetcher = globalThis.fe
|
|
|
987
987
|
const licensedFn = opts.isLicensed ?? isLicensed;
|
|
988
988
|
const upgradeFn = opts.showUpgradePrompt ?? showUpgradePrompt;
|
|
989
989
|
|
|
990
|
+
// Fail-fast: check Pro gate before any network call for Pro-only flags
|
|
991
|
+
if (args.includes('--summarize') || args.includes('--handoff')) {
|
|
992
|
+
if (!licensedFn('pro', configDir)) {
|
|
993
|
+
upgradeFn('pro', args.includes('--handoff') ? '--handoff' : '--summarize');
|
|
994
|
+
process.exitCode = 1;
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
|
|
990
999
|
const noCache = args.includes('--no-cache');
|
|
991
1000
|
|
|
992
1001
|
// Resolve brief cache TTL: configurable for Pro tier only, else fixed 4h default
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
export const DEFAULT_API_BASE = 'https://api.ticketlens.app';
|
|
7
|
+
export const DEFAULT_SITE_BASE = 'https://ticketlens.dev';
|
|
7
8
|
|
|
8
9
|
// Matches localhost, 127.0.0.1, and any hostname ending in .test or .local,
|
|
9
10
|
// with an optional port — all treated as local-only addresses.
|
|
@@ -13,6 +14,10 @@ export function apiBase() {
|
|
|
13
14
|
return process.env?.TICKETLENS_API_URL ?? DEFAULT_API_BASE;
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
export function siteBase() {
|
|
18
|
+
return process.env?.TICKETLENS_SITE_URL ?? DEFAULT_SITE_BASE;
|
|
19
|
+
}
|
|
20
|
+
|
|
16
21
|
export function warnIfInsecure(url, warn) {
|
|
17
22
|
if (url.startsWith('http://') && !LOCAL_HOST_RE.test(url)) {
|
|
18
23
|
warn('⚠ Warning: TICKETLENS_API_URL uses an unencrypted HTTP connection. Use https:// for production.\n');
|
|
@@ -19,6 +19,7 @@ import { DEFAULT_BRIEF_TTL } from './brief-cache.mjs';
|
|
|
19
19
|
import { DEFAULT_CONFIG_DIR } from './config.mjs';
|
|
20
20
|
import { visLen, SERVER_AUTH_TYPES, promptText, promptSecret, promptYN } from './prompt-helpers.mjs';
|
|
21
21
|
import { isLicensed } from './license.mjs';
|
|
22
|
+
import { siteBase } from './api-utils.mjs';
|
|
22
23
|
|
|
23
24
|
const RETRY_OPTIONS = [
|
|
24
25
|
{ label: 'Retry', sublabel: 'Try again — same credentials (e.g. VPN just connected)', value: 'retry' },
|
|
@@ -384,7 +385,7 @@ export async function run({ configDir = DEFAULT_CONFIG_DIR, profileName } = {})
|
|
|
384
385
|
);
|
|
385
386
|
cacheTtl = ttlInput || curTtl;
|
|
386
387
|
} else {
|
|
387
|
-
stream.write(` ${s.dim('○')} Brief cache TTL: ${s.dim('4h')} ${s.dim('·')} ${s.cyan('Pro')} ${s.dim(
|
|
388
|
+
stream.write(` ${s.dim('○')} Brief cache TTL: ${s.dim('4h')} ${s.dim('·')} ${s.cyan('Pro')} ${s.dim(`unlocks configurable TTL → ${siteBase()}/#pricing`)}\n`);
|
|
388
389
|
}
|
|
389
390
|
|
|
390
391
|
// ── Save ───────────────────────────────────────────────────────────────────
|
|
@@ -4,6 +4,7 @@ import os from 'node:os';
|
|
|
4
4
|
import crypto from 'node:crypto';
|
|
5
5
|
import { DEFAULT_CONFIG_DIR } from './config.mjs';
|
|
6
6
|
import { createStyler } from './ansi.mjs';
|
|
7
|
+
import { siteBase } from './api-utils.mjs';
|
|
7
8
|
|
|
8
9
|
// Mixed into the HMAC key so that knowing the license key alone is not sufficient
|
|
9
10
|
// to forge a valid signature — an attacker also needs this constant from the source.
|
|
@@ -14,7 +15,7 @@ const LICENSE_SECRET_FILE = 'license-hmac-secret.json';
|
|
|
14
15
|
const REVALIDATION_DAYS = 7; // attempt background revalidation after this many days
|
|
15
16
|
const GRACE_DAYS = 30; // treat license as invalid if not revalidated within this window
|
|
16
17
|
const MS_PER_DAY = 86400000;
|
|
17
|
-
const
|
|
18
|
+
const upgradeUrl = () => `${siteBase()}/#pricing`;
|
|
18
19
|
|
|
19
20
|
const ANSI_RE_LIC = /\x1b\[[0-9;]*m|\x1b\]8;[^\x07]*\x07/g;
|
|
20
21
|
const visLen = (s) => s.replace(ANSI_RE_LIC, '').length;
|
|
@@ -138,7 +139,8 @@ export function showUpgradePrompt(requiredTier, featureFlag, { stream = process.
|
|
|
138
139
|
const bc = (t) => s.dim(t);
|
|
139
140
|
|
|
140
141
|
const featureLine = padInner(` ${s.yellow('◆')} ${s.bold(featureFlag)} requires ${s.bold(s.cyan(tier))}`, W);
|
|
141
|
-
const
|
|
142
|
+
const url = upgradeUrl();
|
|
143
|
+
const upgradeLine = padInner(` ${s.dim('Upgrade:')} ${s.link(url, s.cyan(url))}`, W);
|
|
142
144
|
const activateLine = padInner(` ${s.dim('Or run:')} ${s.dim('ticketlens activate <KEY>')}`, W);
|
|
143
145
|
const blank = ' '.repeat(W);
|
|
144
146
|
|