hive-intelligence 1.6.3 → 1.7.1

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.
Files changed (57) hide show
  1. package/README.md +10 -4
  2. package/agent-skills/.claude-plugin/plugin.json +10 -0
  3. package/agent-skills/README.md +2 -0
  4. package/agent-skills/SKILL.md +18 -6
  5. package/agent-skills/evals/trigger-queries.json +70 -0
  6. package/agent-skills/package.json +3 -1
  7. package/agent-skills/packs/hive/SKILL.md +382 -0
  8. package/agent-skills/packs/hive/evals/evals.json +50 -0
  9. package/agent-skills/packs/hive/evals/triggers.json +15 -0
  10. package/agent-skills/packs/hive-build-onboarding/SKILL.md +1 -1
  11. package/agent-skills/packs/hive-cli/SKILL.md +2 -2
  12. package/agent-skills/packs/hive-mcp/SKILL.md +9 -5
  13. package/agent-skills/packs/hive-prediction-markets/SKILL.md +145 -0
  14. package/agent-skills/packs/hive-prediction-markets/evals/evals.json +29 -0
  15. package/agent-skills/packs/hive-prediction-markets/evals/triggers.json +14 -0
  16. package/agent-skills/packs/hive-query/SKILL.md +2 -1
  17. package/agent-skills/packs/hive-stateful-monitoring/SKILL.md +3 -3
  18. package/agent-skills/references/agents-routing.md +4 -1
  19. package/agent-skills/scripts/validate-agent-skills-package.mjs +2 -0
  20. package/agent-skills/skills.sh.json +9 -1
  21. package/build/{api-client-6AM3HVHQ.js → api-client-UGNFUZ2L.js} +12 -3
  22. package/build/{auth-4LPSQJXD.js → auth-5TLYP2DC.js} +1 -1
  23. package/build/call-args-NVOYX6DL.js +27 -0
  24. package/build/{chunk-XZJICKAL.js → chunk-2C5FBG7B.js} +4 -3
  25. package/build/{chunk-IJ5KOZH5.js → chunk-3SQPLZW6.js} +42730 -39105
  26. package/build/{chunk-S4HBVZ7O.js → chunk-3Z5VR53Z.js} +36 -7
  27. package/build/{chunk-GADPH72D.js → chunk-4QDGA6GP.js} +52 -1
  28. package/build/{chunk-2SPCKQBJ.js → chunk-5QAXJON7.js} +71 -0
  29. package/build/{chunk-GYVF75XB.js → chunk-FQNW6SZ6.js} +1 -1
  30. package/build/{chunk-LW2DHE32.js → chunk-L4KVJDA7.js} +1 -1
  31. package/build/chunk-POPAEM4E.js +474 -0
  32. package/build/{chunk-P7NLFSZQ.js → chunk-TWYGJEVN.js} +100 -9
  33. package/build/{chunk-32NRUWXA.js → chunk-UPWYFTTJ.js} +3 -3
  34. package/build/{chunk-KUF754NT.js → chunk-WII7MTYZ.js} +52 -12
  35. package/build/chunk-XN3T6YOG.js +32 -0
  36. package/build/chunk-XT6RURQV.js +394 -0
  37. package/build/{chunk-LP5ECG7A.js → chunk-ZDXMZPC6.js} +26 -2
  38. package/build/cli.js +120 -48
  39. package/build/{completion-YVSWWF6D.js → completion-ROZAH667.js} +1 -1
  40. package/build/{doctor-YBCU5FLQ.js → doctor-44WSVNGJ.js} +42 -5
  41. package/build/{errors-EL6UMXNJ.js → errors-W3GXQN34.js} +7 -1
  42. package/build/feedback-FRRLZDAF.js +113 -0
  43. package/build/{init-all-54G3CVZA.js → init-all-LYRFE2YQ.js} +5 -5
  44. package/build/{mcpServer-V4XUSCX7.js → mcpServer-43LTLURB.js} +2 -2
  45. package/build/monitor-worker.js +62 -10
  46. package/build/{namespace-PA2IDNRT.js → namespace-XMFFW47T.js} +1 -1
  47. package/build/{output-XGTE4EWJ.js → output-BBHBJI32.js} +5 -1
  48. package/build/release.json +4 -4
  49. package/build/routing-CP43PP5F.js +43 -0
  50. package/build/{serve-SU67DIY3.js → serve-LTMDRKXS.js} +1 -1
  51. package/build/server.js +295 -41
  52. package/build/stdio.js +2 -2
  53. package/build/{tools-FNPMELSU.js → tools-VA7JXD3K.js} +81 -33
  54. package/build/{uninstall-P3JJZCXT.js → uninstall-3KJLHTNM.js} +26 -7
  55. package/build/{upgrade-ATIFZANV.js → upgrade-3NR3LAGU.js} +2 -2
  56. package/build/{watch-FG2C5Y2Z.js → watch-HJOTI32P.js} +5 -3
  57. package/package.json +1 -1
