hivenet 0.4.2 → 0.6.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/README.md +55 -0
- package/dist/chunk-AK23JXBN.js +32 -0
- package/dist/{chunk-WWPF6V2Y.js → chunk-DNR6HNJD.js} +484 -70
- package/dist/cli.js +323 -66
- package/dist/{doctor-CVWF7J3P.js → doctor-EA2GJRUS.js} +1 -1
- package/dist/index.cjs +868 -460
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +384 -31
- package/dist/index.d.ts +384 -31
- package/dist/index.js +863 -457
- package/dist/index.js.map +1 -1
- package/dist/owner-ZWROA7AU.js +539 -0
- package/package.json +1 -1
- package/dist/owner-N2MSYLV7.js +0 -257
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
import {
|
|
2
|
+
attachmentFlags
|
|
3
|
+
} from "./chunk-AK23JXBN.js";
|
|
4
|
+
import {
|
|
5
|
+
ATTACHMENT_TARGET_TYPES,
|
|
6
|
+
EVAL_STATUSES,
|
|
7
|
+
EVENT_STATUSES,
|
|
8
|
+
FEEDBACK_CATEGORIES,
|
|
9
|
+
FeedbackError,
|
|
10
|
+
ISSUE_STATUSES,
|
|
11
|
+
__toESM,
|
|
12
|
+
attachmentTargetSchema,
|
|
13
|
+
ownerApi,
|
|
14
|
+
require_arg,
|
|
15
|
+
resolveApiOrigin
|
|
16
|
+
} from "./chunk-DNR6HNJD.js";
|
|
17
|
+
|
|
18
|
+
// src/owner.ts
|
|
19
|
+
var import_arg = __toESM(require_arg(), 1);
|
|
20
|
+
var SECRET_ENV_VAR = "HIVENET_SECRET_KEY";
|
|
21
|
+
async function runAttach(argv) {
|
|
22
|
+
const args = (0, import_arg.default)({ ...OWNER_FLAGS, ...attachmentFlags() }, { argv });
|
|
23
|
+
if (args["--help"]) {
|
|
24
|
+
console.log(`
|
|
25
|
+
Usage
|
|
26
|
+
$ npx hivenet attach <record type> <id> --attach <path> [--describe "<what it shows>"] \u2026
|
|
27
|
+
|
|
28
|
+
Record types: ${ATTACHMENT_TARGET_TYPES.join(" | ")}.
|
|
29
|
+
Up to 10 files per record, 20 MiB each. Files on a question are shared with every agent
|
|
30
|
+
that receives it; everything else stays private. Auth: --secret or ${SECRET_ENV_VAR}.
|
|
31
|
+
`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const target = attachmentTargetSchema.safeParse({ type: args._[0], id: args._[1] });
|
|
35
|
+
if (!target.success || args._.length !== 2 || !args["--attach"]?.length) {
|
|
36
|
+
throw new FeedbackError(
|
|
37
|
+
"Usage: hivenet attach <record type> <id> --attach <path>. See hivenet attach --help."
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
const api = await buildApi(args);
|
|
41
|
+
const result = await api.attachFiles(target.data, args["--attach"]);
|
|
42
|
+
console.log(
|
|
43
|
+
args["--json"] ? JSON.stringify(result, null, 2) : `\u2713 Attached ${args["--attach"].length} file(s) to ${target.data.type} ${target.data.id}.`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
var OWNER_FLAGS = {
|
|
47
|
+
"--to": String,
|
|
48
|
+
"--dsn": String,
|
|
49
|
+
"--api": String,
|
|
50
|
+
"--secret": String,
|
|
51
|
+
"--json": Boolean,
|
|
52
|
+
"--help": Boolean,
|
|
53
|
+
"-h": "--help"
|
|
54
|
+
};
|
|
55
|
+
async function buildApi(args) {
|
|
56
|
+
const secretKey = args["--secret"] ?? process.env[SECRET_ENV_VAR];
|
|
57
|
+
if (!secretKey?.startsWith("hv_sec_")) {
|
|
58
|
+
throw new FeedbackError(
|
|
59
|
+
`An owner secret key is required (hv_sec_\u2026, from your project's Settings).
|
|
60
|
+
Pass --secret <key> or set ${SECRET_ENV_VAR}.`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
const apiOrigin = args["--api"] ?? await resolveApiOrigin({
|
|
64
|
+
...args["--to"] !== void 0 ? { to: args["--to"] } : {},
|
|
65
|
+
...args["--dsn"] !== void 0 ? { dsn: args["--dsn"] } : {}
|
|
66
|
+
});
|
|
67
|
+
return ownerApi({ secretKey, apiOrigin });
|
|
68
|
+
}
|
|
69
|
+
function statusIcon(status) {
|
|
70
|
+
return { new: "\u25CF", seen: "\u25D0", in_progress: "\u25D2", resolved: "\u2713", wont_fix: "\u2717", spam: "\u2298" }[status];
|
|
71
|
+
}
|
|
72
|
+
function printEvent(event, verbose = false) {
|
|
73
|
+
const subject = event.subject ? ` \xB7 ${event.subject}` : "";
|
|
74
|
+
console.log(
|
|
75
|
+
`${statusIcon(event.status)} ${event.id} [${event.status}] ${event.category}${subject} (${event.agent ?? event.author ?? "unknown"}, ${event.received_at.slice(0, 16).replace("T", " ")})`
|
|
76
|
+
);
|
|
77
|
+
console.log(` ${event.feedback}`);
|
|
78
|
+
if (event.triage_note) {
|
|
79
|
+
console.log(` \u21B3 note: ${event.triage_note}`);
|
|
80
|
+
}
|
|
81
|
+
if (verbose) {
|
|
82
|
+
console.log(
|
|
83
|
+
` thread ${event.thread_id} \xB7 telemetry ${event.consent_telemetry ? "on" : "off"}`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function runInbox(argv) {
|
|
88
|
+
const args = (0, import_arg.default)(
|
|
89
|
+
{
|
|
90
|
+
...OWNER_FLAGS,
|
|
91
|
+
"--status": String,
|
|
92
|
+
"--category": String,
|
|
93
|
+
"--thread": String,
|
|
94
|
+
"--limit": Number
|
|
95
|
+
},
|
|
96
|
+
{ argv }
|
|
97
|
+
);
|
|
98
|
+
if (args["--help"]) {
|
|
99
|
+
console.log(`
|
|
100
|
+
Usage
|
|
101
|
+
$ npx hivenet inbox [--status new] [--category mcp] [--thread <id>] [--limit 50]
|
|
102
|
+
|
|
103
|
+
List feedback for your project (owners only). Auth: --secret <hv_sec_\u2026> or
|
|
104
|
+
${SECRET_ENV_VAR}. Destination: --to <slug|domain>, --dsn, or --api <origin>
|
|
105
|
+
(defaults to the hosted API). Statuses: ${EVENT_STATUSES.join(" | ")}. --json for raw output.
|
|
106
|
+
`);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const status = args["--status"];
|
|
110
|
+
if (status && !EVENT_STATUSES.includes(status)) {
|
|
111
|
+
throw new FeedbackError(
|
|
112
|
+
`Invalid --status "${status}". Expected: ${EVENT_STATUSES.join(", ")}.`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
const api = await buildApi(args);
|
|
116
|
+
const result = await api.listFeedback({
|
|
117
|
+
...status ? { status } : {},
|
|
118
|
+
...args["--category"] ? { category: args["--category"] } : {},
|
|
119
|
+
...args["--thread"] ? { thread: args["--thread"] } : {},
|
|
120
|
+
...args["--limit"] ? { limit: args["--limit"] } : {}
|
|
121
|
+
});
|
|
122
|
+
if (args["--json"]) {
|
|
123
|
+
console.log(JSON.stringify(result, null, 2));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (result.events.length === 0) {
|
|
127
|
+
console.log(`No matching feedback for ${result.project}.`);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
console.log(`${result.events.length} event(s) for ${result.project}:
|
|
131
|
+
`);
|
|
132
|
+
for (const event of result.events) {
|
|
133
|
+
printEvent(event);
|
|
134
|
+
}
|
|
135
|
+
console.log(
|
|
136
|
+
`
|
|
137
|
+
Triage: npx hivenet triage <eventId> --status <${EVENT_STATUSES.join("|")}> [--note "\u2026"]`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
async function runTriage(argv) {
|
|
141
|
+
const args = (0, import_arg.default)(
|
|
142
|
+
{ ...OWNER_FLAGS, "--status": String, "--note": String, "--link": String },
|
|
143
|
+
{ argv }
|
|
144
|
+
);
|
|
145
|
+
if (args["--help"] || args._.length === 0) {
|
|
146
|
+
console.log(`
|
|
147
|
+
Usage
|
|
148
|
+
$ npx hivenet triage <eventId> [--status <status>] [--note "why"] [--link <url>]
|
|
149
|
+
|
|
150
|
+
Set triage status and/or attach an external link (Slack, Linear, GitHub, PR) to a
|
|
151
|
+
feedback event (owners only). Statuses: ${EVENT_STATUSES.join(" | ")}.
|
|
152
|
+
Auth: --secret <hv_sec_\u2026> or ${SECRET_ENV_VAR}. Destination: --to/--dsn/--api.
|
|
153
|
+
`);
|
|
154
|
+
if (!args["--help"]) {
|
|
155
|
+
throw new FeedbackError("An event ID is required.");
|
|
156
|
+
}
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const status = args["--status"];
|
|
160
|
+
if (status && !EVENT_STATUSES.includes(status)) {
|
|
161
|
+
throw new FeedbackError(`Invalid --status. Expected: ${EVENT_STATUSES.join(", ")}.`);
|
|
162
|
+
}
|
|
163
|
+
if (!status && !args["--link"]) {
|
|
164
|
+
throw new FeedbackError("Provide --status and/or --link.");
|
|
165
|
+
}
|
|
166
|
+
const api = await buildApi(args);
|
|
167
|
+
const id = args._[0];
|
|
168
|
+
if (args["--link"]) {
|
|
169
|
+
await api.addLink(id, args["--link"]);
|
|
170
|
+
}
|
|
171
|
+
const event = status ? (await api.setFeedbackStatus(
|
|
172
|
+
id,
|
|
173
|
+
status,
|
|
174
|
+
args["--note"] !== void 0 ? args["--note"] : void 0
|
|
175
|
+
)).event : (await api.getFeedback(id)).event;
|
|
176
|
+
if (args["--json"]) {
|
|
177
|
+
console.log(JSON.stringify({ event }, null, 2));
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
console.log("Updated:");
|
|
181
|
+
printEvent(event, true);
|
|
182
|
+
printLinks(event.links_json);
|
|
183
|
+
}
|
|
184
|
+
function printLinks(linksJson) {
|
|
185
|
+
try {
|
|
186
|
+
const links = JSON.parse(linksJson ?? "[]");
|
|
187
|
+
for (const link of links) {
|
|
188
|
+
console.log(` \u21C4 ${link.kind}: ${link.url}`);
|
|
189
|
+
}
|
|
190
|
+
} catch {
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function issueIcon(status) {
|
|
194
|
+
return {
|
|
195
|
+
open: "\u25CF",
|
|
196
|
+
in_progress: "\u25D2",
|
|
197
|
+
needs_review: "\u25CE",
|
|
198
|
+
resolved: "\u2713",
|
|
199
|
+
wont_fix: "\u2717",
|
|
200
|
+
spam: "\u2298"
|
|
201
|
+
}[status];
|
|
202
|
+
}
|
|
203
|
+
function printIssue(issue) {
|
|
204
|
+
const regressed = issue.reopened_count > 0 ? ` \u21BB regressed \xD7${issue.reopened_count}` : "";
|
|
205
|
+
console.log(
|
|
206
|
+
`${issueIcon(issue.status)} ${issue.id} [${issue.status}] ${issue.title} (${issue.event_count} event${issue.event_count === 1 ? "" : "s"}, last ${issue.last_seen.slice(0, 16).replace("T", " ")})${regressed}`
|
|
207
|
+
);
|
|
208
|
+
printLinks(issue.links_json);
|
|
209
|
+
}
|
|
210
|
+
async function runIssues(argv) {
|
|
211
|
+
const args = (0, import_arg.default)({ ...OWNER_FLAGS, "--status": String, "--limit": Number }, { argv });
|
|
212
|
+
if (args["--help"]) {
|
|
213
|
+
console.log(`
|
|
214
|
+
Usage
|
|
215
|
+
$ npx hivenet issues [--status open] [--limit 50]
|
|
216
|
+
|
|
217
|
+
List issues \u2014 feedback clustered by thread and (category, subject) fingerprint.
|
|
218
|
+
Statuses: ${ISSUE_STATUSES.join(" | ")}. Auth: --secret or ${SECRET_ENV_VAR}.
|
|
219
|
+
Manage one: npx hivenet issue <issueId> --status resolved --note "\u2026" --link <url>
|
|
220
|
+
`);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const status = args["--status"];
|
|
224
|
+
if (status && !ISSUE_STATUSES.includes(status)) {
|
|
225
|
+
throw new FeedbackError(`Invalid --status. Expected: ${ISSUE_STATUSES.join(", ")}.`);
|
|
226
|
+
}
|
|
227
|
+
const api = await buildApi(args);
|
|
228
|
+
const result = await api.listIssues({
|
|
229
|
+
...status ? { status } : {},
|
|
230
|
+
...args["--limit"] ? { limit: args["--limit"] } : {}
|
|
231
|
+
});
|
|
232
|
+
if (args["--json"]) {
|
|
233
|
+
console.log(JSON.stringify(result, null, 2));
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (result.issues.length === 0) {
|
|
237
|
+
console.log(`No matching issues for ${result.project}.`);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
console.log(`${result.issues.length} issue(s) for ${result.project}:
|
|
241
|
+
`);
|
|
242
|
+
for (const issue of result.issues) {
|
|
243
|
+
printIssue(issue);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
async function runIssue(argv) {
|
|
247
|
+
const args = (0, import_arg.default)(
|
|
248
|
+
{ ...OWNER_FLAGS, "--status": String, "--note": String, "--link": String },
|
|
249
|
+
{ argv }
|
|
250
|
+
);
|
|
251
|
+
if (args["--help"] || args._.length === 0) {
|
|
252
|
+
console.log(`
|
|
253
|
+
Usage
|
|
254
|
+
$ npx hivenet issue <issueId> (show with events)
|
|
255
|
+
$ npx hivenet issue <issueId> --status resolved --note "\u2026" --link <url>
|
|
256
|
+
|
|
257
|
+
Statuses: ${ISSUE_STATUSES.join(" | ")}. Resolving cascades to member events; new
|
|
258
|
+
feedback on a resolved issue reopens it. Auth: --secret or ${SECRET_ENV_VAR}.
|
|
259
|
+
`);
|
|
260
|
+
if (!args["--help"]) {
|
|
261
|
+
throw new FeedbackError("An issue ID is required.");
|
|
262
|
+
}
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
const status = args["--status"];
|
|
266
|
+
if (status && !ISSUE_STATUSES.includes(status)) {
|
|
267
|
+
throw new FeedbackError(`Invalid --status. Expected: ${ISSUE_STATUSES.join(", ")}.`);
|
|
268
|
+
}
|
|
269
|
+
if (args["--note"] !== void 0 && !status) {
|
|
270
|
+
throw new FeedbackError("--note requires --status.");
|
|
271
|
+
}
|
|
272
|
+
const api = await buildApi(args);
|
|
273
|
+
const id = args._[0];
|
|
274
|
+
if (args["--link"]) {
|
|
275
|
+
await api.addLink(id, args["--link"]);
|
|
276
|
+
}
|
|
277
|
+
if (status) {
|
|
278
|
+
await api.setIssueStatus(id, status, args["--note"] !== void 0 ? args["--note"] : void 0);
|
|
279
|
+
}
|
|
280
|
+
const { issue, events } = await api.getIssue(id);
|
|
281
|
+
if (args["--json"]) {
|
|
282
|
+
console.log(JSON.stringify({ issue, events }, null, 2));
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
printIssue(issue);
|
|
286
|
+
if (issue.summary) {
|
|
287
|
+
console.log(` ${issue.summary}`);
|
|
288
|
+
}
|
|
289
|
+
console.log("");
|
|
290
|
+
for (const event of events) {
|
|
291
|
+
printEvent(event);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function printQuestion(question) {
|
|
295
|
+
const icon = question.status === "active" ? "\u25CF" : "\u2713";
|
|
296
|
+
const auto = question.source === "system" ? " [auto]" : "";
|
|
297
|
+
const target = question.target.category ? ` \xB7 ${question.target.category}` : question.target.issue_id ? ` \xB7 verifies ${question.target.issue_id.slice(0, 14)}\u2026` : "";
|
|
298
|
+
console.log(`${icon} ${question.id} [${question.status}]${auto}${target}`);
|
|
299
|
+
console.log(` ${question.prompt}`);
|
|
300
|
+
for (const answer of question.answers) {
|
|
301
|
+
const who = answer.agent ?? answer.client_name ?? "unknown";
|
|
302
|
+
console.log(
|
|
303
|
+
` \u21B3 (${who}, ${answer.received_at.slice(0, 16).replace("T", " ")}) ${answer.feedback}`
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
async function runQuestions(argv) {
|
|
308
|
+
const args = (0, import_arg.default)({ ...OWNER_FLAGS, "--status": String }, { argv });
|
|
309
|
+
if (args["--help"]) {
|
|
310
|
+
console.log(`
|
|
311
|
+
Usage
|
|
312
|
+
$ npx hivenet questions [--status active|closed] (list with answers)
|
|
313
|
+
$ npx hivenet ask "<prompt>" [--category cli] (publish a question)
|
|
314
|
+
$ npx hivenet question <id> --close (stop delivery)
|
|
315
|
+
|
|
316
|
+
Questions ride along with submission responses \u2014 one per response, delivered
|
|
317
|
+
to matching agents; answers group under each question. Auth: --secret or
|
|
318
|
+
${SECRET_ENV_VAR}.
|
|
319
|
+
|
|
320
|
+
Show agents what you mean: --attach <path> [--describe "<what it shows>"] on ask
|
|
321
|
+
(repeatable; 10 files, 20 MiB each). Every agent the question reaches can open them.
|
|
322
|
+
`);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
const status = args["--status"];
|
|
326
|
+
if (status && status !== "active" && status !== "closed") {
|
|
327
|
+
throw new FeedbackError("Invalid --status. Expected: active or closed.");
|
|
328
|
+
}
|
|
329
|
+
const api = await buildApi(args);
|
|
330
|
+
const result = await api.listQuestions(status ? { status } : {});
|
|
331
|
+
if (args["--json"]) {
|
|
332
|
+
console.log(JSON.stringify(result, null, 2));
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (result.questions.length === 0) {
|
|
336
|
+
console.log('No questions yet. Publish one: npx hivenet ask "<prompt>" [--category cli]');
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
console.log(`${result.questions.length} question(s):
|
|
340
|
+
`);
|
|
341
|
+
for (const question of result.questions) {
|
|
342
|
+
printQuestion(question);
|
|
343
|
+
console.log("");
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
async function runAsk(argv) {
|
|
347
|
+
const args = (0, import_arg.default)(
|
|
348
|
+
{ ...OWNER_FLAGS, "--category": String, "--idempotency-key": String, ...attachmentFlags() },
|
|
349
|
+
{ argv }
|
|
350
|
+
);
|
|
351
|
+
const prompt = args._.join(" ").trim();
|
|
352
|
+
if (args["--help"] || prompt.length === 0) {
|
|
353
|
+
console.log(`
|
|
354
|
+
Usage
|
|
355
|
+
$ npx hivenet ask "If you used <feature>: what broke or was missing?" [--category cli]
|
|
356
|
+
|
|
357
|
+
Publishes a question to the agents using your product. Targeted beats
|
|
358
|
+
open-ended. Categories: ${FEEDBACK_CATEGORIES.join(" | ")}. Auth: --secret or ${SECRET_ENV_VAR}.
|
|
359
|
+
--attach <path> [--describe "<what it shows>"] shares a file with every agent that receives it.
|
|
360
|
+
--idempotency-key <key> publishes the question once however often you retry (8\u201364 letters,
|
|
361
|
+
digits, _ or -).
|
|
362
|
+
`);
|
|
363
|
+
if (!args["--help"]) {
|
|
364
|
+
throw new FeedbackError("A question prompt is required.");
|
|
365
|
+
}
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const api = await buildApi(args);
|
|
369
|
+
const { question } = await api.createQuestion(prompt, args["--category"], {
|
|
370
|
+
...args["--attach"] ? { files: args["--attach"] } : {},
|
|
371
|
+
...args["--idempotency-key"] ? { idempotencyKey: args["--idempotency-key"] } : {}
|
|
372
|
+
});
|
|
373
|
+
if (args["--json"]) {
|
|
374
|
+
console.log(JSON.stringify({ question }, null, 2));
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
console.log(`\u2713 Question published (${question.id})`);
|
|
378
|
+
console.log(` It rides along with matching submission responses \u2014 one ask per response.`);
|
|
379
|
+
console.log(
|
|
380
|
+
` Answers: npx hivenet questions \xB7 close: npx hivenet question ${question.id} --close`
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
async function runQuestion(argv) {
|
|
384
|
+
const args = (0, import_arg.default)({ ...OWNER_FLAGS, "--close": Boolean }, { argv });
|
|
385
|
+
if (args["--help"] || args._.length === 0) {
|
|
386
|
+
console.log(`
|
|
387
|
+
Usage
|
|
388
|
+
$ npx hivenet question <questionId> (show with answers)
|
|
389
|
+
$ npx hivenet question <questionId> --close (stop delivery)
|
|
390
|
+
`);
|
|
391
|
+
if (!args["--help"]) {
|
|
392
|
+
throw new FeedbackError("A question ID is required.");
|
|
393
|
+
}
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
const api = await buildApi(args);
|
|
397
|
+
const id = args._[0];
|
|
398
|
+
if (args["--close"]) {
|
|
399
|
+
await api.closeQuestion(id);
|
|
400
|
+
}
|
|
401
|
+
const { questions } = await api.listQuestions();
|
|
402
|
+
const question = questions.find((candidate) => candidate.id === id);
|
|
403
|
+
if (!question) {
|
|
404
|
+
throw new FeedbackError(`Question "${id}" not found.`);
|
|
405
|
+
}
|
|
406
|
+
if (args["--json"]) {
|
|
407
|
+
console.log(JSON.stringify({ question }, null, 2));
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
printQuestion(question);
|
|
411
|
+
}
|
|
412
|
+
function evalIcon(status) {
|
|
413
|
+
return { proposed: "\u25C7", accepted: "\u25C6", archived: "\u229F" }[status];
|
|
414
|
+
}
|
|
415
|
+
function printEval(entry) {
|
|
416
|
+
console.log(`${evalIcon(entry.status)} ${entry.id} [${entry.status}] ${entry.title}`);
|
|
417
|
+
console.log(` task: ${entry.task}`);
|
|
418
|
+
if (entry.expected) console.log(` expected: ${entry.expected}`);
|
|
419
|
+
if (entry.setup) {
|
|
420
|
+
console.log(` setup: ${entry.setup}`);
|
|
421
|
+
} else {
|
|
422
|
+
console.log(
|
|
423
|
+
` setup: missing${entry.setup_pending ? " \u2014 reporter asked on their thread" : ""}`
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
if (entry.source) {
|
|
427
|
+
const who = entry.source.agent ?? entry.source.client_name ?? "unknown";
|
|
428
|
+
const attempts = entry.source.attempts ? ` \xB7 ${entry.source.attempts} attempts` : "";
|
|
429
|
+
console.log(` from ${entry.source.event_id} (${who}${attempts})`);
|
|
430
|
+
}
|
|
431
|
+
if (entry.note) console.log(` \u21B3 note: ${entry.note}`);
|
|
432
|
+
}
|
|
433
|
+
async function runEvals(argv) {
|
|
434
|
+
const args = (0, import_arg.default)({ ...OWNER_FLAGS, "--status": String }, { argv });
|
|
435
|
+
if (args["--help"]) {
|
|
436
|
+
console.log(`
|
|
437
|
+
Usage
|
|
438
|
+
$ npx hivenet evals [--status proposed|accepted|archived] (list the corpus)
|
|
439
|
+
$ npx hivenet eval <id> [--status accepted] [--note "\u2026"] (curate one)
|
|
440
|
+
$ npx hivenet promote <eventId> (report \u2192 eval)
|
|
441
|
+
|
|
442
|
+
Evals are real tasks agents failed at, promoted from failed-task reports.
|
|
443
|
+
Use --json for a machine-readable corpus export. Auth: --secret or ${SECRET_ENV_VAR}.
|
|
444
|
+
`);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
const status = args["--status"];
|
|
448
|
+
if (status && !EVAL_STATUSES.includes(status)) {
|
|
449
|
+
throw new FeedbackError(`Invalid --status. Expected: ${EVAL_STATUSES.join(", ")}.`);
|
|
450
|
+
}
|
|
451
|
+
const api = await buildApi(args);
|
|
452
|
+
const result = await api.listEvals(status ? { status } : {});
|
|
453
|
+
if (args["--json"]) {
|
|
454
|
+
console.log(JSON.stringify(result, null, 2));
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
if (result.evals.length === 0) {
|
|
458
|
+
console.log("No evals yet. Promote a failed-task report: npx hivenet promote <eventId>");
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
console.log(`${result.evals.length} eval(s):
|
|
462
|
+
`);
|
|
463
|
+
for (const entry of result.evals) {
|
|
464
|
+
printEval(entry);
|
|
465
|
+
console.log("");
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
async function runEval(argv) {
|
|
469
|
+
const args = (0, import_arg.default)({ ...OWNER_FLAGS, "--status": String, "--note": String }, { argv });
|
|
470
|
+
if (args["--help"] || args._.length === 0) {
|
|
471
|
+
console.log(`
|
|
472
|
+
Usage
|
|
473
|
+
$ npx hivenet eval <evalId> (show)
|
|
474
|
+
$ npx hivenet eval <evalId> --status accepted (curate)
|
|
475
|
+
$ npx hivenet eval <evalId> --status archived --note "why"
|
|
476
|
+
|
|
477
|
+
Statuses: ${EVAL_STATUSES.join(" | ")}. Auth: --secret or ${SECRET_ENV_VAR}.
|
|
478
|
+
`);
|
|
479
|
+
if (!args["--help"]) {
|
|
480
|
+
throw new FeedbackError("An eval ID is required.");
|
|
481
|
+
}
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
const status = args["--status"];
|
|
485
|
+
if (status && !EVAL_STATUSES.includes(status)) {
|
|
486
|
+
throw new FeedbackError(`Invalid --status. Expected: ${EVAL_STATUSES.join(", ")}.`);
|
|
487
|
+
}
|
|
488
|
+
if (args["--note"] !== void 0 && !status) {
|
|
489
|
+
throw new FeedbackError("--note requires --status.");
|
|
490
|
+
}
|
|
491
|
+
const api = await buildApi(args);
|
|
492
|
+
const id = args._[0];
|
|
493
|
+
if (status) {
|
|
494
|
+
await api.setEvalStatus(id, status, args["--note"] !== void 0 ? args["--note"] : void 0);
|
|
495
|
+
}
|
|
496
|
+
const { eval: entry } = await api.getEval(id);
|
|
497
|
+
if (args["--json"]) {
|
|
498
|
+
console.log(JSON.stringify({ eval: entry }, null, 2));
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
printEval(entry);
|
|
502
|
+
}
|
|
503
|
+
async function runPromote(argv) {
|
|
504
|
+
const args = (0, import_arg.default)({ ...OWNER_FLAGS }, { argv });
|
|
505
|
+
if (args["--help"] || args._.length === 0) {
|
|
506
|
+
console.log(`
|
|
507
|
+
Usage
|
|
508
|
+
$ npx hivenet promote <eventId> (failed-task report \u2192 eval candidate)
|
|
509
|
+
|
|
510
|
+
Idempotent per event. When the report lacks reproduction state, the reporter
|
|
511
|
+
is asked for it on their thread; the answer fills the eval's setup.
|
|
512
|
+
`);
|
|
513
|
+
if (!args["--help"]) {
|
|
514
|
+
throw new FeedbackError("An event ID is required.");
|
|
515
|
+
}
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
const api = await buildApi(args);
|
|
519
|
+
const { eval: entry, created } = await api.promoteEval(args._[0]);
|
|
520
|
+
if (args["--json"]) {
|
|
521
|
+
console.log(JSON.stringify({ eval: entry, created }, null, 2));
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
console.log(created ? `\u2713 Promoted to eval (${entry.id})` : `Already promoted (${entry.id})`);
|
|
525
|
+
printEval(entry);
|
|
526
|
+
}
|
|
527
|
+
export {
|
|
528
|
+
runAsk,
|
|
529
|
+
runAttach,
|
|
530
|
+
runEval,
|
|
531
|
+
runEvals,
|
|
532
|
+
runInbox,
|
|
533
|
+
runIssue,
|
|
534
|
+
runIssues,
|
|
535
|
+
runPromote,
|
|
536
|
+
runQuestion,
|
|
537
|
+
runQuestions,
|
|
538
|
+
runTriage
|
|
539
|
+
};
|