mitsupi 1.0.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/LICENSE +201 -0
- package/README.md +95 -0
- package/TODO.md +11 -0
- package/commands/handoff.md +100 -0
- package/commands/make-release.md +75 -0
- package/commands/pickup.md +30 -0
- package/commands/update-changelog.md +78 -0
- package/package.json +22 -0
- package/pi-extensions/answer.ts +527 -0
- package/pi-extensions/codex-tuning.ts +632 -0
- package/pi-extensions/commit.ts +248 -0
- package/pi-extensions/cwd-history.ts +237 -0
- package/pi-extensions/issues.ts +548 -0
- package/pi-extensions/loop.ts +446 -0
- package/pi-extensions/qna.ts +167 -0
- package/pi-extensions/reveal.ts +689 -0
- package/pi-extensions/review.ts +807 -0
- package/pi-themes/armin.json +81 -0
- package/pi-themes/nightowl.json +82 -0
- package/skills/anachb/SKILL.md +183 -0
- package/skills/anachb/departures.sh +79 -0
- package/skills/anachb/disruptions.sh +53 -0
- package/skills/anachb/route.sh +87 -0
- package/skills/anachb/search.sh +43 -0
- package/skills/ghidra/SKILL.md +254 -0
- package/skills/ghidra/scripts/find-ghidra.sh +54 -0
- package/skills/ghidra/scripts/ghidra-analyze.sh +239 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportAll.java +278 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportCalls.java +148 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportDecompiled.java +84 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportFunctions.java +114 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportStrings.java +123 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportSymbols.java +135 -0
- package/skills/github/SKILL.md +47 -0
- package/skills/improve-skill/SKILL.md +155 -0
- package/skills/improve-skill/scripts/extract-session.js +349 -0
- package/skills/oebb-scotty/SKILL.md +429 -0
- package/skills/oebb-scotty/arrivals.sh +83 -0
- package/skills/oebb-scotty/departures.sh +83 -0
- package/skills/oebb-scotty/disruptions.sh +33 -0
- package/skills/oebb-scotty/search-station.sh +36 -0
- package/skills/oebb-scotty/trip.sh +119 -0
- package/skills/openscad/SKILL.md +232 -0
- package/skills/openscad/examples/parametric_box.scad +92 -0
- package/skills/openscad/examples/phone_stand.scad +95 -0
- package/skills/openscad/tools/common.sh +50 -0
- package/skills/openscad/tools/export-stl.sh +56 -0
- package/skills/openscad/tools/extract-params.sh +147 -0
- package/skills/openscad/tools/multi-preview.sh +68 -0
- package/skills/openscad/tools/preview.sh +74 -0
- package/skills/openscad/tools/render-with-params.sh +91 -0
- package/skills/openscad/tools/validate.sh +46 -0
- package/skills/pi-share/SKILL.md +105 -0
- package/skills/pi-share/fetch-session.mjs +322 -0
- package/skills/sentry/SKILL.md +239 -0
- package/skills/sentry/lib/auth.js +99 -0
- package/skills/sentry/scripts/fetch-event.js +329 -0
- package/skills/sentry/scripts/fetch-issue.js +356 -0
- package/skills/sentry/scripts/list-issues.js +239 -0
- package/skills/sentry/scripts/search-events.js +291 -0
- package/skills/sentry/scripts/search-logs.js +240 -0
- package/skills/tmux/SKILL.md +105 -0
- package/skills/tmux/scripts/find-sessions.sh +112 -0
- package/skills/tmux/scripts/wait-for-text.sh +83 -0
- package/skills/web-browser/SKILL.md +91 -0
- package/skills/web-browser/scripts/cdp.js +210 -0
- package/skills/web-browser/scripts/dismiss-cookies.js +373 -0
- package/skills/web-browser/scripts/eval.js +68 -0
- package/skills/web-browser/scripts/logs-tail.js +69 -0
- package/skills/web-browser/scripts/nav.js +65 -0
- package/skills/web-browser/scripts/net-summary.js +94 -0
- package/skills/web-browser/scripts/package-lock.json +33 -0
- package/skills/web-browser/scripts/package.json +6 -0
- package/skills/web-browser/scripts/pick.js +165 -0
- package/skills/web-browser/scripts/screenshot.js +52 -0
- package/skills/web-browser/scripts/start.js +80 -0
- package/skills/web-browser/scripts/watch.js +266 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { SENTRY_API_BASE, getAuthToken, fetchJson, formatTimestamp, resolveProjectId } from "../lib/auth.js";
|
|
4
|
+
|
|
5
|
+
const HELP = `Usage: list-issues.js [options]
|
|
6
|
+
|
|
7
|
+
List and search issues in Sentry.
|
|
8
|
+
|
|
9
|
+
Options:
|
|
10
|
+
--org, -o <org> Organization slug (required)
|
|
11
|
+
--project, -p <project> Project slug (can be repeated for multiple projects)
|
|
12
|
+
--query, -q <query> Search query (Sentry issue search syntax)
|
|
13
|
+
--status <status> Filter by status (unresolved, resolved, ignored)
|
|
14
|
+
--level <level> Filter by level (error, warning, info, fatal)
|
|
15
|
+
--period, -t <period> Time period (default: 14d, e.g., 24h, 7d, 14d)
|
|
16
|
+
--limit, -n <n> Max results (default: 25)
|
|
17
|
+
--sort <sort> Sort order (date, new, priority, freq, user)
|
|
18
|
+
--json Output raw JSON
|
|
19
|
+
-h, --help Show this help
|
|
20
|
+
|
|
21
|
+
Query Syntax:
|
|
22
|
+
is:unresolved Unresolved issues only
|
|
23
|
+
is:resolved Resolved issues only
|
|
24
|
+
is:ignored Ignored issues only
|
|
25
|
+
is:assigned Assigned to someone
|
|
26
|
+
is:unassigned Not assigned
|
|
27
|
+
assigned:me Assigned to current user
|
|
28
|
+
level:error Error level only
|
|
29
|
+
firstSeen:+7d First seen more than 7 days ago
|
|
30
|
+
lastSeen:-24h Last seen within 24 hours
|
|
31
|
+
event.timestamp:>=2025-12-23 Events since date
|
|
32
|
+
times_seen:>100 Seen more than 100 times
|
|
33
|
+
user.email:*@example.com User email pattern
|
|
34
|
+
has:user Has user context
|
|
35
|
+
error.handled:0 Unhandled errors
|
|
36
|
+
|
|
37
|
+
Sort Options:
|
|
38
|
+
date - Last seen (default)
|
|
39
|
+
new - First seen (newest first)
|
|
40
|
+
priority - Priority score
|
|
41
|
+
freq - Frequency (events per time)
|
|
42
|
+
user - Users affected
|
|
43
|
+
|
|
44
|
+
Examples:
|
|
45
|
+
# List unresolved errors
|
|
46
|
+
list-issues.js --org myorg --project backend --status unresolved
|
|
47
|
+
|
|
48
|
+
# Find recent high-priority issues
|
|
49
|
+
list-issues.js --org myorg --query "is:unresolved lastSeen:-24h" --sort priority
|
|
50
|
+
|
|
51
|
+
# Search for specific error type
|
|
52
|
+
list-issues.js --org myorg --query "AI_NoOutputGeneratedError"
|
|
53
|
+
|
|
54
|
+
# Find issues first seen in a date range
|
|
55
|
+
list-issues.js --org myorg --query "firstSeen:>=2025-12-23 firstSeen:<=2025-12-24"
|
|
56
|
+
|
|
57
|
+
# Find issues with many events
|
|
58
|
+
list-issues.js --org myorg --query "times_seen:>50" --sort freq
|
|
59
|
+
`;
|
|
60
|
+
|
|
61
|
+
function parseArgs(args) {
|
|
62
|
+
const options = {
|
|
63
|
+
org: null,
|
|
64
|
+
projects: [],
|
|
65
|
+
query: null,
|
|
66
|
+
status: null,
|
|
67
|
+
level: null,
|
|
68
|
+
period: "14d",
|
|
69
|
+
limit: 25,
|
|
70
|
+
sort: null,
|
|
71
|
+
json: false,
|
|
72
|
+
help: false,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
for (let i = 0; i < args.length; i++) {
|
|
76
|
+
const arg = args[i];
|
|
77
|
+
|
|
78
|
+
switch (arg) {
|
|
79
|
+
case "--help":
|
|
80
|
+
case "-h":
|
|
81
|
+
options.help = true;
|
|
82
|
+
break;
|
|
83
|
+
case "--json":
|
|
84
|
+
options.json = true;
|
|
85
|
+
break;
|
|
86
|
+
case "--org":
|
|
87
|
+
case "-o":
|
|
88
|
+
options.org = args[++i];
|
|
89
|
+
break;
|
|
90
|
+
case "--project":
|
|
91
|
+
case "-p":
|
|
92
|
+
options.projects.push(args[++i]);
|
|
93
|
+
break;
|
|
94
|
+
case "--query":
|
|
95
|
+
case "-q":
|
|
96
|
+
options.query = args[++i];
|
|
97
|
+
break;
|
|
98
|
+
case "--status":
|
|
99
|
+
options.status = args[++i];
|
|
100
|
+
break;
|
|
101
|
+
case "--level":
|
|
102
|
+
options.level = args[++i];
|
|
103
|
+
break;
|
|
104
|
+
case "--period":
|
|
105
|
+
case "-t":
|
|
106
|
+
options.period = args[++i];
|
|
107
|
+
break;
|
|
108
|
+
case "--limit":
|
|
109
|
+
case "-n":
|
|
110
|
+
options.limit = parseInt(args[++i], 10);
|
|
111
|
+
break;
|
|
112
|
+
case "--sort":
|
|
113
|
+
options.sort = args[++i];
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return options;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function formatIssue(issue) {
|
|
122
|
+
const lines = [];
|
|
123
|
+
|
|
124
|
+
const id = issue.shortId || issue.id;
|
|
125
|
+
const title = issue.title || "(no title)";
|
|
126
|
+
const level = issue.level || "?";
|
|
127
|
+
const status = issue.status || "?";
|
|
128
|
+
const count = issue.count || 0;
|
|
129
|
+
const userCount = issue.userCount || 0;
|
|
130
|
+
const firstSeen = formatTimestamp(issue.firstSeen);
|
|
131
|
+
const lastSeen = formatTimestamp(issue.lastSeen);
|
|
132
|
+
const project = issue.project?.slug || "?";
|
|
133
|
+
const culprit = issue.culprit || "";
|
|
134
|
+
const permalink = issue.permalink || "";
|
|
135
|
+
|
|
136
|
+
lines.push(`[${id}] ${title}`);
|
|
137
|
+
lines.push(` level: ${level} | status: ${status} | project: ${project}`);
|
|
138
|
+
lines.push(` events: ${count} | users: ${userCount}`);
|
|
139
|
+
lines.push(` first: ${firstSeen} | last: ${lastSeen}`);
|
|
140
|
+
|
|
141
|
+
if (culprit) {
|
|
142
|
+
lines.push(` culprit: ${culprit}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (permalink) {
|
|
146
|
+
lines.push(` url: ${permalink}`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return lines.join("\n");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function formatOutput(issues) {
|
|
153
|
+
if (!issues || issues.length === 0) {
|
|
154
|
+
return "No issues found matching your query.";
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const lines = [];
|
|
158
|
+
lines.push(`Found ${issues.length} issues:\n`);
|
|
159
|
+
|
|
160
|
+
for (const issue of issues) {
|
|
161
|
+
lines.push(formatIssue(issue));
|
|
162
|
+
lines.push("");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return lines.join("\n").trimEnd();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function main() {
|
|
169
|
+
const args = process.argv.slice(2);
|
|
170
|
+
const options = parseArgs(args);
|
|
171
|
+
|
|
172
|
+
if (options.help) {
|
|
173
|
+
console.log(HELP);
|
|
174
|
+
process.exit(0);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (!options.org) {
|
|
178
|
+
console.error("Error: --org is required");
|
|
179
|
+
console.error("Run with --help for usage information");
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const token = getAuthToken();
|
|
184
|
+
|
|
185
|
+
// Build query parameters
|
|
186
|
+
const params = new URLSearchParams();
|
|
187
|
+
|
|
188
|
+
if (options.period) {
|
|
189
|
+
params.set("statsPeriod", options.period);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
params.set("limit", Math.min(options.limit, 100).toString());
|
|
193
|
+
|
|
194
|
+
// Build search query
|
|
195
|
+
const queryParts = [];
|
|
196
|
+
|
|
197
|
+
if (options.query) {
|
|
198
|
+
queryParts.push(options.query);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (options.status) {
|
|
202
|
+
queryParts.push(`is:${options.status}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (options.level) {
|
|
206
|
+
queryParts.push(`level:${options.level}`);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (queryParts.length > 0) {
|
|
210
|
+
params.set("query", queryParts.join(" "));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (options.sort) {
|
|
214
|
+
params.set("sort", options.sort);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Build URL - always use org endpoint with resolved project IDs
|
|
218
|
+
// This handles both slugs and numeric IDs uniformly
|
|
219
|
+
for (const project of options.projects) {
|
|
220
|
+
const projectId = await resolveProjectId(options.org, project, token);
|
|
221
|
+
params.append("project", projectId);
|
|
222
|
+
}
|
|
223
|
+
const url = `${SENTRY_API_BASE}/organizations/${encodeURIComponent(options.org)}/issues/?${params.toString()}`;
|
|
224
|
+
|
|
225
|
+
try {
|
|
226
|
+
const data = await fetchJson(url, token);
|
|
227
|
+
|
|
228
|
+
if (options.json) {
|
|
229
|
+
console.log(JSON.stringify(data, null, 2));
|
|
230
|
+
} else {
|
|
231
|
+
console.log(formatOutput(data));
|
|
232
|
+
}
|
|
233
|
+
} catch (err) {
|
|
234
|
+
console.error("Error:", err.message);
|
|
235
|
+
process.exit(1);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
main();
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { SENTRY_API_BASE, getAuthToken, fetchJson, formatTimestamp, resolveProjectId } from "../lib/auth.js";
|
|
4
|
+
|
|
5
|
+
const HELP = `Usage: search-events.js [options]
|
|
6
|
+
|
|
7
|
+
Search for events (transactions, errors) in Sentry Discover.
|
|
8
|
+
|
|
9
|
+
Options:
|
|
10
|
+
--org, -o <org> Organization slug (required)
|
|
11
|
+
--project, -p <project> Project slug or ID
|
|
12
|
+
--query, -q <query> Search query (Discover syntax)
|
|
13
|
+
--period, -t <period> Time period (default: 24h, e.g., 1h, 7d, 14d)
|
|
14
|
+
--start <datetime> Start time (ISO 8601, e.g., 2025-12-23T15:00:00)
|
|
15
|
+
--end <datetime> End time (ISO 8601)
|
|
16
|
+
--transaction <name> Filter by transaction name
|
|
17
|
+
--tag <key:value> Filter by tag (can be repeated)
|
|
18
|
+
--level <level> Filter by level (error, warning, info)
|
|
19
|
+
--limit, -n <n> Max results (default: 25, max: 100)
|
|
20
|
+
--fields <fields> Comma-separated fields to include
|
|
21
|
+
--json Output raw JSON
|
|
22
|
+
-h, --help Show this help
|
|
23
|
+
|
|
24
|
+
Common Fields:
|
|
25
|
+
id, title, timestamp, transaction, message, level, environment,
|
|
26
|
+
user.email, user.id, tags[key], http.method, http.url
|
|
27
|
+
|
|
28
|
+
Query Syntax (Discover):
|
|
29
|
+
transaction:process-* Match transaction names with wildcards
|
|
30
|
+
level:error Filter by log level
|
|
31
|
+
user.email:foo@bar.com Filter by user email
|
|
32
|
+
environment:production Filter by environment
|
|
33
|
+
has:stack.filename Events with stack traces
|
|
34
|
+
!has:user Events without user
|
|
35
|
+
|
|
36
|
+
Date Range Examples:
|
|
37
|
+
--period 7d Last 7 days
|
|
38
|
+
--start 2025-12-23T15:00:00 From specific time to now
|
|
39
|
+
--start 2025-12-23T15:00:00 --end 2025-12-23T18:00:00 Specific range
|
|
40
|
+
|
|
41
|
+
Examples:
|
|
42
|
+
# Find all transactions for a transaction name
|
|
43
|
+
search-events.js --org myorg --project backend --transaction process-incoming-email
|
|
44
|
+
|
|
45
|
+
# Find errors in the last 7 days
|
|
46
|
+
search-events.js --org myorg --query "level:error" --period 7d
|
|
47
|
+
|
|
48
|
+
# Find events around a specific time
|
|
49
|
+
search-events.js --org myorg --start 2025-12-23T15:00:00 --end 2025-12-23T17:00:00
|
|
50
|
+
|
|
51
|
+
# Search with a specific tag
|
|
52
|
+
search-events.js --org myorg --tag thread_id:th_abc123
|
|
53
|
+
|
|
54
|
+
# Get more fields
|
|
55
|
+
search-events.js --org myorg --fields "id,title,timestamp,user.email"
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
function parseArgs(args) {
|
|
59
|
+
const options = {
|
|
60
|
+
org: null,
|
|
61
|
+
project: null,
|
|
62
|
+
query: null,
|
|
63
|
+
period: null,
|
|
64
|
+
start: null,
|
|
65
|
+
end: null,
|
|
66
|
+
transaction: null,
|
|
67
|
+
tags: [],
|
|
68
|
+
level: null,
|
|
69
|
+
limit: 25,
|
|
70
|
+
fields: ["id", "title", "timestamp", "transaction", "message"],
|
|
71
|
+
json: false,
|
|
72
|
+
help: false,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
for (let i = 0; i < args.length; i++) {
|
|
76
|
+
const arg = args[i];
|
|
77
|
+
|
|
78
|
+
switch (arg) {
|
|
79
|
+
case "--help":
|
|
80
|
+
case "-h":
|
|
81
|
+
options.help = true;
|
|
82
|
+
break;
|
|
83
|
+
case "--json":
|
|
84
|
+
options.json = true;
|
|
85
|
+
break;
|
|
86
|
+
case "--org":
|
|
87
|
+
case "-o":
|
|
88
|
+
options.org = args[++i];
|
|
89
|
+
break;
|
|
90
|
+
case "--project":
|
|
91
|
+
case "-p":
|
|
92
|
+
options.project = args[++i];
|
|
93
|
+
break;
|
|
94
|
+
case "--query":
|
|
95
|
+
case "-q":
|
|
96
|
+
options.query = args[++i];
|
|
97
|
+
break;
|
|
98
|
+
case "--period":
|
|
99
|
+
case "-t":
|
|
100
|
+
options.period = args[++i];
|
|
101
|
+
break;
|
|
102
|
+
case "--start":
|
|
103
|
+
options.start = args[++i];
|
|
104
|
+
break;
|
|
105
|
+
case "--end":
|
|
106
|
+
options.end = args[++i];
|
|
107
|
+
break;
|
|
108
|
+
case "--transaction":
|
|
109
|
+
options.transaction = args[++i];
|
|
110
|
+
break;
|
|
111
|
+
case "--tag":
|
|
112
|
+
options.tags.push(args[++i]);
|
|
113
|
+
break;
|
|
114
|
+
case "--level":
|
|
115
|
+
options.level = args[++i];
|
|
116
|
+
break;
|
|
117
|
+
case "--limit":
|
|
118
|
+
case "-n":
|
|
119
|
+
options.limit = parseInt(args[++i], 10);
|
|
120
|
+
break;
|
|
121
|
+
case "--fields":
|
|
122
|
+
options.fields = args[++i].split(",").map((f) => f.trim());
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Default to 24h if no time range specified
|
|
128
|
+
if (!options.period && !options.start) {
|
|
129
|
+
options.period = "24h";
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return options;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function formatEvent(event, fields) {
|
|
136
|
+
const lines = [];
|
|
137
|
+
|
|
138
|
+
const id = event.id || event["event.type"] || "?";
|
|
139
|
+
const ts = event.timestamp || "N/A";
|
|
140
|
+
const title = event.title || event.transaction || event.message || "(no title)";
|
|
141
|
+
const transaction = event.transaction || "";
|
|
142
|
+
|
|
143
|
+
// Format timestamp
|
|
144
|
+
let displayTs = ts;
|
|
145
|
+
try {
|
|
146
|
+
const date = new Date(ts);
|
|
147
|
+
if (!isNaN(date.getTime())) {
|
|
148
|
+
displayTs = date.toISOString().replace("T", " ").slice(0, 19);
|
|
149
|
+
}
|
|
150
|
+
} catch {}
|
|
151
|
+
|
|
152
|
+
lines.push(`[${displayTs}] ${title}`);
|
|
153
|
+
|
|
154
|
+
if (transaction && transaction !== title) {
|
|
155
|
+
lines.push(` transaction: ${transaction}`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (event.message && event.message !== title) {
|
|
159
|
+
lines.push(` message: ${event.message}`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Show any extra fields the user requested
|
|
163
|
+
for (const field of fields) {
|
|
164
|
+
if (["id", "title", "timestamp", "transaction", "message"].includes(field)) continue;
|
|
165
|
+
const value = event[field];
|
|
166
|
+
if (value !== undefined && value !== null && value !== "") {
|
|
167
|
+
lines.push(` ${field}: ${value}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
lines.push(` id: ${id}`);
|
|
172
|
+
|
|
173
|
+
return lines.join("\n");
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function formatOutput(data, fields) {
|
|
177
|
+
if (!data.data || data.data.length === 0) {
|
|
178
|
+
return "No events found matching your query.";
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const lines = [];
|
|
182
|
+
lines.push(`Found ${data.data.length} events:\n`);
|
|
183
|
+
|
|
184
|
+
for (const event of data.data) {
|
|
185
|
+
lines.push(formatEvent(event, fields));
|
|
186
|
+
lines.push("");
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return lines.join("\n").trimEnd();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async function main() {
|
|
193
|
+
const args = process.argv.slice(2);
|
|
194
|
+
const options = parseArgs(args);
|
|
195
|
+
|
|
196
|
+
if (options.help) {
|
|
197
|
+
console.log(HELP);
|
|
198
|
+
process.exit(0);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (!options.org) {
|
|
202
|
+
console.error("Error: --org is required");
|
|
203
|
+
console.error("Run with --help for usage information");
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const token = getAuthToken();
|
|
208
|
+
|
|
209
|
+
// Build query parameters
|
|
210
|
+
const params = new URLSearchParams();
|
|
211
|
+
params.set("dataset", "discover");
|
|
212
|
+
|
|
213
|
+
// Time range
|
|
214
|
+
if (options.start) {
|
|
215
|
+
params.set("start", options.start);
|
|
216
|
+
if (options.end) {
|
|
217
|
+
params.set("end", options.end);
|
|
218
|
+
} else {
|
|
219
|
+
// If only start, use current time as end
|
|
220
|
+
params.set("end", new Date().toISOString());
|
|
221
|
+
}
|
|
222
|
+
} else if (options.period) {
|
|
223
|
+
params.set("statsPeriod", options.period);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
params.set("per_page", Math.min(options.limit, 100).toString());
|
|
227
|
+
params.set("sort", "-timestamp");
|
|
228
|
+
|
|
229
|
+
// Add fields
|
|
230
|
+
for (const field of options.fields) {
|
|
231
|
+
params.append("field", field);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Always include project.name for context
|
|
235
|
+
if (!options.fields.includes("project.name")) {
|
|
236
|
+
params.append("field", "project.name");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Build search query
|
|
240
|
+
const queryParts = [];
|
|
241
|
+
|
|
242
|
+
if (options.project) {
|
|
243
|
+
const projectId = await resolveProjectId(options.org, options.project, token);
|
|
244
|
+
params.set("project", projectId);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (options.query) {
|
|
248
|
+
queryParts.push(options.query);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (options.transaction) {
|
|
252
|
+
queryParts.push(`transaction:${options.transaction}`);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (options.level) {
|
|
256
|
+
queryParts.push(`level:${options.level}`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
for (const tag of options.tags) {
|
|
260
|
+
// Handle tags[key]:value format
|
|
261
|
+
if (tag.includes(":")) {
|
|
262
|
+
const [key, value] = tag.split(":", 2);
|
|
263
|
+
if (key.startsWith("tags[")) {
|
|
264
|
+
queryParts.push(`${key}:${value}`);
|
|
265
|
+
} else {
|
|
266
|
+
queryParts.push(`tags[${key}]:${value}`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (queryParts.length > 0) {
|
|
272
|
+
params.set("query", queryParts.join(" "));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const url = `${SENTRY_API_BASE}/organizations/${encodeURIComponent(options.org)}/events/?${params.toString()}`;
|
|
276
|
+
|
|
277
|
+
try {
|
|
278
|
+
const data = await fetchJson(url, token);
|
|
279
|
+
|
|
280
|
+
if (options.json) {
|
|
281
|
+
console.log(JSON.stringify(data, null, 2));
|
|
282
|
+
} else {
|
|
283
|
+
console.log(formatOutput(data, options.fields));
|
|
284
|
+
}
|
|
285
|
+
} catch (err) {
|
|
286
|
+
console.error("Error:", err.message);
|
|
287
|
+
process.exit(1);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
main();
|