@@ -0,0 +1,474 @@
1
+ import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
2
+ import {
3
+ CliError,
4
+ ExitCode
5
+ } from "./chunk-3Z5VR53Z.js";
6
+ import {
7
+ errorEnvelope,
8
+ info,
9
+ shouldOutputJson,
10
+ successEnvelope,
11
+ writeOutput,
12
+ yellow
13
+ } from "./chunk-ZDXMZPC6.js";
14
+ import {
15
+ ensureConfigDir,
16
+ getConfigDir,
17
+ readJsonFile,
18
+ writeJsonFile
19
+ } from "./chunk-6PC3HUAK.js";
20
+
21
+ // src/cli/routing.ts
22
+ import { createHash } from "crypto";
23
+ import {
24
+ existsSync,
25
+ mkdirSync,
26
+ readFileSync,
27
+ unlinkSync,
28
+ writeFileSync
29
+ } from "fs";
30
+ import { homedir } from "os";
31
+ import { dirname, join, resolve } from "path";
32
+
33
+ // src/cli/routing-block.ts
34
+ var ROUTING_BLOCK = [
35
+ "## Hive MCP Routing",
36
+ "",
37
+ "For live crypto intelligence, prefer Hive retrieval over model memory.",
38
+ "",
39
+ "- User asks about live crypto data, prices, wallets, tokens, DeFi, NFTs,",
40
+ " Solana, security, DEX pools, network state, or prediction markets:",
41
+ " use `hive` (it loads `hive-query` for the full toolset walk).",
42
+ "- Exact Hive endpoint, provider, schema, or argument shape is unknown:",
43
+ " use `hive-tool-discovery`.",
44
+ "- Clear domain request:",
45
+ " - prices, liquidity, exchanges, OHLC, order books: `hive-market-research`",
46
+ " - token metadata, holders, liquidity, enrichment, risk:",
47
+ " `hive-token-diligence`",
48
+ " - wallet balances, transfers, PnL, NFTs, DeFi positions:",
49
+ " `hive-wallet-investigation`",
50
+ " - token, approval, contract, dApp, phishing, simulation risk:",
51
+ " `hive-security-risk`",
52
+ " - DEX pools, pairs, trades, liquidity, OHLCV: `hive-dex-pool-analysis`",
53
+ " - protocol TVL, fees, yields, stablecoins, bridges:",
54
+ " `hive-defi-research`",
55
+ " - NFT ownership, metadata, floors, sales, rarity: `hive-nft-research`",
56
+ " - Solana wallets, SPL accounts, DAS assets, priority fees:",
57
+ " `hive-solana-analysis`",
58
+ " - blocks, gas, receipts, logs, RPC diagnostics:",
59
+ " `hive-network-infrastructure`",
60
+ " - Polymarket odds, events, order books, odds history, wallet positions:",
61
+ " `hive-prediction-markets`",
62
+ "- User asks Hive to remember, monitor, schedule, alert, or report crypto",
63
+ " intelligence across sessions: use `hive-stateful-monitoring`.",
64
+ "- Setup, API key, CLI, or app integration:",
65
+ " use `hive-mcp`, `hive-build-onboarding`, `hive-cli`, or `hive-build`.",
66
+ "",
67
+ "Default execution loop:",
68
+ "1. Read the compact `hive://toolsets` index or call `search_tools`.",
69
+ "2. Select one task toolset and its best matching compact `routes[]` entry.",
70
+ " Preserve `route_id`; follow the ordered calls, conditional per-call",
71
+ " fallbacks, four-call cap, and stop condition. Use `coverageCatalog` only",
72
+ " for long-tail discovery.",
73
+ "3. Call `get_api_endpoint_schema` and inspect `operation`/`callPattern`.",
74
+ "4. Execute reads through `invoke_api_endpoint` with bounded args. Explain any",
75
+ " Hive state change, obtain explicit approval, then use",
76
+ " `invoke_stateful_endpoint`; never auto-approve it.",
77
+ "5. Copy exact server-returned `_hive` blocks into the task receipt, cite exact",
78
+ " receipt IDs from `claims[]`, account for every canonical `coverage[]`",
79
+ " phase, include `route_id`, and call `validate_task_result` before presenting a structured",
80
+ " result.",
81
+ "6. Report provider, source recency, fallback/cache/degraded state, and runtime",
82
+ " status (`ok`, `invalid_input`, `missing_key`, `plan_required`,",
83
+ " `rate_limited`, `degraded`, or `failing`). `observed_at` is Hive",
84
+ " first-observation/original cache time, not necessarily upstream event time."
85
+ ].join("\n");
86
+
87
+ // src/cli/routing.ts
88
+ var ROUTING_START = "<!-- hive-routing:start -->";
89
+ var ROUTING_END = "<!-- hive-routing:end -->";
90
+ var ROUTING_FILES = ["AGENTS.md", "CLAUDE.md"];
91
+ function detectEol(content) {
92
+ return content.includes("\r\n") ? "\r\n" : "\n";
93
+ }
94
+ function renderRoutingBlock(eol = "\n") {
95
+ return [ROUTING_START, ...ROUTING_BLOCK.split("\n"), ROUTING_END].join(eol) + eol;
96
+ }
97
+ function findRoutingBlock(content) {
98
+ const start = content.indexOf(ROUTING_START);
99
+ if (start < 0) return null;
100
+ if (start > 0 && content[start - 1] !== "\n") return null;
101
+ const endMarker = content.indexOf(ROUTING_END, start + ROUTING_START.length);
102
+ if (endMarker < 0) return null;
103
+ let end = endMarker + ROUTING_END.length;
104
+ if (content.startsWith("\r\n", end)) end += 2;
105
+ else if (content.startsWith("\n", end)) end += 1;
106
+ const inner = content.slice(start + ROUTING_START.length, endMarker).replace(/\r\n/g, "\n");
107
+ const body = inner.replace(/^\n/, "").replace(/\n$/, "");
108
+ return { start, end, body };
109
+ }
110
+ function appendRoutingBlock(content) {
111
+ const eol = detectEol(content);
112
+ const block = renderRoutingBlock(eol);
113
+ if (content === "") return block;
114
+ return content + (content.endsWith("\n") ? eol : eol + eol) + block;
115
+ }
116
+ function stripRoutingBlock(content) {
117
+ const span = findRoutingBlock(content);
118
+ if (!span) return null;
119
+ let before = content.slice(0, span.start);
120
+ if (before.endsWith("\r\n\r\n")) before = before.slice(0, -2);
121
+ else if (before.endsWith("\n\n")) before = before.slice(0, -1);
122
+ return before + content.slice(span.end);
123
+ }
124
+ function diffLines(expected, actual) {
125
+ const a = expected.split("\n");
126
+ const b = actual.split("\n");
127
+ const out = [];
128
+ for (let i = 0; i < Math.max(a.length, b.length); i++) {
129
+ if (a[i] === b[i]) continue;
130
+ if (a[i] !== void 0) out.push(`- ${a[i]}`);
131
+ if (b[i] !== void 0) out.push(`+ ${b[i]}`);
132
+ }
133
+ return out;
134
+ }
135
+ function recordsPath() {
136
+ return join(getConfigDir(), "routing.json");
137
+ }
138
+ function readRoutingRecords() {
139
+ return readJsonFile(recordsPath()) ?? {};
140
+ }
141
+ function writeRoutingRecords(records) {
142
+ ensureConfigDir();
143
+ writeJsonFile(recordsPath(), records);
144
+ }
145
+ function projectHash(projectPath) {
146
+ return createHash("sha256").update(projectPath).digest("hex").slice(0, 16);
147
+ }
148
+ function backupPath(projectPath) {
149
+ return join(getConfigDir(), "routing", `${projectHash(projectPath)}.orig`);
150
+ }
151
+ function resolveProject(opts) {
152
+ return resolve(opts.cwd ?? process.cwd());
153
+ }
154
+ function outcome(action, project, file, status, message, exitCode, extra = {}) {
155
+ return { action, project, file, status, message, exitCode, ...extra };
156
+ }
157
+ function refuseHome(action, project) {
158
+ if (project !== resolve(homedir())) return null;
159
+ return outcome(
160
+ action,
161
+ project,
162
+ null,
163
+ "refused",
164
+ "Refusing to touch your home directory; run `hive routing` inside a project.",
165
+ ExitCode.INVALID_ARGS
166
+ );
167
+ }
168
+ function skippedByEnv(action, project) {
169
+ return outcome(
170
+ action,
171
+ project,
172
+ null,
173
+ "skipped",
174
+ `Routing ${action} skipped: HIVE_ROUTING=never is set, so the CLI never writes routing blocks on this machine.`,
175
+ ExitCode.SUCCESS
176
+ );
177
+ }
178
+ function isRoutingFile(name) {
179
+ return ROUTING_FILES.includes(name);
180
+ }
181
+ function pickFile(project, requested) {
182
+ if (requested !== void 0) {
183
+ if (!isRoutingFile(requested)) {
184
+ return {
185
+ error: `--file must be one of ${ROUTING_FILES.join(", ")} (got '${requested}')`
186
+ };
187
+ }
188
+ return { file: requested };
189
+ }
190
+ for (const candidate of ROUTING_FILES) {
191
+ if (existsSync(join(project, candidate))) return { file: candidate };
192
+ }
193
+ return { file: ROUTING_FILES[0] };
194
+ }
195
+ function installRouting(opts = {}) {
196
+ const project = resolveProject(opts);
197
+ if (process.env.HIVE_ROUTING === "never")
198
+ return skippedByEnv("install", project);
199
+ const home = refuseHome("install", project);
200
+ if (home) return home;
201
+ const pick = pickFile(project, opts.file);
202
+ if ("error" in pick) {
203
+ return outcome(
204
+ "install",
205
+ project,
206
+ null,
207
+ "refused",
208
+ pick.error,
209
+ ExitCode.INVALID_ARGS
210
+ );
211
+ }
212
+ const filePath = join(project, pick.file);
213
+ const exists = existsSync(filePath);
214
+ if (!exists && !opts.create) {
215
+ return outcome(
216
+ "install",
217
+ project,
218
+ pick.file,
219
+ "refused",
220
+ `${pick.file} not found in ${project}; pass --create to create it${opts.file ? "" : ", or --file CLAUDE.md to target that file"}.`,
221
+ ExitCode.INVALID_ARGS
222
+ );
223
+ }
224
+ const originalBytes = exists ? readFileSync(filePath) : Buffer.alloc(0);
225
+ const original = originalBytes.toString("utf-8");
226
+ const span = findRoutingBlock(original);
227
+ const records = readRoutingRecords();
228
+ if (span) {
229
+ if (span.body !== ROUTING_BLOCK) {
230
+ return outcome(
231
+ "install",
232
+ project,
233
+ pick.file,
234
+ "refused",
235
+ `${pick.file} already has a Hive routing block that was edited by hand; leaving it alone. Remove it manually to reinstall the stock block.`,
236
+ ExitCode.GENERAL_ERROR,
237
+ { diff: diffLines(ROUTING_BLOCK, span.body) }
238
+ );
239
+ }
240
+ if (!opts.dryRun && records[project]?.status !== "installed") {
241
+ records[project] = {
242
+ status: "installed",
243
+ file: pick.file,
244
+ at: (/* @__PURE__ */ new Date()).toISOString()
245
+ };
246
+ writeRoutingRecords(records);
247
+ }
248
+ return outcome(
249
+ "install",
250
+ project,
251
+ pick.file,
252
+ "already-installed",
253
+ `${pick.file} already contains the Hive routing block; nothing to do.`,
254
+ ExitCode.SUCCESS
255
+ );
256
+ }
257
+ const next = appendRoutingBlock(original);
258
+ if (opts.dryRun) {
259
+ return outcome(
260
+ "install",
261
+ project,
262
+ pick.file,
263
+ "dry-run",
264
+ `Would ${exists ? "append the Hive routing block to" : "create"} ${pick.file} (dry-run: nothing written).`,
265
+ ExitCode.SUCCESS,
266
+ { content: next }
267
+ );
268
+ }
269
+ const backup = backupPath(project);
270
+ mkdirSync(dirname(backup), { recursive: true, mode: 448 });
271
+ writeFileSync(backup, originalBytes);
272
+ writeFileSync(filePath, next, "utf-8");
273
+ records[project] = {
274
+ status: "installed",
275
+ file: pick.file,
276
+ at: (/* @__PURE__ */ new Date()).toISOString(),
277
+ ...exists ? {} : { created: true }
278
+ };
279
+ writeRoutingRecords(records);
280
+ return outcome(
281
+ "install",
282
+ project,
283
+ pick.file,
284
+ "installed",
285
+ `${exists ? "Appended" : "Created"} ${pick.file} with the Hive routing block. Undo with: hive routing remove`,
286
+ ExitCode.SUCCESS
287
+ );
288
+ }
289
+ function declineRouting(opts = {}) {
290
+ const project = resolveProject(opts);
291
+ if (process.env.HIVE_ROUTING === "never")
292
+ return skippedByEnv("decline", project);
293
+ const home = refuseHome("decline", project);
294
+ if (home) return home;
295
+ if (opts.dryRun) {
296
+ return outcome(
297
+ "decline",
298
+ project,
299
+ null,
300
+ "dry-run",
301
+ `Would record ${project} as declined (dry-run: nothing written).`,
302
+ ExitCode.SUCCESS
303
+ );
304
+ }
305
+ const records = readRoutingRecords();
306
+ records[project] = {
307
+ status: "declined",
308
+ file: null,
309
+ at: (/* @__PURE__ */ new Date()).toISOString()
310
+ };
311
+ writeRoutingRecords(records);
312
+ return outcome(
313
+ "decline",
314
+ project,
315
+ null,
316
+ "declined",
317
+ `Recorded: no Hive routing block for ${project}. Change your mind with: hive routing install`,
318
+ ExitCode.SUCCESS
319
+ );
320
+ }
321
+ function forgetProject(project, records) {
322
+ const backup = backupPath(project);
323
+ if (existsSync(backup)) unlinkSync(backup);
324
+ if (project in records) {
325
+ delete records[project];
326
+ writeRoutingRecords(records);
327
+ }
328
+ }
329
+ function removeRouting(opts = {}) {
330
+ const project = resolveProject(opts);
331
+ const home = refuseHome("remove", project);
332
+ if (home) return home;
333
+ const records = readRoutingRecords();
334
+ const record = records[project];
335
+ let file = opts.file ?? record?.file ?? void 0;
336
+ if (file !== void 0 && !isRoutingFile(file)) {
337
+ return outcome(
338
+ "remove",
339
+ project,
340
+ null,
341
+ "refused",
342
+ `--file must be one of ${ROUTING_FILES.join(", ")} (got '${file}')`,
343
+ ExitCode.INVALID_ARGS
344
+ );
345
+ }
346
+ if (file === void 0) {
347
+ file = ROUTING_FILES.find((candidate) => {
348
+ const path = join(project, candidate);
349
+ return existsSync(path) && findRoutingBlock(readFileSync(path, "utf-8")) !== null;
350
+ });
351
+ }
352
+ const filePath = file ? join(project, file) : null;
353
+ const current = filePath && existsSync(filePath) ? readFileSync(filePath, "utf-8") : null;
354
+ const span = current === null ? null : findRoutingBlock(current);
355
+ if (current === null || !span) {
356
+ if (!opts.dryRun) forgetProject(project, records);
357
+ return outcome(
358
+ "remove",
359
+ project,
360
+ file ?? null,
361
+ "not-installed",
362
+ `No Hive routing block found in ${project}; nothing to remove.`,
363
+ ExitCode.SUCCESS
364
+ );
365
+ }
366
+ if (span.body !== ROUTING_BLOCK) {
367
+ return outcome(
368
+ "remove",
369
+ project,
370
+ file,
371
+ "refused",
372
+ `The Hive routing block in ${file} was edited by hand; refusing to remove it. Delete it manually if you want it gone.`,
373
+ ExitCode.GENERAL_ERROR,
374
+ { diff: diffLines(ROUTING_BLOCK, span.body) }
375
+ );
376
+ }
377
+ const stripped = stripRoutingBlock(current);
378
+ const backup = backupPath(project);
379
+ let final = stripped;
380
+ let note = "";
381
+ if (existsSync(backup)) {
382
+ const originalBytes = readFileSync(backup);
383
+ const original = originalBytes.toString("utf-8");
384
+ if (stripped === original || stripped === original + detectEol(current)) {
385
+ final = originalBytes;
386
+ } else {
387
+ note = " The file changed since install, so only the block was removed and your other edits were kept.";
388
+ }
389
+ }
390
+ if (opts.dryRun) {
391
+ return outcome(
392
+ "remove",
393
+ project,
394
+ file,
395
+ "dry-run",
396
+ `Would remove the Hive routing block from ${file} (dry-run: nothing written).${note}`,
397
+ ExitCode.SUCCESS,
398
+ { content: final.toString() }
399
+ );
400
+ }
401
+ if (record?.created && final.length === 0) {
402
+ unlinkSync(filePath);
403
+ } else {
404
+ writeFileSync(filePath, final);
405
+ }
406
+ forgetProject(project, records);
407
+ return outcome(
408
+ "remove",
409
+ project,
410
+ file,
411
+ "removed",
412
+ `${record?.created && final.length === 0 ? `Deleted ${file}, which install had created` : `Removed the Hive routing block from ${file}`}.${note}`,
413
+ ExitCode.SUCCESS
414
+ );
415
+ }
416
+ function removeRecordedRoutingBlocks(opts = {}) {
417
+ const records = readRoutingRecords();
418
+ const outcomes = [];
419
+ for (const [project, record] of Object.entries(records)) {
420
+ if (record.status !== "installed") continue;
421
+ outcomes.push(
422
+ removeRouting({
423
+ cwd: project,
424
+ file: record.file ?? void 0,
425
+ dryRun: opts.dryRun
426
+ })
427
+ );
428
+ }
429
+ return outcomes;
430
+ }
431
+ async function runRouting(action, opts = {}) {
432
+ const result = action === "install" ? installRouting(opts) : action === "decline" ? declineRouting(opts) : removeRouting(opts);
433
+ const { exitCode, ...data } = result;
434
+ if (opts.json || shouldOutputJson()) {
435
+ if (exitCode === ExitCode.SUCCESS) {
436
+ await writeOutput(successEnvelope(data, {}));
437
+ } else {
438
+ const err = new CliError(
439
+ exitCode === ExitCode.INVALID_ARGS ? "INVALID_ARGS" : "GENERAL_ERROR",
440
+ result.message
441
+ );
442
+ await writeOutput({ ...errorEnvelope(err, {}), data });
443
+ }
444
+ return exitCode;
445
+ }
446
+ if (result.status === "dry-run" && result.content !== void 0) {
447
+ process.stdout.write(result.content);
448
+ }
449
+ info(result.message);
450
+ for (const line of result.diff ?? []) {
451
+ process.stderr.write(yellow(` ${line}`) + "\n");
452
+ }
453
+ return exitCode;
454
+ }
455
+
456
+ export {
457
+ ROUTING_START,
458
+ ROUTING_END,
459
+ ROUTING_FILES,
460
+ detectEol,
461
+ renderRoutingBlock,
462
+ findRoutingBlock,
463
+ appendRoutingBlock,
464
+ stripRoutingBlock,
465
+ diffLines,
466
+ readRoutingRecords,
467
+ projectHash,
468
+ backupPath,
469
+ installRouting,
470
+ declineRouting,
471
+ removeRouting,
472
+ removeRecordedRoutingBlocks,
473
+ runRouting
474
+ };
@@ -1,4 +1,7 @@
1
1
  import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
