hive-intelligence 1.6.7 → 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.
- package/README.md +10 -4
- package/agent-skills/.claude-plugin/plugin.json +10 -0
- package/agent-skills/README.md +2 -0
- package/agent-skills/SKILL.md +18 -6
- package/agent-skills/evals/trigger-queries.json +70 -0
- package/agent-skills/package.json +3 -1
- package/agent-skills/packs/hive/SKILL.md +382 -0
- package/agent-skills/packs/hive/evals/evals.json +50 -0
- package/agent-skills/packs/hive/evals/triggers.json +15 -0
- package/agent-skills/packs/hive-build-onboarding/SKILL.md +1 -1
- package/agent-skills/packs/hive-cli/SKILL.md +2 -2
- package/agent-skills/packs/hive-mcp/SKILL.md +9 -5
- package/agent-skills/packs/hive-prediction-markets/SKILL.md +145 -0
- package/agent-skills/packs/hive-prediction-markets/evals/evals.json +29 -0
- package/agent-skills/packs/hive-prediction-markets/evals/triggers.json +14 -0
- package/agent-skills/packs/hive-query/SKILL.md +2 -1
- package/agent-skills/packs/hive-stateful-monitoring/SKILL.md +3 -3
- package/agent-skills/references/agents-routing.md +4 -1
- package/agent-skills/scripts/validate-agent-skills-package.mjs +2 -0
- package/agent-skills/skills.sh.json +9 -1
- package/build/{api-client-6AM3HVHQ.js → api-client-UGNFUZ2L.js} +12 -3
- package/build/{auth-4LPSQJXD.js → auth-5TLYP2DC.js} +1 -1
- package/build/call-args-NVOYX6DL.js +27 -0
- package/build/{chunk-G23ZAW36.js → chunk-2C5FBG7B.js} +3 -2
- package/build/{chunk-J2PXISXW.js → chunk-3SQPLZW6.js} +42810 -39335
- package/build/{chunk-S4HBVZ7O.js → chunk-3Z5VR53Z.js} +36 -7
- package/build/{chunk-GADPH72D.js → chunk-4QDGA6GP.js} +52 -1
- package/build/{chunk-2SPCKQBJ.js → chunk-5QAXJON7.js} +71 -0
- package/build/{chunk-GYVF75XB.js → chunk-FQNW6SZ6.js} +1 -1
- package/build/{chunk-LW2DHE32.js → chunk-L4KVJDA7.js} +1 -1
- package/build/chunk-POPAEM4E.js +474 -0
- package/build/{chunk-P7NLFSZQ.js → chunk-TWYGJEVN.js} +100 -9
- package/build/{chunk-32NRUWXA.js → chunk-UPWYFTTJ.js} +3 -3
- package/build/{chunk-KUF754NT.js → chunk-WII7MTYZ.js} +52 -12
- package/build/chunk-XN3T6YOG.js +32 -0
- package/build/chunk-XT6RURQV.js +394 -0
- package/build/{chunk-LP5ECG7A.js → chunk-ZDXMZPC6.js} +26 -2
- package/build/cli.js +120 -48
- package/build/{completion-YVSWWF6D.js → completion-ROZAH667.js} +1 -1
- package/build/{doctor-AM6QST7Z.js → doctor-44WSVNGJ.js} +42 -5
- package/build/{errors-EL6UMXNJ.js → errors-W3GXQN34.js} +7 -1
- package/build/feedback-FRRLZDAF.js +113 -0
- package/build/{init-all-54G3CVZA.js → init-all-LYRFE2YQ.js} +5 -5
- package/build/{mcpServer-NWOKFR7Q.js → mcpServer-43LTLURB.js} +2 -2
- package/build/monitor-worker.js +62 -10
- package/build/{namespace-PA2IDNRT.js → namespace-XMFFW47T.js} +1 -1
- package/build/{output-XGTE4EWJ.js → output-BBHBJI32.js} +5 -1
- package/build/release.json +4 -4
- package/build/routing-CP43PP5F.js +43 -0
- package/build/{serve-XRNMHIJX.js → serve-LTMDRKXS.js} +1 -1
- package/build/server.js +295 -41
- package/build/stdio.js +2 -2
- package/build/{tools-FNPMELSU.js → tools-VA7JXD3K.js} +81 -33
- package/build/{uninstall-P3JJZCXT.js → uninstall-3KJLHTNM.js} +26 -7
- package/build/{upgrade-ATIFZANV.js → upgrade-3NR3LAGU.js} +2 -2
- package/build/{watch-FG2C5Y2Z.js → watch-HJOTI32P.js} +5 -3
- 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
|
-
|
|
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-
|
|
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-
|
|
301
|
-
const { CliError, classifyError } = await import("./errors-
|
|
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",
|