@canaryai/cli 0.2.0 → 0.2.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/dist/{chunk-2T64Z2NI.js → chunk-7R4YFGP6.js} +53 -2
- package/dist/chunk-7R4YFGP6.js.map +1 -0
- package/dist/chunk-DXJNFJ3A.js +64 -0
- package/dist/chunk-DXJNFJ3A.js.map +1 -0
- package/dist/{chunk-V7U52ISX.js → chunk-HOYYXZPV.js} +136 -131
- package/dist/chunk-HOYYXZPV.js.map +1 -0
- package/dist/{chunk-ROTCL5WO.js → chunk-TO66FC4R.js} +688 -479
- package/dist/chunk-TO66FC4R.js.map +1 -0
- package/dist/debug-workflow-EHKNO7BJ.js +240 -0
- package/dist/debug-workflow-EHKNO7BJ.js.map +1 -0
- package/dist/{feature-flag-ESPSOSKG.js → feature-flag-ZDLDYRSF.js} +15 -92
- package/dist/feature-flag-ZDLDYRSF.js.map +1 -0
- package/dist/index.js +50 -66
- package/dist/index.js.map +1 -1
- package/dist/issues-FI3RIWGV.js +362 -0
- package/dist/issues-FI3RIWGV.js.map +1 -0
- package/dist/{knobs-HKONHY55.js → knobs-3MKMOXIV.js} +19 -104
- package/dist/knobs-3MKMOXIV.js.map +1 -0
- package/dist/{local-browser-MKKPBTYI.js → local-browser-GG5GUXDS.js} +2 -2
- package/dist/{mcp-4F4HI7L2.js → mcp-AD67OLQM.js} +3 -3
- package/dist/{psql-6IFVXM3A.js → psql-IVAPNYZV.js} +2 -2
- package/dist/{redis-HZC32IEO.js → redis-LWY7L6AS.js} +2 -2
- package/dist/{release-WOD3DAX4.js → release-KQFCTAXA.js} +5 -35
- package/dist/release-KQFCTAXA.js.map +1 -0
- package/dist/runner/preload.js +7 -323
- package/dist/runner/preload.js.map +1 -1
- package/dist/test.js +5 -340
- package/dist/test.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-2T64Z2NI.js.map +0 -1
- package/dist/chunk-ROTCL5WO.js.map +0 -1
- package/dist/chunk-V7U52ISX.js.map +0 -1
- package/dist/feature-flag-ESPSOSKG.js.map +0 -1
- package/dist/knobs-HKONHY55.js.map +0 -1
- package/dist/release-WOD3DAX4.js.map +0 -1
- /package/dist/{local-browser-MKKPBTYI.js.map → local-browser-GG5GUXDS.js.map} +0 -0
- /package/dist/{mcp-4F4HI7L2.js.map → mcp-AD67OLQM.js.map} +0 -0
- /package/dist/{psql-6IFVXM3A.js.map → psql-IVAPNYZV.js.map} +0 -0
- /package/dist/{redis-HZC32IEO.js.map → redis-LWY7L6AS.js.map} +0 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import {
|
|
2
|
+
apiRequest
|
|
3
|
+
} from "./chunk-DXJNFJ3A.js";
|
|
4
|
+
import {
|
|
5
|
+
getArgValue,
|
|
6
|
+
hasFlag,
|
|
7
|
+
resolveConfig
|
|
8
|
+
} from "./chunk-7R4YFGP6.js";
|
|
9
|
+
import "./chunk-DGUM43GV.js";
|
|
10
|
+
|
|
11
|
+
// src/issues.ts
|
|
12
|
+
import process from "process";
|
|
13
|
+
var SEVERITY_ICONS = {
|
|
14
|
+
high: "!!!",
|
|
15
|
+
medium: "!!",
|
|
16
|
+
low: "!",
|
|
17
|
+
unknown: "?"
|
|
18
|
+
};
|
|
19
|
+
var DIAGNOSTIC_CATEGORY_LABELS = {
|
|
20
|
+
software_bug: "Software Bug",
|
|
21
|
+
dependency_issue: "Dependency Issue",
|
|
22
|
+
agent_confusion: "Agent Confusion"
|
|
23
|
+
};
|
|
24
|
+
function formatStatusLabel(status) {
|
|
25
|
+
switch (status) {
|
|
26
|
+
case "open":
|
|
27
|
+
return "Open";
|
|
28
|
+
case "closed":
|
|
29
|
+
return "Closed";
|
|
30
|
+
case "not_a_bug":
|
|
31
|
+
return "Not a Bug";
|
|
32
|
+
default:
|
|
33
|
+
return status;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function formatSeverity(severity) {
|
|
37
|
+
return severity.charAt(0).toUpperCase() + severity.slice(1);
|
|
38
|
+
}
|
|
39
|
+
function formatRelative(value) {
|
|
40
|
+
if (!value) return "\u2014";
|
|
41
|
+
try {
|
|
42
|
+
const date = new Date(value);
|
|
43
|
+
const now = /* @__PURE__ */ new Date();
|
|
44
|
+
const diffMs = now.getTime() - date.getTime();
|
|
45
|
+
const diffMins = Math.floor(diffMs / 6e4);
|
|
46
|
+
if (diffMins < 1) return "just now";
|
|
47
|
+
if (diffMins < 60) return `${diffMins}m ago`;
|
|
48
|
+
const diffHours = Math.floor(diffMins / 60);
|
|
49
|
+
if (diffHours < 24) return `${diffHours}h ago`;
|
|
50
|
+
const diffDays = Math.floor(diffHours / 24);
|
|
51
|
+
if (diffDays < 30) return `${diffDays}d ago`;
|
|
52
|
+
return date.toISOString().slice(0, 10);
|
|
53
|
+
} catch {
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function truncate(text, max) {
|
|
58
|
+
if (text.length <= max) return text;
|
|
59
|
+
return text.slice(0, max - 1) + "\u2026";
|
|
60
|
+
}
|
|
61
|
+
function formatIssueDetailMarkdown(issue, consoleErrors, networkErrors) {
|
|
62
|
+
const sections = [];
|
|
63
|
+
const occ = issue.latestOccurrence;
|
|
64
|
+
sections.push(`# ${issue.title}`);
|
|
65
|
+
const meta = [];
|
|
66
|
+
meta.push(`**Status:** ${formatStatusLabel(issue.status)}`);
|
|
67
|
+
meta.push(`**Severity:** ${formatSeverity(issue.severity)}`);
|
|
68
|
+
meta.push(`**Category:** ${issue.category.label}`);
|
|
69
|
+
sections.push(meta.join(" | "));
|
|
70
|
+
if (occ?.primaryUrl) {
|
|
71
|
+
sections.push(`**URL:** ${occ.primaryUrl}`);
|
|
72
|
+
}
|
|
73
|
+
const timeParts = [];
|
|
74
|
+
timeParts.push(`**First seen:** ${formatRelative(issue.firstSeenAt)}`);
|
|
75
|
+
timeParts.push(`**Last seen:** ${formatRelative(issue.lastSeenAt)}`);
|
|
76
|
+
timeParts.push(`**Occurrences:** ${issue.occurrenceCount}`);
|
|
77
|
+
sections.push(timeParts.join(" | "));
|
|
78
|
+
const summary = occ?.ticketUserFacingSummary?.trim();
|
|
79
|
+
if (summary) {
|
|
80
|
+
sections.push(`## Summary
|
|
81
|
+
${summary}`);
|
|
82
|
+
}
|
|
83
|
+
const reproSteps = occ?.ticketReproSteps?.trim();
|
|
84
|
+
if (reproSteps) {
|
|
85
|
+
sections.push(`## Reproduction Steps
|
|
86
|
+
${reproSteps}`);
|
|
87
|
+
}
|
|
88
|
+
const diagnostic = occ?.diagnosticJson;
|
|
89
|
+
if (diagnostic) {
|
|
90
|
+
const diagParts = [];
|
|
91
|
+
const categoryLabel = DIAGNOSTIC_CATEGORY_LABELS[diagnostic.category] ?? diagnostic.category;
|
|
92
|
+
diagParts.push("## Diagnostic Analysis");
|
|
93
|
+
diagParts.push(`**Category:** ${categoryLabel} (${Math.round(diagnostic.confidence * 100)}% confidence)`);
|
|
94
|
+
diagParts.push(`**${diagnostic.title}**`);
|
|
95
|
+
diagParts.push(diagnostic.analysis);
|
|
96
|
+
if (diagnostic.smokingGun) {
|
|
97
|
+
const gun = diagnostic.smokingGun;
|
|
98
|
+
const gunLines = [`> **Key Evidence:** ${gun.description}`];
|
|
99
|
+
if (gun.detail) {
|
|
100
|
+
gunLines.push(`> \`${gun.detail}\``);
|
|
101
|
+
}
|
|
102
|
+
diagParts.push(gunLines.join("\n"));
|
|
103
|
+
}
|
|
104
|
+
diagParts.push(`**Recommendation:** ${diagnostic.recommendation}`);
|
|
105
|
+
sections.push(diagParts.join("\n\n"));
|
|
106
|
+
}
|
|
107
|
+
if (consoleErrors.length > 0) {
|
|
108
|
+
const logLines = consoleErrors.map((entry) => {
|
|
109
|
+
const loc = entry.location ? ` (${entry.location})` : "";
|
|
110
|
+
return `[${entry.type}] ${entry.text}${loc}`;
|
|
111
|
+
});
|
|
112
|
+
sections.push(`## Console Errors
|
|
113
|
+
\`\`\`
|
|
114
|
+
${logLines.join("\n")}
|
|
115
|
+
\`\`\``);
|
|
116
|
+
}
|
|
117
|
+
if (networkErrors.length > 0) {
|
|
118
|
+
const rows = networkErrors.map((entry) => {
|
|
119
|
+
const status = entry.status === null || entry.status === 0 ? "0 (failed)" : String(entry.status);
|
|
120
|
+
const duration = entry.durationMs != null ? `${entry.durationMs}ms` : "\u2014";
|
|
121
|
+
return `| ${entry.method} | ${entry.url} | ${status} | ${duration} |`;
|
|
122
|
+
});
|
|
123
|
+
sections.push(
|
|
124
|
+
`## Network Errors
|
|
125
|
+
| Method | URL | Status | Duration |
|
|
126
|
+
|--------|-----|--------|----------|
|
|
127
|
+
${rows.join("\n")}`
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
return sections.join("\n\n");
|
|
131
|
+
}
|
|
132
|
+
function formatIssueListMarkdown(data, pagination) {
|
|
133
|
+
const sections = [];
|
|
134
|
+
sections.push(`# Issues (page ${pagination.page} of ${pagination.totalPages}, ${pagination.totalItems} total)`);
|
|
135
|
+
if (data.length === 0) {
|
|
136
|
+
sections.push("No issues found.");
|
|
137
|
+
return sections.join("\n\n");
|
|
138
|
+
}
|
|
139
|
+
const header = "| Severity | Title | Status | Last Seen | Occurrences |";
|
|
140
|
+
const divider = "|----------|-------|--------|-----------|-------------|";
|
|
141
|
+
const rows = data.map(
|
|
142
|
+
(issue) => `| ${issue.severity} | ${truncate(issue.title, 60)} | ${issue.status} | ${formatRelative(issue.lastSeenAt)} | ${issue.occurrenceCount} |`
|
|
143
|
+
);
|
|
144
|
+
sections.push([header, divider, ...rows].join("\n"));
|
|
145
|
+
return sections.join("\n\n");
|
|
146
|
+
}
|
|
147
|
+
async function fetchDiagnostics(apiUrl, token, issue) {
|
|
148
|
+
const occ = issue.latestOccurrence;
|
|
149
|
+
if (!occ) return { consoleErrors: [], networkErrors: [] };
|
|
150
|
+
let consoleErrors = [];
|
|
151
|
+
let networkErrors = [];
|
|
152
|
+
try {
|
|
153
|
+
const [consoleRes, networkRes] = await Promise.all([
|
|
154
|
+
apiRequest(
|
|
155
|
+
apiUrl,
|
|
156
|
+
token,
|
|
157
|
+
"GET",
|
|
158
|
+
`/v2/issues/${issue.id}/occurrences/${occ.id}/console-logs`
|
|
159
|
+
),
|
|
160
|
+
apiRequest(
|
|
161
|
+
apiUrl,
|
|
162
|
+
token,
|
|
163
|
+
"GET",
|
|
164
|
+
`/v2/issues/${issue.id}/occurrences/${occ.id}/network-requests`
|
|
165
|
+
)
|
|
166
|
+
]);
|
|
167
|
+
if (consoleRes.ok && Array.isArray(consoleRes.data)) {
|
|
168
|
+
consoleErrors = consoleRes.data.filter((e) => e.type === "error");
|
|
169
|
+
}
|
|
170
|
+
if (networkRes.ok && Array.isArray(networkRes.data)) {
|
|
171
|
+
networkErrors = networkRes.data.filter(
|
|
172
|
+
(e) => e.status === null || e.status === 0 || e.status >= 400
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
} catch {
|
|
176
|
+
}
|
|
177
|
+
if (consoleErrors.length === 0 && occ.consoleLogExcerpt?.trim()) {
|
|
178
|
+
consoleErrors = [{ type: "error", text: occ.consoleLogExcerpt.trim(), timestamp: null, location: null }];
|
|
179
|
+
}
|
|
180
|
+
if (networkErrors.length === 0 && occ.networkFailureUrl) {
|
|
181
|
+
networkErrors = [{
|
|
182
|
+
url: occ.networkFailureUrl,
|
|
183
|
+
method: "GET",
|
|
184
|
+
status: occ.networkFailureStatus,
|
|
185
|
+
durationMs: null
|
|
186
|
+
}];
|
|
187
|
+
}
|
|
188
|
+
return { consoleErrors, networkErrors };
|
|
189
|
+
}
|
|
190
|
+
async function handleList(argv, apiUrl, token) {
|
|
191
|
+
const jsonOutput = hasFlag(argv, "--json");
|
|
192
|
+
const markdownOutput = getArgValue(argv, "--format") === "markdown";
|
|
193
|
+
const params = new URLSearchParams();
|
|
194
|
+
const search = getArgValue(argv, "--search");
|
|
195
|
+
const severity = getArgValue(argv, "--severity");
|
|
196
|
+
const status = getArgValue(argv, "--status");
|
|
197
|
+
const propertyId = getArgValue(argv, "--property-id");
|
|
198
|
+
const page = getArgValue(argv, "--page");
|
|
199
|
+
const pageSize = getArgValue(argv, "--page-size");
|
|
200
|
+
if (search) params.set("search", search);
|
|
201
|
+
if (severity) params.set("severity", severity);
|
|
202
|
+
if (status) params.set("statuses", status);
|
|
203
|
+
if (propertyId) params.set("propertyId", propertyId);
|
|
204
|
+
if (page) params.set("page", page);
|
|
205
|
+
if (pageSize) params.set("pageSize", pageSize);
|
|
206
|
+
const qs = params.toString();
|
|
207
|
+
const path = `/v2/issues${qs ? `?${qs}` : ""}`;
|
|
208
|
+
const result = await apiRequest(apiUrl, token, "GET", path);
|
|
209
|
+
if (!result.ok) {
|
|
210
|
+
console.error(`Error: ${result.error}`);
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
if (jsonOutput) {
|
|
214
|
+
console.log(JSON.stringify({ data: result.data, pagination: result.pagination }, null, 2));
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (markdownOutput) {
|
|
218
|
+
console.log(formatIssueListMarkdown(result.data, result.pagination));
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const { data, pagination } = result;
|
|
222
|
+
console.log(`Issues: ${pagination.totalItems} total (page ${pagination.page}/${pagination.totalPages})
|
|
223
|
+
`);
|
|
224
|
+
if (data.length === 0) {
|
|
225
|
+
console.log("No issues found.");
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
for (const issue of data) {
|
|
229
|
+
const icon = SEVERITY_ICONS[issue.severity] ?? "?";
|
|
230
|
+
const statusLabel = formatStatusLabel(issue.status);
|
|
231
|
+
const lastSeen = formatRelative(issue.lastSeenAt);
|
|
232
|
+
console.log(` [${icon}] ${truncate(issue.title, 60)} ${statusLabel} ${lastSeen} (${issue.occurrenceCount}x) ${issue.id}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
async function handleGet(argv, apiUrl, token) {
|
|
236
|
+
const issueId = argv[0];
|
|
237
|
+
if (!issueId || issueId.startsWith("--")) {
|
|
238
|
+
console.error("Error: Missing issue ID.");
|
|
239
|
+
console.error("Usage: canary issues get <issueId>");
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
const jsonOutput = hasFlag(argv, "--json");
|
|
243
|
+
const markdownOutput = getArgValue(argv, "--format") === "markdown";
|
|
244
|
+
const result = await apiRequest(apiUrl, token, "GET", `/v2/issues/${issueId}`);
|
|
245
|
+
if (!result.ok) {
|
|
246
|
+
console.error(`Error: ${result.error}`);
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
const { issue } = result.data;
|
|
250
|
+
const { consoleErrors, networkErrors } = await fetchDiagnostics(apiUrl, token, issue);
|
|
251
|
+
if (jsonOutput) {
|
|
252
|
+
console.log(JSON.stringify({ ...result.data, consoleErrors, networkErrors }, null, 2));
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (markdownOutput) {
|
|
256
|
+
console.log(formatIssueDetailMarkdown(issue, consoleErrors, networkErrors));
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const occ = issue.latestOccurrence;
|
|
260
|
+
console.log(` Title: ${issue.title}`);
|
|
261
|
+
console.log(` ID: ${issue.id}`);
|
|
262
|
+
console.log(` Status: ${formatStatusLabel(issue.status)}`);
|
|
263
|
+
console.log(` Severity: ${formatSeverity(issue.severity)}`);
|
|
264
|
+
console.log(` Category: ${issue.category.label}`);
|
|
265
|
+
console.log(` Occurrences: ${issue.occurrenceCount}`);
|
|
266
|
+
console.log(` First seen: ${formatRelative(issue.firstSeenAt)}`);
|
|
267
|
+
console.log(` Last seen: ${formatRelative(issue.lastSeenAt)}`);
|
|
268
|
+
if (occ?.primaryUrl) {
|
|
269
|
+
console.log(` URL: ${occ.primaryUrl}`);
|
|
270
|
+
}
|
|
271
|
+
if (issue.assignedTo) {
|
|
272
|
+
console.log(` Assigned to: ${issue.assignedTo.displayName ?? issue.assignedTo.primaryEmail ?? issue.assignedTo.id}`);
|
|
273
|
+
}
|
|
274
|
+
const summary = occ?.ticketUserFacingSummary?.trim();
|
|
275
|
+
if (summary) {
|
|
276
|
+
console.log(`
|
|
277
|
+
Summary:
|
|
278
|
+
${summary.split("\n").join("\n ")}`);
|
|
279
|
+
}
|
|
280
|
+
const diagnostic = occ?.diagnosticJson;
|
|
281
|
+
if (diagnostic) {
|
|
282
|
+
const categoryLabel = DIAGNOSTIC_CATEGORY_LABELS[diagnostic.category] ?? diagnostic.category;
|
|
283
|
+
console.log(`
|
|
284
|
+
Diagnostic: ${categoryLabel} (${Math.round(diagnostic.confidence * 100)}%)`);
|
|
285
|
+
console.log(` ${diagnostic.title}`);
|
|
286
|
+
console.log(` Recommendation: ${diagnostic.recommendation}`);
|
|
287
|
+
}
|
|
288
|
+
if (consoleErrors.length > 0) {
|
|
289
|
+
console.log(`
|
|
290
|
+
Console Errors (${consoleErrors.length}):`);
|
|
291
|
+
for (const entry of consoleErrors.slice(0, 5)) {
|
|
292
|
+
const loc = entry.location ? ` (${entry.location})` : "";
|
|
293
|
+
console.log(` [${entry.type}] ${truncate(entry.text, 100)}${loc}`);
|
|
294
|
+
}
|
|
295
|
+
if (consoleErrors.length > 5) {
|
|
296
|
+
console.log(` ... and ${consoleErrors.length - 5} more`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (networkErrors.length > 0) {
|
|
300
|
+
console.log(`
|
|
301
|
+
Network Errors (${networkErrors.length}):`);
|
|
302
|
+
for (const entry of networkErrors.slice(0, 5)) {
|
|
303
|
+
const status = entry.status === null || entry.status === 0 ? "failed" : String(entry.status);
|
|
304
|
+
console.log(` ${entry.method} ${truncate(entry.url, 80)} -> ${status}`);
|
|
305
|
+
}
|
|
306
|
+
if (networkErrors.length > 5) {
|
|
307
|
+
console.log(` ... and ${networkErrors.length - 5} more`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
function printIssuesHelp() {
|
|
312
|
+
console.log(
|
|
313
|
+
[
|
|
314
|
+
"Usage: canary issues <sub-command> [options]",
|
|
315
|
+
"",
|
|
316
|
+
"Sub-commands:",
|
|
317
|
+
" list [options] List and search issues",
|
|
318
|
+
" get <issueId> [options] Get issue detail with diagnostics",
|
|
319
|
+
"",
|
|
320
|
+
"List options:",
|
|
321
|
+
" --search <query> Full-text search",
|
|
322
|
+
" --severity <level> Filter: low, medium, high, unknown",
|
|
323
|
+
" --status <statuses> Filter: open, closed, not_a_bug (comma-separated)",
|
|
324
|
+
" --property-id <uuid> Filter by property",
|
|
325
|
+
" --page <n> Page number (default: 1)",
|
|
326
|
+
" --page-size <n> Page size (default: 25)",
|
|
327
|
+
"",
|
|
328
|
+
"Output options:",
|
|
329
|
+
" --json Output raw JSON",
|
|
330
|
+
" --format markdown Output as markdown",
|
|
331
|
+
"",
|
|
332
|
+
"Common options:",
|
|
333
|
+
" --env <env> Target environment (prod, dev, local)",
|
|
334
|
+
" --api-url <url> API URL override (takes precedence over --env)",
|
|
335
|
+
" --token <key> API token override"
|
|
336
|
+
].join("\n")
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
async function runIssues(argv) {
|
|
340
|
+
const [subCommand, ...rest] = argv;
|
|
341
|
+
if (!subCommand || subCommand === "help" || hasFlag(argv, "--help", "-h")) {
|
|
342
|
+
printIssuesHelp();
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
const { apiUrl, token } = await resolveConfig(argv);
|
|
346
|
+
switch (subCommand) {
|
|
347
|
+
case "list":
|
|
348
|
+
await handleList(rest, apiUrl, token);
|
|
349
|
+
break;
|
|
350
|
+
case "get":
|
|
351
|
+
await handleGet(rest, apiUrl, token);
|
|
352
|
+
break;
|
|
353
|
+
default:
|
|
354
|
+
console.error(`Unknown sub-command: ${subCommand}`);
|
|
355
|
+
printIssuesHelp();
|
|
356
|
+
process.exit(1);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
export {
|
|
360
|
+
runIssues
|
|
361
|
+
};
|
|
362
|
+
//# sourceMappingURL=issues-FI3RIWGV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/issues.ts"],"sourcesContent":["/**\n * CLI Issues Management\n *\n * Search, list, and view issues with full diagnostics from the terminal.\n */\n\nimport process from \"node:process\";\nimport { resolveConfig, getArgValue, hasFlag } from \"./auth.js\";\nimport { apiRequest } from \"./cli-helpers.js\";\n\n/* ── Types ────────────────────────────────────────────────────────────── */\n\ntype IssueCategorySummary = {\n id: string;\n key: string;\n label: string;\n description: string | null;\n instructions: string | null;\n};\n\ntype IssueAssignee = {\n id: string;\n displayName: string | null;\n primaryEmail: string | null;\n};\n\ntype FailureDiagnostic = {\n category: string;\n confidence: number;\n title: string;\n analysis: string;\n recommendation: string;\n smokingGun?: { description: string; detail?: string } | null;\n};\n\ntype IssueOccurrence = {\n id: string;\n primaryUrl: string;\n ticketUserFacingSummary: string | null;\n ticketReproSteps: string | null;\n diagnosticJson: FailureDiagnostic | null;\n consoleLogExcerpt: string | null;\n networkFailureUrl: string | null;\n networkFailureStatus: number | null;\n};\n\ntype IssueSummary = {\n id: string;\n title: string;\n severity: \"low\" | \"medium\" | \"high\" | \"unknown\";\n status: \"open\" | \"closed\" | \"not_a_bug\";\n category: IssueCategorySummary;\n firstSeenAt: string;\n lastSeenAt: string;\n occurrenceCount: number;\n assignedTo: IssueAssignee | null;\n latestOccurrence: IssueOccurrence | null;\n};\n\ntype ConsoleLogEntry = {\n type: string;\n text: string;\n timestamp: number | null;\n location: string | null;\n};\n\ntype NetworkRequestEntry = {\n url: string;\n method: string;\n status: number | null;\n durationMs: number | null;\n};\n\ntype Pagination = {\n page: number;\n pageSize: number;\n totalItems: number;\n totalPages: number;\n};\n\ntype IssueListResponse = {\n ok: boolean;\n error?: string;\n data: IssueSummary[];\n pagination: Pagination;\n};\n\ntype IssueDetailResponse = {\n ok: boolean;\n error?: string;\n data: {\n issue: IssueSummary;\n occurrences: IssueOccurrence[];\n commentCount: number;\n };\n};\n\ntype DiagnosticsDataResponse = {\n ok: boolean;\n data: ConsoleLogEntry[] | NetworkRequestEntry[];\n};\n\n/* ── Formatting Helpers ───────────────────────────────────────────────── */\n\nconst SEVERITY_ICONS: Record<string, string> = {\n high: \"!!!\",\n medium: \"!!\",\n low: \"!\",\n unknown: \"?\",\n};\n\nconst DIAGNOSTIC_CATEGORY_LABELS: Record<string, string> = {\n software_bug: \"Software Bug\",\n dependency_issue: \"Dependency Issue\",\n agent_confusion: \"Agent Confusion\",\n};\n\nfunction formatStatusLabel(status: string): string {\n switch (status) {\n case \"open\": return \"Open\";\n case \"closed\": return \"Closed\";\n case \"not_a_bug\": return \"Not a Bug\";\n default: return status;\n }\n}\n\nfunction formatSeverity(severity: string): string {\n return severity.charAt(0).toUpperCase() + severity.slice(1);\n}\n\nfunction formatRelative(value: string | null): string {\n if (!value) return \"—\";\n try {\n const date = new Date(value);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffMins = Math.floor(diffMs / 60000);\n if (diffMins < 1) return \"just now\";\n if (diffMins < 60) return `${diffMins}m ago`;\n const diffHours = Math.floor(diffMins / 60);\n if (diffHours < 24) return `${diffHours}h ago`;\n const diffDays = Math.floor(diffHours / 24);\n if (diffDays < 30) return `${diffDays}d ago`;\n return date.toISOString().slice(0, 10);\n } catch {\n return value;\n }\n}\n\nfunction truncate(text: string, max: number): string {\n if (text.length <= max) return text;\n return text.slice(0, max - 1) + \"…\";\n}\n\n/* ── Markdown Formatters ──────────────────────────────────────────────── */\n\nfunction formatIssueDetailMarkdown(\n issue: IssueSummary,\n consoleErrors: ConsoleLogEntry[],\n networkErrors: NetworkRequestEntry[]\n): string {\n const sections: string[] = [];\n const occ = issue.latestOccurrence;\n\n sections.push(`# ${issue.title}`);\n\n const meta: string[] = [];\n meta.push(`**Status:** ${formatStatusLabel(issue.status)}`);\n meta.push(`**Severity:** ${formatSeverity(issue.severity)}`);\n meta.push(`**Category:** ${issue.category.label}`);\n sections.push(meta.join(\" | \"));\n\n if (occ?.primaryUrl) {\n sections.push(`**URL:** ${occ.primaryUrl}`);\n }\n\n const timeParts: string[] = [];\n timeParts.push(`**First seen:** ${formatRelative(issue.firstSeenAt)}`);\n timeParts.push(`**Last seen:** ${formatRelative(issue.lastSeenAt)}`);\n timeParts.push(`**Occurrences:** ${issue.occurrenceCount}`);\n sections.push(timeParts.join(\" | \"));\n\n const summary = occ?.ticketUserFacingSummary?.trim();\n if (summary) {\n sections.push(`## Summary\\n${summary}`);\n }\n\n const reproSteps = occ?.ticketReproSteps?.trim();\n if (reproSteps) {\n sections.push(`## Reproduction Steps\\n${reproSteps}`);\n }\n\n const diagnostic = occ?.diagnosticJson;\n if (diagnostic) {\n const diagParts: string[] = [];\n const categoryLabel = DIAGNOSTIC_CATEGORY_LABELS[diagnostic.category] ?? diagnostic.category;\n diagParts.push(\"## Diagnostic Analysis\");\n diagParts.push(`**Category:** ${categoryLabel} (${Math.round(diagnostic.confidence * 100)}% confidence)`);\n diagParts.push(`**${diagnostic.title}**`);\n diagParts.push(diagnostic.analysis);\n\n if (diagnostic.smokingGun) {\n const gun = diagnostic.smokingGun;\n const gunLines = [`> **Key Evidence:** ${gun.description}`];\n if (gun.detail) {\n gunLines.push(`> \\`${gun.detail}\\``);\n }\n diagParts.push(gunLines.join(\"\\n\"));\n }\n\n diagParts.push(`**Recommendation:** ${diagnostic.recommendation}`);\n sections.push(diagParts.join(\"\\n\\n\"));\n }\n\n if (consoleErrors.length > 0) {\n const logLines = consoleErrors.map((entry) => {\n const loc = entry.location ? ` (${entry.location})` : \"\";\n return `[${entry.type}] ${entry.text}${loc}`;\n });\n sections.push(`## Console Errors\\n\\`\\`\\`\\n${logLines.join(\"\\n\")}\\n\\`\\`\\``);\n }\n\n if (networkErrors.length > 0) {\n const rows = networkErrors.map((entry) => {\n const status =\n entry.status === null || entry.status === 0\n ? \"0 (failed)\"\n : String(entry.status);\n const duration = entry.durationMs != null ? `${entry.durationMs}ms` : \"—\";\n return `| ${entry.method} | ${entry.url} | ${status} | ${duration} |`;\n });\n sections.push(\n `## Network Errors\\n| Method | URL | Status | Duration |\\n|--------|-----|--------|----------|\\n${rows.join(\"\\n\")}`\n );\n }\n\n return sections.join(\"\\n\\n\");\n}\n\nfunction formatIssueListMarkdown(data: IssueSummary[], pagination: Pagination): string {\n const sections: string[] = [];\n sections.push(`# Issues (page ${pagination.page} of ${pagination.totalPages}, ${pagination.totalItems} total)`);\n\n if (data.length === 0) {\n sections.push(\"No issues found.\");\n return sections.join(\"\\n\\n\");\n }\n\n const header = \"| Severity | Title | Status | Last Seen | Occurrences |\";\n const divider = \"|----------|-------|--------|-----------|-------------|\";\n const rows = data.map((issue) =>\n `| ${issue.severity} | ${truncate(issue.title, 60)} | ${issue.status} | ${formatRelative(issue.lastSeenAt)} | ${issue.occurrenceCount} |`\n );\n sections.push([header, divider, ...rows].join(\"\\n\"));\n\n return sections.join(\"\\n\\n\");\n}\n\n/* ── Diagnostics Fetching ─────────────────────────────────────────────── */\n\nasync function fetchDiagnostics(\n apiUrl: string,\n token: string,\n issue: IssueSummary\n): Promise<{ consoleErrors: ConsoleLogEntry[]; networkErrors: NetworkRequestEntry[] }> {\n const occ = issue.latestOccurrence;\n if (!occ) return { consoleErrors: [], networkErrors: [] };\n\n let consoleErrors: ConsoleLogEntry[] = [];\n let networkErrors: NetworkRequestEntry[] = [];\n\n try {\n const [consoleRes, networkRes] = await Promise.all([\n apiRequest<DiagnosticsDataResponse>(\n apiUrl, token, \"GET\",\n `/v2/issues/${issue.id}/occurrences/${occ.id}/console-logs`\n ),\n apiRequest<DiagnosticsDataResponse>(\n apiUrl, token, \"GET\",\n `/v2/issues/${issue.id}/occurrences/${occ.id}/network-requests`\n ),\n ]);\n\n if (consoleRes.ok && Array.isArray(consoleRes.data)) {\n consoleErrors = (consoleRes.data as ConsoleLogEntry[]).filter((e) => e.type === \"error\");\n }\n\n if (networkRes.ok && Array.isArray(networkRes.data)) {\n networkErrors = (networkRes.data as NetworkRequestEntry[]).filter(\n (e) => e.status === null || e.status === 0 || e.status >= 400\n );\n }\n } catch {\n // Diagnostics are best-effort; continue without them\n }\n\n // Fallback to legacy fields\n if (consoleErrors.length === 0 && occ.consoleLogExcerpt?.trim()) {\n consoleErrors = [{ type: \"error\", text: occ.consoleLogExcerpt.trim(), timestamp: null, location: null }];\n }\n\n if (networkErrors.length === 0 && occ.networkFailureUrl) {\n networkErrors = [{\n url: occ.networkFailureUrl,\n method: \"GET\",\n status: occ.networkFailureStatus,\n durationMs: null,\n }];\n }\n\n return { consoleErrors, networkErrors };\n}\n\n/* ── Sub-command Handlers ─────────────────────────────────────────────── */\n\nasync function handleList(argv: string[], apiUrl: string, token: string): Promise<void> {\n const jsonOutput = hasFlag(argv, \"--json\");\n const markdownOutput = getArgValue(argv, \"--format\") === \"markdown\";\n\n const params = new URLSearchParams();\n const search = getArgValue(argv, \"--search\");\n const severity = getArgValue(argv, \"--severity\");\n const status = getArgValue(argv, \"--status\");\n const propertyId = getArgValue(argv, \"--property-id\");\n const page = getArgValue(argv, \"--page\");\n const pageSize = getArgValue(argv, \"--page-size\");\n\n if (search) params.set(\"search\", search);\n if (severity) params.set(\"severity\", severity);\n if (status) params.set(\"statuses\", status);\n if (propertyId) params.set(\"propertyId\", propertyId);\n if (page) params.set(\"page\", page);\n if (pageSize) params.set(\"pageSize\", pageSize);\n\n const qs = params.toString();\n const path = `/v2/issues${qs ? `?${qs}` : \"\"}`;\n\n const result = await apiRequest<IssueListResponse>(apiUrl, token, \"GET\", path);\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n if (jsonOutput) {\n console.log(JSON.stringify({ data: result.data, pagination: result.pagination }, null, 2));\n return;\n }\n\n if (markdownOutput) {\n console.log(formatIssueListMarkdown(result.data, result.pagination));\n return;\n }\n\n // Default compact output\n const { data, pagination } = result;\n console.log(`Issues: ${pagination.totalItems} total (page ${pagination.page}/${pagination.totalPages})\\n`);\n\n if (data.length === 0) {\n console.log(\"No issues found.\");\n return;\n }\n\n for (const issue of data) {\n const icon = SEVERITY_ICONS[issue.severity] ?? \"?\";\n const statusLabel = formatStatusLabel(issue.status);\n const lastSeen = formatRelative(issue.lastSeenAt);\n console.log(` [${icon}] ${truncate(issue.title, 60)} ${statusLabel} ${lastSeen} (${issue.occurrenceCount}x) ${issue.id}`);\n }\n}\n\nasync function handleGet(argv: string[], apiUrl: string, token: string): Promise<void> {\n const issueId = argv[0];\n if (!issueId || issueId.startsWith(\"--\")) {\n console.error(\"Error: Missing issue ID.\");\n console.error(\"Usage: canary issues get <issueId>\");\n process.exit(1);\n }\n\n const jsonOutput = hasFlag(argv, \"--json\");\n const markdownOutput = getArgValue(argv, \"--format\") === \"markdown\";\n\n const result = await apiRequest<IssueDetailResponse>(apiUrl, token, \"GET\", `/v2/issues/${issueId}`);\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n const { issue } = result.data;\n const { consoleErrors, networkErrors } = await fetchDiagnostics(apiUrl, token, issue);\n\n if (jsonOutput) {\n console.log(JSON.stringify({ ...result.data, consoleErrors, networkErrors }, null, 2));\n return;\n }\n\n if (markdownOutput) {\n console.log(formatIssueDetailMarkdown(issue, consoleErrors, networkErrors));\n return;\n }\n\n // Default human-readable output\n const occ = issue.latestOccurrence;\n console.log(` Title: ${issue.title}`);\n console.log(` ID: ${issue.id}`);\n console.log(` Status: ${formatStatusLabel(issue.status)}`);\n console.log(` Severity: ${formatSeverity(issue.severity)}`);\n console.log(` Category: ${issue.category.label}`);\n console.log(` Occurrences: ${issue.occurrenceCount}`);\n console.log(` First seen: ${formatRelative(issue.firstSeenAt)}`);\n console.log(` Last seen: ${formatRelative(issue.lastSeenAt)}`);\n\n if (occ?.primaryUrl) {\n console.log(` URL: ${occ.primaryUrl}`);\n }\n\n if (issue.assignedTo) {\n console.log(` Assigned to: ${issue.assignedTo.displayName ?? issue.assignedTo.primaryEmail ?? issue.assignedTo.id}`);\n }\n\n const summary = occ?.ticketUserFacingSummary?.trim();\n if (summary) {\n console.log(`\\n Summary:\\n ${summary.split(\"\\n\").join(\"\\n \")}`);\n }\n\n const diagnostic = occ?.diagnosticJson;\n if (diagnostic) {\n const categoryLabel = DIAGNOSTIC_CATEGORY_LABELS[diagnostic.category] ?? diagnostic.category;\n console.log(`\\n Diagnostic: ${categoryLabel} (${Math.round(diagnostic.confidence * 100)}%)`);\n console.log(` ${diagnostic.title}`);\n console.log(` Recommendation: ${diagnostic.recommendation}`);\n }\n\n if (consoleErrors.length > 0) {\n console.log(`\\n Console Errors (${consoleErrors.length}):`);\n for (const entry of consoleErrors.slice(0, 5)) {\n const loc = entry.location ? ` (${entry.location})` : \"\";\n console.log(` [${entry.type}] ${truncate(entry.text, 100)}${loc}`);\n }\n if (consoleErrors.length > 5) {\n console.log(` ... and ${consoleErrors.length - 5} more`);\n }\n }\n\n if (networkErrors.length > 0) {\n console.log(`\\n Network Errors (${networkErrors.length}):`);\n for (const entry of networkErrors.slice(0, 5)) {\n const status = entry.status === null || entry.status === 0 ? \"failed\" : String(entry.status);\n console.log(` ${entry.method} ${truncate(entry.url, 80)} -> ${status}`);\n }\n if (networkErrors.length > 5) {\n console.log(` ... and ${networkErrors.length - 5} more`);\n }\n }\n}\n\n/* ── Help & Entry Point ───────────────────────────────────────────────── */\n\nfunction printIssuesHelp(): void {\n console.log(\n [\n \"Usage: canary issues <sub-command> [options]\",\n \"\",\n \"Sub-commands:\",\n \" list [options] List and search issues\",\n \" get <issueId> [options] Get issue detail with diagnostics\",\n \"\",\n \"List options:\",\n \" --search <query> Full-text search\",\n \" --severity <level> Filter: low, medium, high, unknown\",\n \" --status <statuses> Filter: open, closed, not_a_bug (comma-separated)\",\n \" --property-id <uuid> Filter by property\",\n \" --page <n> Page number (default: 1)\",\n \" --page-size <n> Page size (default: 25)\",\n \"\",\n \"Output options:\",\n \" --json Output raw JSON\",\n \" --format markdown Output as markdown\",\n \"\",\n \"Common options:\",\n \" --env <env> Target environment (prod, dev, local)\",\n \" --api-url <url> API URL override (takes precedence over --env)\",\n \" --token <key> API token override\",\n ].join(\"\\n\")\n );\n}\n\nexport async function runIssues(argv: string[]): Promise<void> {\n const [subCommand, ...rest] = argv;\n\n if (!subCommand || subCommand === \"help\" || hasFlag(argv, \"--help\", \"-h\")) {\n printIssuesHelp();\n return;\n }\n\n const { apiUrl, token } = await resolveConfig(argv);\n\n switch (subCommand) {\n case \"list\":\n await handleList(rest, apiUrl, token);\n break;\n case \"get\":\n await handleGet(rest, apiUrl, token);\n break;\n default:\n console.error(`Unknown sub-command: ${subCommand}`);\n printIssuesHelp();\n process.exit(1);\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,53 +1,24 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
apiRequest,
|
|
3
|
+
fetchList,
|
|
4
|
+
parseLifecycleStage,
|
|
5
|
+
toLifecycleLabel
|
|
6
|
+
} from "./chunk-DXJNFJ3A.js";
|
|
7
|
+
import {
|
|
8
|
+
getArgValue,
|
|
9
|
+
hasFlag,
|
|
10
|
+
resolveConfig
|
|
11
|
+
} from "./chunk-7R4YFGP6.js";
|
|
5
12
|
import "./chunk-DGUM43GV.js";
|
|
6
13
|
|
|
7
14
|
// src/knobs.ts
|
|
8
15
|
import process from "process";
|
|
9
|
-
var ENV_URLS = {
|
|
10
|
-
prod: "https://api.trycanary.ai",
|
|
11
|
-
production: "https://api.trycanary.ai",
|
|
12
|
-
dev: "https://api.dev.trycanary.ai",
|
|
13
|
-
local: "http://localhost:3000"
|
|
14
|
-
};
|
|
15
|
-
function getArgValue(argv, key) {
|
|
16
|
-
const index = argv.indexOf(key);
|
|
17
|
-
if (index === -1 || index >= argv.length - 1) return void 0;
|
|
18
|
-
return argv[index + 1];
|
|
19
|
-
}
|
|
20
|
-
function hasFlag(argv, ...flags) {
|
|
21
|
-
return flags.some((flag) => argv.includes(flag));
|
|
22
|
-
}
|
|
23
|
-
function toLifecycleLabel(stage) {
|
|
24
|
-
switch (stage) {
|
|
25
|
-
case "deprecated":
|
|
26
|
-
return "deprecated";
|
|
27
|
-
case "ready_for_cleanup":
|
|
28
|
-
return "ready_for_cleanup";
|
|
29
|
-
default:
|
|
30
|
-
return "active";
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
function parseLifecycleStage(argv) {
|
|
34
|
-
const stage = getArgValue(argv, "--stage");
|
|
35
|
-
if (!stage || !["active", "deprecated", "ready_for_cleanup"].includes(stage)) {
|
|
36
|
-
console.error("Error: --stage is required and must be one of: active, deprecated, ready_for_cleanup");
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
return stage;
|
|
40
|
-
}
|
|
41
16
|
function formatValue(valueType, value) {
|
|
42
|
-
if (valueType === "json")
|
|
43
|
-
return JSON.stringify(value);
|
|
44
|
-
}
|
|
17
|
+
if (valueType === "json") return JSON.stringify(value);
|
|
45
18
|
return String(value);
|
|
46
19
|
}
|
|
47
20
|
function formatFinalValue(knob) {
|
|
48
|
-
if (knob.lifecycleStage === "active")
|
|
49
|
-
return "(none)";
|
|
50
|
-
}
|
|
21
|
+
if (knob.lifecycleStage === "active") return "(none)";
|
|
51
22
|
return formatValue(knob.valueType, knob.finalValue);
|
|
52
23
|
}
|
|
53
24
|
function parseTypedValue(raw, valueType) {
|
|
@@ -77,55 +48,8 @@ function parseTypedValue(raw, valueType) {
|
|
|
77
48
|
}
|
|
78
49
|
}
|
|
79
50
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const env = getArgValue(argv, "--env");
|
|
83
|
-
if (env && !ENV_URLS[env]) {
|
|
84
|
-
console.error(`Unknown environment: ${env}`);
|
|
85
|
-
console.error("Valid environments: prod, dev, local");
|
|
86
|
-
process.exit(1);
|
|
87
|
-
}
|
|
88
|
-
const apiUrl = getArgValue(argv, "--api-url") ?? (env ? ENV_URLS[env] : void 0) ?? process.env.CANARY_API_URL ?? storedApiUrl ?? "https://api.trycanary.ai";
|
|
89
|
-
const token = getArgValue(argv, "--token") ?? process.env.CANARY_API_TOKEN ?? await readStoredToken();
|
|
90
|
-
if (!token) {
|
|
91
|
-
console.error("Error: No API token found.");
|
|
92
|
-
console.error("Run: canary login");
|
|
93
|
-
process.exit(1);
|
|
94
|
-
}
|
|
95
|
-
return { apiUrl, token };
|
|
96
|
-
}
|
|
97
|
-
async function apiRequest(apiUrl, token, method, path, body) {
|
|
98
|
-
const res = await fetch(`${apiUrl}${path}`, {
|
|
99
|
-
method,
|
|
100
|
-
headers: {
|
|
101
|
-
Authorization: `Bearer ${token}`,
|
|
102
|
-
"Content-Type": "application/json"
|
|
103
|
-
},
|
|
104
|
-
...body ? { body: JSON.stringify(body) } : {}
|
|
105
|
-
});
|
|
106
|
-
if (res.status === 401) {
|
|
107
|
-
console.error("Error: Unauthorized. Your session may have expired.");
|
|
108
|
-
console.error("Run: canary login");
|
|
109
|
-
process.exit(1);
|
|
110
|
-
}
|
|
111
|
-
const json = await res.json();
|
|
112
|
-
return json;
|
|
113
|
-
}
|
|
114
|
-
async function fetchKnobs(apiUrl, token) {
|
|
115
|
-
const res = await fetch(`${apiUrl}/superadmin/knobs`, {
|
|
116
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
117
|
-
});
|
|
118
|
-
if (res.status === 401) {
|
|
119
|
-
console.error("Error: Unauthorized. Your session may have expired.");
|
|
120
|
-
console.error("Run: canary login");
|
|
121
|
-
process.exit(1);
|
|
122
|
-
}
|
|
123
|
-
const json = await res.json();
|
|
124
|
-
if (!json.ok) {
|
|
125
|
-
console.error(`Error: ${json.error}`);
|
|
126
|
-
process.exit(1);
|
|
127
|
-
}
|
|
128
|
-
return json.knobs ?? [];
|
|
51
|
+
function fetchKnobs(apiUrl, token) {
|
|
52
|
+
return fetchList(apiUrl, token, "/superadmin/knobs", "knobs");
|
|
129
53
|
}
|
|
130
54
|
async function handleList(argv, apiUrl, token) {
|
|
131
55
|
const jsonOutput = hasFlag(argv, "--json");
|
|
@@ -177,17 +101,13 @@ async function handleSet(argv, apiUrl, token) {
|
|
|
177
101
|
const key = argv[0];
|
|
178
102
|
if (!key || key.startsWith("--")) {
|
|
179
103
|
console.error("Error: Missing knob key.");
|
|
180
|
-
console.error(
|
|
181
|
-
"Usage: canary knobs set <key> <value> --type <boolean|string|number|json> [--description <text>]"
|
|
182
|
-
);
|
|
104
|
+
console.error("Usage: canary knobs set <key> <value> --type <boolean|string|number|json> [--description <text>]");
|
|
183
105
|
process.exit(1);
|
|
184
106
|
}
|
|
185
107
|
const rawValue = argv[1];
|
|
186
108
|
if (rawValue === void 0 || rawValue.startsWith("--")) {
|
|
187
109
|
console.error("Error: Missing knob value.");
|
|
188
|
-
console.error(
|
|
189
|
-
"Usage: canary knobs set <key> <value> --type <boolean|string|number|json> [--description <text>]"
|
|
190
|
-
);
|
|
110
|
+
console.error("Usage: canary knobs set <key> <value> --type <boolean|string|number|json> [--description <text>]");
|
|
191
111
|
process.exit(1);
|
|
192
112
|
}
|
|
193
113
|
const valueType = getArgValue(argv, "--type");
|
|
@@ -252,9 +172,7 @@ async function handleLifecycle(argv, apiUrl, token) {
|
|
|
252
172
|
const key = argv[0];
|
|
253
173
|
if (!key || key.startsWith("--")) {
|
|
254
174
|
console.error("Error: Missing knob key.");
|
|
255
|
-
console.error(
|
|
256
|
-
"Usage: canary knobs lifecycle <key> --stage <active|deprecated|ready_for_cleanup> [--final-value <value>]"
|
|
257
|
-
);
|
|
175
|
+
console.error("Usage: canary knobs lifecycle <key> --stage <active|deprecated|ready_for_cleanup> [--final-value <value>]");
|
|
258
176
|
process.exit(1);
|
|
259
177
|
}
|
|
260
178
|
const stage = parseLifecycleStage(argv);
|
|
@@ -293,10 +211,7 @@ async function handleLifecycle(argv, apiUrl, token) {
|
|
|
293
211
|
token,
|
|
294
212
|
"POST",
|
|
295
213
|
`/superadmin/knobs/${encodeURIComponent(key)}/lifecycle`,
|
|
296
|
-
{
|
|
297
|
-
stage,
|
|
298
|
-
finalValue
|
|
299
|
-
}
|
|
214
|
+
{ stage, finalValue }
|
|
300
215
|
);
|
|
301
216
|
if (!result.ok || !result.knob) {
|
|
302
217
|
console.error(`Error: ${result.error ?? "Failed to update lifecycle"}`);
|
|
@@ -369,4 +284,4 @@ async function runKnobs(argv) {
|
|
|
369
284
|
export {
|
|
370
285
|
runKnobs
|
|
371
286
|
};
|
|
372
|
-
//# sourceMappingURL=knobs-
|
|
287
|
+
//# sourceMappingURL=knobs-3MKMOXIV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/knobs.ts"],"sourcesContent":["/**\n * CLI Knobs Management\n *\n * Allows superadmins to manage knobs (global config) via the CLI.\n */\n\nimport process from \"node:process\";\nimport { resolveConfig, getArgValue, hasFlag } from \"./auth.js\";\nimport {\n type LifecycleStage,\n toLifecycleLabel,\n parseLifecycleStage,\n apiRequest,\n fetchList,\n} from \"./cli-helpers.js\";\n\ntype SerializedKnob = {\n key: string;\n description: string | null;\n valueType: \"boolean\" | \"string\" | \"number\" | \"json\";\n value: unknown;\n lifecycleStage: LifecycleStage;\n finalValue: unknown;\n updatedAt: string | null;\n updatedBy: string | null;\n createdAt: string | null;\n};\n\ntype KnobApiResponse = {\n ok: boolean;\n error?: string;\n knob?: SerializedKnob;\n};\n\nfunction formatValue(valueType: SerializedKnob[\"valueType\"], value: unknown): string {\n if (valueType === \"json\") return JSON.stringify(value);\n return String(value);\n}\n\nfunction formatFinalValue(knob: SerializedKnob): string {\n if (knob.lifecycleStage === \"active\") return \"(none)\";\n return formatValue(knob.valueType, knob.finalValue);\n}\n\nfunction parseTypedValue(raw: string, valueType: SerializedKnob[\"valueType\"]): unknown {\n switch (valueType) {\n case \"boolean\":\n if (raw !== \"true\" && raw !== \"false\") {\n console.error('Error: Boolean value must be \"true\" or \"false\".');\n process.exit(1);\n }\n return raw === \"true\";\n case \"string\":\n return raw;\n case \"number\": {\n const num = parseFloat(raw);\n if (Number.isNaN(num)) {\n console.error(`Error: Invalid number: ${raw}`);\n process.exit(1);\n }\n return num;\n }\n case \"json\":\n try {\n return JSON.parse(raw);\n } catch {\n console.error(`Error: Invalid JSON: ${raw}`);\n process.exit(1);\n }\n }\n}\n\nfunction fetchKnobs(apiUrl: string, token: string): Promise<SerializedKnob[]> {\n return fetchList<SerializedKnob>(apiUrl, token, \"/superadmin/knobs\", \"knobs\");\n}\n\nasync function handleList(argv: string[], apiUrl: string, token: string): Promise<void> {\n const jsonOutput = hasFlag(argv, \"--json\");\n const knobs = await fetchKnobs(apiUrl, token);\n\n if (jsonOutput) {\n console.log(JSON.stringify(knobs, null, 2));\n return;\n }\n\n if (knobs.length === 0) {\n console.log(\"No knobs found.\");\n return;\n }\n\n for (const knob of knobs) {\n const desc = knob.description ? ` ${knob.description}` : \"\";\n const lifecycle = `lifecycle=${toLifecycleLabel(knob.lifecycleStage)}`;\n const finalValue =\n knob.lifecycleStage === \"active\" ? \"\" : ` final=${formatFinalValue(knob)}`;\n console.log(\n ` ${knob.key} [${knob.valueType}] = ${formatValue(knob.valueType, knob.value)} (${lifecycle}${finalValue})${desc}`\n );\n }\n}\n\nasync function handleGet(argv: string[], apiUrl: string, token: string): Promise<void> {\n const key = argv[0];\n if (!key || key.startsWith(\"--\")) {\n console.error(\"Error: Missing knob key.\");\n console.error(\"Usage: canary knobs get <key>\");\n process.exit(1);\n }\n\n const jsonOutput = hasFlag(argv, \"--json\");\n const knobs = await fetchKnobs(apiUrl, token);\n const knob = knobs.find((k) => k.key === key);\n\n if (!knob) {\n console.error(`Knob not found: ${key}`);\n process.exit(1);\n }\n\n if (jsonOutput) {\n console.log(JSON.stringify(knob, null, 2));\n return;\n }\n\n console.log(` Key: ${knob.key}`);\n console.log(` Type: ${knob.valueType}`);\n console.log(` Value: ${formatValue(knob.valueType, knob.value)}`);\n console.log(` Lifecycle: ${toLifecycleLabel(knob.lifecycleStage)}`);\n console.log(` Final value: ${formatFinalValue(knob)}`);\n console.log(` Description: ${knob.description ?? \"(none)\"}`);\n console.log(` Updated: ${knob.updatedAt ?? \"(never)\"}`);\n}\n\nasync function handleSet(argv: string[], apiUrl: string, token: string): Promise<void> {\n const key = argv[0];\n if (!key || key.startsWith(\"--\")) {\n console.error(\"Error: Missing knob key.\");\n console.error(\"Usage: canary knobs set <key> <value> --type <boolean|string|number|json> [--description <text>]\");\n process.exit(1);\n }\n\n const rawValue = argv[1];\n if (rawValue === undefined || rawValue.startsWith(\"--\")) {\n console.error(\"Error: Missing knob value.\");\n console.error(\"Usage: canary knobs set <key> <value> --type <boolean|string|number|json> [--description <text>]\");\n process.exit(1);\n }\n\n const valueType = getArgValue(argv, \"--type\") as SerializedKnob[\"valueType\"] | undefined;\n if (!valueType || ![\"boolean\", \"string\", \"number\", \"json\"].includes(valueType)) {\n console.error(\"Error: --type is required and must be one of: boolean, string, number, json\");\n process.exit(1);\n }\n\n const value = parseTypedValue(rawValue, valueType);\n const description = getArgValue(argv, \"--description\") ?? undefined;\n\n const result = await apiRequest<KnobApiResponse>(apiUrl, token, \"POST\", \"/superadmin/knobs\", {\n key,\n valueType,\n value,\n description,\n });\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Set knob: ${key} = ${formatValue(valueType, result.knob?.value)}`);\n}\n\nasync function handleDelete(argv: string[], apiUrl: string, token: string): Promise<void> {\n const key = argv[0];\n if (!key || key.startsWith(\"--\")) {\n console.error(\"Error: Missing knob key.\");\n console.error(\"Usage: canary knobs delete <key>\");\n process.exit(1);\n }\n\n const result = await apiRequest<KnobApiResponse>(\n apiUrl, token, \"DELETE\", `/superadmin/knobs/${encodeURIComponent(key)}`\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Deleted knob: ${key}`);\n}\n\nasync function handleToggle(argv: string[], apiUrl: string, token: string): Promise<void> {\n const key = argv[0];\n if (!key || key.startsWith(\"--\")) {\n console.error(\"Error: Missing knob key.\");\n console.error(\"Usage: canary knobs toggle <key>\");\n process.exit(1);\n }\n\n const result = await apiRequest<KnobApiResponse>(\n apiUrl, token, \"POST\", `/superadmin/knobs/${encodeURIComponent(key)}/toggle`\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n const knob = result.knob;\n console.log(`Toggled knob: ${key} -> ${formatValue(knob?.valueType ?? \"boolean\", knob?.value)}`);\n}\n\nasync function handleLifecycle(argv: string[], apiUrl: string, token: string): Promise<void> {\n const key = argv[0];\n if (!key || key.startsWith(\"--\")) {\n console.error(\"Error: Missing knob key.\");\n console.error(\"Usage: canary knobs lifecycle <key> --stage <active|deprecated|ready_for_cleanup> [--final-value <value>]\");\n process.exit(1);\n }\n\n const stage = parseLifecycleStage(argv);\n const rawFinalValue = getArgValue(argv, \"--final-value\");\n const clearFinalValue = hasFlag(argv, \"--clear-final-value\");\n\n if (rawFinalValue !== undefined && clearFinalValue) {\n console.error(\"Error: use either --final-value or --clear-final-value, not both.\");\n process.exit(1);\n }\n\n const knobs = await fetchKnobs(apiUrl, token);\n const knob = knobs.find((k) => k.key === key);\n\n if (!knob) {\n console.error(`Knob not found: ${key}`);\n process.exit(1);\n }\n\n let finalValue: unknown = undefined;\n\n if (stage === \"active\") {\n if (rawFinalValue !== undefined) {\n console.error(\"Error: active stage does not accept --final-value. Use --stage deprecated|ready_for_cleanup.\");\n process.exit(1);\n }\n finalValue = clearFinalValue ? undefined : undefined;\n } else {\n if (clearFinalValue) {\n console.error(\"Error: --clear-final-value can only be used with --stage active.\");\n process.exit(1);\n }\n if (rawFinalValue === undefined) {\n console.error(\"Error: --final-value is required when stage is deprecated or ready_for_cleanup.\");\n process.exit(1);\n }\n finalValue = parseTypedValue(rawFinalValue, knob.valueType);\n }\n\n const result = await apiRequest<KnobApiResponse>(\n apiUrl, token, \"POST\", `/superadmin/knobs/${encodeURIComponent(key)}/lifecycle`,\n { stage, finalValue }\n );\n\n if (!result.ok || !result.knob) {\n console.error(`Error: ${result.error ?? \"Failed to update lifecycle\"}`);\n process.exit(1);\n }\n\n console.log(\n `Updated lifecycle for ${key}: stage=${toLifecycleLabel(result.knob.lifecycleStage)}, final=${formatFinalValue(result.knob)}`\n );\n}\n\nfunction printKnobsHelp(): void {\n console.log(\n [\n \"Usage: canary knobs <sub-command> [options]\",\n \"\",\n \"Sub-commands:\",\n \" list List all knobs\",\n \" get <key> Get a knob value\",\n \" set <key> <value> --type <type> [--description <text>]\",\n \" Set a knob value\",\n \" delete <key> Delete a knob\",\n \" toggle <key> Toggle a boolean knob\",\n \" lifecycle <key> --stage <stage> [--final-value <value>]\",\n \" Mark knob lifecycle + final value\",\n \"\",\n \"Types: boolean, string, number, json\",\n \"Stages: active, deprecated, ready_for_cleanup\",\n \"\",\n \"Options:\",\n \" --final-value <value> Final value for deprecated/ready_for_cleanup\",\n \" --clear-final-value Clear final value (only valid with --stage active)\",\n \" --env <env> Target environment (prod, dev, local)\",\n \" --json Output as JSON (list/get only)\",\n \" --api-url <url> API URL override (takes precedence over --env)\",\n \" --token <key> API token override\",\n ].join(\"\\n\")\n );\n}\n\nexport async function runKnobs(argv: string[]): Promise<void> {\n const [subCommand, ...rest] = argv;\n\n if (!subCommand || subCommand === \"help\" || hasFlag(argv, \"--help\", \"-h\")) {\n printKnobsHelp();\n return;\n }\n\n const { apiUrl, token } = await resolveConfig(argv);\n\n switch (subCommand) {\n case \"list\":\n await handleList(rest, apiUrl, token);\n break;\n case \"get\":\n await handleGet(rest, apiUrl, token);\n break;\n case \"set\":\n await handleSet(rest, apiUrl, token);\n break;\n case \"delete\":\n await handleDelete(rest, apiUrl, token);\n break;\n case \"toggle\":\n await handleToggle(rest, apiUrl, token);\n break;\n case \"lifecycle\":\n await handleLifecycle(rest, apiUrl, token);\n break;\n default:\n console.error(`Unknown sub-command: ${subCommand}`);\n printKnobsHelp();\n process.exit(1);\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-UEOXNF5X.js";
|
|
4
4
|
import {
|
|
5
5
|
readStoredToken
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-7R4YFGP6.js";
|
|
7
7
|
import "./chunk-DGUM43GV.js";
|
|
8
8
|
|
|
9
9
|
// src/local-browser/index.ts
|
|
@@ -137,4 +137,4 @@ async function runLocalBrowser(args) {
|
|
|
137
137
|
export {
|
|
138
138
|
runLocalBrowser
|
|
139
139
|
};
|
|
140
|
-
//# sourceMappingURL=local-browser-
|
|
140
|
+
//# sourceMappingURL=local-browser-GG5GUXDS.js.map
|