2
+ import {
3
+ NEVER_RETRY_ERROR_CODES
4
+ } from "./chunk-3Z5VR53Z.js";
2
5
  import {
3
6
  SERVER_VERSION
4
7
  } from "./chunk-ZXKFJQDE.js";
@@ -9,6 +12,56 @@ import {
9
12
  } from "./chunk-6PC3HUAK.js";
10
13
 
11
14
  // src/cli/api-client.ts
15
+ function errorCodeFromBody(body) {
16
+ if (!body || typeof body !== "object") return null;
17
+ const error = body.error;
18
+ if (!error || typeof error !== "object") return null;
19
+ const direct = error.code;
20
+ if (typeof direct === "string") return direct;
21
+ const nested = error.data?.code;
22
+ return typeof nested === "string" ? nested : null;
23
+ }
24
+ async function peekErrorCode(response) {
25
+ if (typeof response.clone !== "function") return null;
26
+ try {
27
+ return errorCodeFromBody(await response.clone().json());
28
+ } catch {
29
+ return null;
30
+ }
31
+ }
32
+ function parseRetryAfter(header) {
33
+ if (header == null || header === "") return null;
34
+ const seconds = Number.parseInt(header, 10);
35
+ return Number.isFinite(seconds) && seconds >= 0 ? seconds : null;
36
+ }
37
+ function attachResponseMeta(data, response, now = Date.now()) {
38
+ const meta = { ...data.meta ?? {} };
39
+ const quotaHeader = response.headers?.get?.("x-quota-remaining");
40
+ if (quotaHeader != null && quotaHeader !== "") {
41
+ const quotaRemaining = Number.parseInt(quotaHeader, 10);
42
+ if (Number.isFinite(quotaRemaining)) meta.quota_remaining = quotaRemaining;
43
+ }
44
+ if ("credits_remaining" in meta) {
45
+ const credits = meta.credits_remaining;
46
+ if (credits === null || typeof credits === "number") {
47
+ meta.quota_remaining = credits;
48
+ }
49
+ }
50
+ const retryAfter = parseRetryAfter(response.headers?.get?.("retry-after"));
51
+ const bodyResetAt = [
52
+ data.reset_at,
53
+ data.error?.reset_at,
54
+ data.error?.data?.reset_at,
55
+ meta.reset_at
56
+ ].find((value) => typeof value === "string" && value !== "");
57
+ if (retryAfter !== null) meta.retry_after_seconds = retryAfter;
58
+ if (bodyResetAt) {
59
+ meta.reset_at = bodyResetAt;
60
+ } else if (retryAfter !== null) {
61
+ meta.reset_at = new Date(now + retryAfter * 1e3).toISOString();
62
+ }
63
+ return { ...data, meta };
64
+ }
12
65
  var MAX_RETRIES = 3;
