ticketlens 0.4.6 → 0.4.7
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
|
@@ -17,20 +17,19 @@ import { runInteractiveList } from './lib/interactive-list.mjs';
|
|
|
17
17
|
import { promptProfileSelect } from './lib/profile-picker.mjs';
|
|
18
18
|
import { printTriageHelp } from './lib/help.mjs';
|
|
19
19
|
import { handleUnknownFlags } from './lib/arg-validator.mjs';
|
|
20
|
-
import { isLicensed, showUpgradePrompt, revalidateIfStale
|
|
20
|
+
import { isLicensed, showUpgradePrompt, revalidateIfStale } from './lib/license.mjs';
|
|
21
21
|
import { readCliToken } from './lib/cli-auth.mjs';
|
|
22
|
+
import { apiBase } from './lib/api-utils.mjs';
|
|
22
23
|
|
|
23
24
|
const DEFAULT_STATUSES = ['In Progress', 'Code Review', 'QA'];
|
|
24
25
|
|
|
25
|
-
async function defaultDigestDeliverer(payload) {
|
|
26
|
-
const
|
|
27
|
-
const licenseKey = readLicense()?.key;
|
|
28
|
-
const res = await fetch('https://api.ticketlens.dev/v1/digest/deliver', {
|
|
26
|
+
async function defaultDigestDeliverer(payload, { cliToken } = {}) {
|
|
27
|
+
const res = await fetch(`${apiBase()}/v1/digest/deliver`, {
|
|
29
28
|
method: 'POST',
|
|
30
29
|
signal: AbortSignal.timeout(10_000),
|
|
31
30
|
headers: {
|
|
32
31
|
'Content-Type': 'application/json',
|
|
33
|
-
'Authorization': `Bearer ${
|
|
32
|
+
'Authorization': `Bearer ${cliToken}`,
|
|
34
33
|
},
|
|
35
34
|
body: JSON.stringify(payload),
|
|
36
35
|
});
|
|
@@ -297,6 +296,7 @@ export async function run(args, envOrOpts = process.env, fetcher = globalThis.fe
|
|
|
297
296
|
return;
|
|
298
297
|
}
|
|
299
298
|
const deliverer = opts.digestDeliverer ?? defaultDigestDeliverer;
|
|
299
|
+
const digestCliToken = opts.cliToken ?? readCliToken(configDir) ?? null;
|
|
300
300
|
|
|
301
301
|
// Triage history delta (non-fatal — wrapped in try/catch)
|
|
302
302
|
let delta = null;
|
|
@@ -321,7 +321,7 @@ export async function run(args, envOrOpts = process.env, fetcher = globalThis.fe
|
|
|
321
321
|
},
|
|
322
322
|
tickets: sorted,
|
|
323
323
|
delta,
|
|
324
|
-
});
|
|
324
|
+
}, { cliToken: digestCliToken });
|
|
325
325
|
return;
|
|
326
326
|
}
|
|
327
327
|
|
|
@@ -2,9 +2,10 @@ import { writeFileSync, mkdirSync, unlinkSync } from 'node:fs';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { homedir, platform as osPlatform } from 'node:os';
|
|
4
4
|
import { spawnSync } from 'node:child_process';
|
|
5
|
-
import { red, green, cyan } from './ansi.mjs';
|
|
5
|
+
import { red, green, yellow, cyan } from './ansi.mjs';
|
|
6
|
+
import { apiBase, warnIfInsecure } from './api-utils.mjs';
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
+
const SCHEDULE_PATH = '/v1/schedule';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Build a macOS LaunchAgent plist string.
|
|
@@ -59,7 +60,9 @@ export async function runScheduleWizard({
|
|
|
59
60
|
writeLocalJob = defaultWriteLocalJob,
|
|
60
61
|
timeoutMs = 10_000,
|
|
61
62
|
print = s => process.stdout.write(s),
|
|
63
|
+
warn = s => process.stderr.write(s),
|
|
62
64
|
}) {
|
|
65
|
+
warnIfInsecure(apiBase(), warn);
|
|
63
66
|
if (!cliToken) {
|
|
64
67
|
print(` ${red('✗')} schedule requires Console access. Run ${cyan('ticketlens login')} first.\n`);
|
|
65
68
|
return { ok: false };
|
|
@@ -69,32 +72,44 @@ export async function runScheduleWizard({
|
|
|
69
72
|
const hour = parseInt(hourStr, 10);
|
|
70
73
|
const minute = parseInt(minuteStr, 10);
|
|
71
74
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
75
|
+
try {
|
|
76
|
+
const res = await fetcher(`${apiBase()}${SCHEDULE_PATH}`, {
|
|
77
|
+
method: 'POST',
|
|
78
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
79
|
+
headers: {
|
|
80
|
+
'Content-Type': 'application/json',
|
|
81
|
+
'Authorization': `Bearer ${cliToken}`,
|
|
82
|
+
},
|
|
83
|
+
body: JSON.stringify({ email, timezone, deliverAt: time }),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
if (!res.ok) {
|
|
87
|
+
if (res.status === 401) {
|
|
88
|
+
print(` ${red('✗')} Session expired. Run ${cyan('ticketlens login')} to reconnect.\n`);
|
|
89
|
+
return { ok: false, status: 401 };
|
|
90
|
+
}
|
|
91
|
+
if (res.status === 403) {
|
|
92
|
+
print(` ${red('✗')} schedule requires a Pro license\n`);
|
|
93
|
+
return { ok: false, status: 403 };
|
|
94
|
+
}
|
|
95
|
+
print(` ${yellow('⚠')} Schedule API error (${res.status}) — try again later\n`);
|
|
96
|
+
return { ok: false, status: res.status };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const data = await res.json();
|
|
100
|
+
|
|
101
|
+
const ticketlensBin = resolveTicketlensBin();
|
|
102
|
+
const content = platform === 'darwin'
|
|
103
|
+
? buildPlist({ hour, minute, ticketlensBin })
|
|
104
|
+
: buildCronLine({ hour, minute, ticketlensBin });
|
|
105
|
+
|
|
106
|
+
writeLocalJob(content, platform);
|
|
107
|
+
|
|
108
|
+
return data;
|
|
109
|
+
} catch {
|
|
110
|
+
print(` ${yellow('⚠')} Schedule API error (network error) — try again later\n`);
|
|
111
|
+
return { ok: false };
|
|
86
112
|
}
|
|
87
|
-
|
|
88
|
-
const data = await res.json();
|
|
89
|
-
|
|
90
|
-
const ticketlensBin = resolveTicketlensBin();
|
|
91
|
-
const content = platform === 'darwin'
|
|
92
|
-
? buildPlist({ hour, minute, ticketlensBin })
|
|
93
|
-
: buildCronLine({ hour, minute, ticketlensBin });
|
|
94
|
-
|
|
95
|
-
writeLocalJob(content, platform);
|
|
96
|
-
|
|
97
|
-
return data;
|
|
98
113
|
}
|
|
99
114
|
|
|
100
115
|
export async function runScheduleStop({
|
|
@@ -102,17 +117,28 @@ export async function runScheduleStop({
|
|
|
102
117
|
cliToken,
|
|
103
118
|
platform = osPlatform(),
|
|
104
119
|
print = s => process.stdout.write(s),
|
|
120
|
+
warn = s => process.stderr.write(s),
|
|
105
121
|
}) {
|
|
122
|
+
warnIfInsecure(apiBase(), warn);
|
|
106
123
|
if (!cliToken) {
|
|
107
124
|
print(` ${red('✗')} schedule requires Console access. Run ${cyan('ticketlens login')} first.\n`);
|
|
108
125
|
return;
|
|
109
126
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
127
|
+
let res;
|
|
128
|
+
try {
|
|
129
|
+
res = await fetcher(`${apiBase()}${SCHEDULE_PATH}`, {
|
|
130
|
+
method: 'DELETE',
|
|
131
|
+
signal: AbortSignal.timeout(10_000),
|
|
132
|
+
headers: { 'Authorization': `Bearer ${cliToken}` },
|
|
133
|
+
});
|
|
134
|
+
} catch {
|
|
135
|
+
print(` ${yellow('⚠')} Schedule API error (network error) — try again later\n`);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (!res.ok) {
|
|
139
|
+
print(` ${yellow('⚠')} Schedule API error (${res.status}) — try again later\n`);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
116
142
|
|
|
117
143
|
if (platform === 'darwin') {
|
|
118
144
|
const plistPath = join(homedir(), 'Library', 'LaunchAgents', 'io.ticketlens.digest.plist');
|
|
@@ -133,18 +159,26 @@ export async function runScheduleStatus({
|
|
|
133
159
|
fetcher = globalThis.fetch,
|
|
134
160
|
cliToken,
|
|
135
161
|
print = s => process.stdout.write(s),
|
|
162
|
+
warn = s => process.stderr.write(s),
|
|
136
163
|
}) {
|
|
164
|
+
warnIfInsecure(apiBase(), warn);
|
|
137
165
|
if (!cliToken) {
|
|
138
166
|
print(` ${red('✗')} schedule requires Console access. Run ${cyan('ticketlens login')} first.\n`);
|
|
139
167
|
return;
|
|
140
168
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
169
|
+
let res;
|
|
170
|
+
try {
|
|
171
|
+
res = await fetcher(`${apiBase()}${SCHEDULE_PATH}`, {
|
|
172
|
+
method: 'GET',
|
|
173
|
+
signal: AbortSignal.timeout(10_000),
|
|
174
|
+
headers: { 'Authorization': `Bearer ${cliToken}` },
|
|
175
|
+
});
|
|
176
|
+
} catch {
|
|
177
|
+
print(` ${yellow('⚠')} Schedule API error (network error) — try again later\n`);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
146
180
|
if (!res.ok) {
|
|
147
|
-
|
|
181
|
+
print(' No active digest schedule found.\n');
|
|
148
182
|
return;
|
|
149
183
|
}
|
|
150
184
|
const data = await res.json();
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { apiBase } from './api-utils.mjs';
|
|
2
|
+
|
|
1
3
|
const ANTHROPIC_URL = 'https://api.anthropic.com/v1/messages';
|
|
2
4
|
const OPENAI_URL = 'https://api.openai.com/v1/chat/completions';
|
|
3
5
|
const GROQ_URL = 'https://api.groq.com/openai/v1/chat/completions';
|
|
4
|
-
const CLOUD_URL = 'https://api.ticketlens.dev/v1/summarize';
|
|
5
6
|
const DEFAULT_PROMPT = 'Summarize this Jira ticket in 3 sentences. Focus on what matters most for implementation. Be concrete.\n\n';
|
|
6
7
|
const DEFAULT_MAX_TOKENS = 256;
|
|
7
8
|
|
|
@@ -134,7 +135,7 @@ async function callGroq({ brief, apiKey, fetcher, timeoutMs, prompt, maxTokens }
|
|
|
134
135
|
}
|
|
135
136
|
|
|
136
137
|
async function cloud({ brief, licenseKey, fetcher, timeoutMs }) {
|
|
137
|
-
const res = await fetcher(
|
|
138
|
+
const res = await fetcher(`${apiBase()}/v1/summarize`, {
|
|
138
139
|
method: 'POST',
|
|
139
140
|
signal: AbortSignal.timeout(timeoutMs),
|
|
140
141
|
headers: {
|