13
66
  var BASE_DELAY_MS = 500;
14
67
  var MAX_DELAY_MS = 5e3;
@@ -44,6 +97,8 @@ async function fetchWithRetry(url, init, fetchFn = fetch) {
44
97
  if (response.ok || !isRetryable(response.status) || attempt === maxRetries) {
45
98
  return response;
46
99
  }
100
+ const code = await peekErrorCode(response);
101
+ if (code && NEVER_RETRY_ERROR_CODES.has(code)) return response;
47
102
  lastResponse = response;
48
103
  const retryAfter = response.headers?.get?.("retry-after") ?? null;
49
104
  const waitMs = computeDelay(attempt, retryAfter);
@@ -87,14 +142,7 @@ async function executeRemoteTool(toolName, args, timeoutMs = 3e4) {
87
142
  signal: controller.signal
88
143
  });
89
144
  const data = await response.json();
90
- const quotaHeader = response.headers?.get?.("x-quota-remaining");
91
- if (quotaHeader != null && quotaHeader !== "") {
92
- const quotaRemaining = Number.parseInt(quotaHeader, 10);
93
- if (Number.isFinite(quotaRemaining)) {
94
- data.meta = { ...data.meta ?? {}, quota_remaining: quotaRemaining };
95
- }
96
- }
97
- return data;
145
+ return attachResponseMeta(data, response);
98
146
  } catch (error) {
99
147
  if (error instanceof Error && error.name === "AbortError") {
100
148
  return {
@@ -192,10 +240,53 @@ async function getUsage() {
192
240
  });
193
241
  return response.json();
194
242
  }
243
+ var SMALL_REQUEST_TIMEOUT_MS = 1e4;
244
+ async function parseJsonBody(response) {
245
+ try {
246
+ const body = await response.json();
247
+ return body && typeof body === "object" ? body : null;
248
+ } catch {
249
+ return null;
250
+ }
251
+ }
252
+ async function postFeedback(payload) {
253
+ const url = buildApiUrl("/api/v1/feedback");
254
+ const response = await fetchWithRetry(url, {
255
+ method: "POST",
256
+ headers: getHeaders(resolveApiKey()),
257
+ body: JSON.stringify(payload),
258
+ signal: AbortSignal.timeout(SMALL_REQUEST_TIMEOUT_MS)
259
+ });
260
+ const body = await parseJsonBody(response);
261
+ if (!body) {
262
+ return {
263
+ ok: false,
264
+ error: {
265
+ code: "API_ERROR",
266
+ message: `Feedback endpoint answered HTTP ${response.status} without a JSON body`,
267
+ suggestion: `Check HIVE_API_URL (${resolveApiUrl()})`
268
+ },
269
+ meta: { http_status: response.status }
270
+ };
271
+ }
272
+ return attachResponseMeta(body, response);
273
+ }
274
+ async function getAnonQuota() {
275
+ const url = buildApiUrl("/api/v1/quota");
276
+ const response = await fetchWithRetry(url, {
277
+ headers: getHeaders(),
278
+ signal: AbortSignal.timeout(SMALL_REQUEST_TIMEOUT_MS)
279
+ });
280
+ return { status: response.status, body: await parseJsonBody(response) };
281
+ }
195
282
 
196
283
  export {
284
+ errorCodeFromBody,
285
+ attachResponseMeta,
197
286
  fetchWithRetry,
198
287
  executeRemoteTool,
199
288
  fetchToolCatalog,
200
- getUsage
289
+ getUsage,
290
+ postFeedback,
291
+ getAnonQuota
201
292
  };
@@ -294,11 +294,11 @@ async function runSwitch(name) {
294
294
  }
295
295
  }
296
296
  async function runUsage() {
297
- const { getUsage } = await import("./api-client-6AM3HVHQ.js");
297
+ const { getUsage } = await import("./api-client-UGNFUZ2L.js");
298
298
  const result = await getUsage();
299
299
  if (!result.ok) {
300
- const { outputError } = await import("./output-XGTE4EWJ.js");
301
- const { CliError, classifyError } = await import("./errors-EL6UMXNJ.js");
300
+ const { outputError } = await import("./output-BBHBJI32.js");
301
+ const { CliError, classifyError } = await import("./errors-W3GXQN34.js");
302
302
  const code = result.error?.code;
303
303
  const err = code === "AUTH_REQUIRED" || code === "INVALID_TOKEN" ? new CliError(
304
304
  "AUTH_REQUIRED",