@synaplink/orqlaude 0.3.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/.mcp.json.template +8 -0
- package/README.md +239 -0
- package/dist/__tests__/hallucination.test.d.ts +1 -0
- package/dist/__tests__/hallucination.test.js +65 -0
- package/dist/__tests__/hallucination.test.js.map +1 -0
- package/dist/__tests__/state.test.d.ts +1 -0
- package/dist/__tests__/state.test.js +124 -0
- package/dist/__tests__/state.test.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +322 -0
- package/dist/cli.js.map +1 -0
- package/dist/lib/audit.d.ts +38 -0
- package/dist/lib/audit.js +108 -0
- package/dist/lib/audit.js.map +1 -0
- package/dist/lib/budgeting.d.ts +37 -0
- package/dist/lib/budgeting.js +67 -0
- package/dist/lib/budgeting.js.map +1 -0
- package/dist/lib/hallucination.d.ts +46 -0
- package/dist/lib/hallucination.js +154 -0
- package/dist/lib/hallucination.js.map +1 -0
- package/dist/lib/jsonl_tail.d.ts +40 -0
- package/dist/lib/jsonl_tail.js +126 -0
- package/dist/lib/jsonl_tail.js.map +1 -0
- package/dist/lib/pricing.d.ts +24 -0
- package/dist/lib/pricing.js +43 -0
- package/dist/lib/pricing.js.map +1 -0
- package/dist/lib/state.d.ts +118 -0
- package/dist/lib/state.js +138 -0
- package/dist/lib/state.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +34 -0
- package/dist/server.js.map +1 -0
- package/dist/telegram/api.d.ts +45 -0
- package/dist/telegram/api.js +59 -0
- package/dist/telegram/api.js.map +1 -0
- package/dist/telegram/bot.d.ts +11 -0
- package/dist/telegram/bot.js +73 -0
- package/dist/telegram/bot.js.map +1 -0
- package/dist/telegram/commands.d.ts +22 -0
- package/dist/telegram/commands.js +217 -0
- package/dist/telegram/commands.js.map +1 -0
- package/dist/telegram/config.d.ts +30 -0
- package/dist/telegram/config.js +37 -0
- package/dist/telegram/config.js.map +1 -0
- package/dist/telegram/notifier.d.ts +14 -0
- package/dist/telegram/notifier.js +136 -0
- package/dist/telegram/notifier.js.map +1 -0
- package/dist/tools/broker.d.ts +15 -0
- package/dist/tools/broker.js +245 -0
- package/dist/tools/broker.js.map +1 -0
- package/dist/tools/dispatch.d.ts +14 -0
- package/dist/tools/dispatch.js +231 -0
- package/dist/tools/dispatch.js.map +1 -0
- package/dist/tools/lifecycle.d.ts +18 -0
- package/dist/tools/lifecycle.js +124 -0
- package/dist/tools/lifecycle.js.map +1 -0
- package/dist/tools/ping.d.ts +2 -0
- package/dist/tools/ping.js +26 -0
- package/dist/tools/ping.js.map +1 -0
- package/dist/tools/planning.d.ts +4 -0
- package/dist/tools/planning.js +160 -0
- package/dist/tools/planning.js.map +1 -0
- package/dist/tools/review.d.ts +18 -0
- package/dist/tools/review.js +93 -0
- package/dist/tools/review.js.map +1 -0
- package/package.json +56 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import readline from "node:readline/promises";
|
|
4
|
+
import { stdin, stdout } from "node:process";
|
|
5
|
+
import { StateStore } from "./lib/state.js";
|
|
6
|
+
import { AuditLog } from "./lib/audit.js";
|
|
7
|
+
import { snapshotSession } from "./lib/jsonl_tail.js";
|
|
8
|
+
import { loadConfig, saveConfig, CONFIG_PATH } from "./telegram/config.js";
|
|
9
|
+
import { TelegramApi } from "./telegram/api.js";
|
|
10
|
+
import { runBot } from "./telegram/bot.js";
|
|
11
|
+
/**
|
|
12
|
+
* orqlaude CLI — read-only inspection of state + audit log + Telegram setup
|
|
13
|
+
* and run.
|
|
14
|
+
*
|
|
15
|
+
* Subcommands:
|
|
16
|
+
* orqlaude list / status / show / history — read-only project inspection
|
|
17
|
+
* orqlaude tg setup / whitelist / start / test / show / help
|
|
18
|
+
*/
|
|
19
|
+
const STATE_DIR = process.env.ORQLAUDE_STATE_DIR ?? path.join(process.cwd(), ".orqlaude");
|
|
20
|
+
async function main() {
|
|
21
|
+
const [cmd, ...rest] = process.argv.slice(2);
|
|
22
|
+
switch (cmd) {
|
|
23
|
+
case undefined:
|
|
24
|
+
case "help":
|
|
25
|
+
case "-h":
|
|
26
|
+
case "--help":
|
|
27
|
+
printHelp();
|
|
28
|
+
return 0;
|
|
29
|
+
case "list":
|
|
30
|
+
return await cmdList();
|
|
31
|
+
case "status":
|
|
32
|
+
return await cmdStatus(rest[0]);
|
|
33
|
+
case "show":
|
|
34
|
+
return await cmdShow(rest[0]);
|
|
35
|
+
case "history":
|
|
36
|
+
return await cmdHistory(parseLimit(rest));
|
|
37
|
+
case "tg":
|
|
38
|
+
return await cmdTg(rest);
|
|
39
|
+
default:
|
|
40
|
+
console.error(`Unknown subcommand: ${cmd}`);
|
|
41
|
+
printHelp();
|
|
42
|
+
return 1;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function printHelp() {
|
|
46
|
+
console.log(`orqlaude — multi-agent orchestrator for Claude Code
|
|
47
|
+
|
|
48
|
+
Inspection:
|
|
49
|
+
orqlaude list List every plan in this project
|
|
50
|
+
orqlaude status <plan_id> Refreshed status of one plan
|
|
51
|
+
orqlaude show <plan_id> Raw plan JSON
|
|
52
|
+
orqlaude history [--limit N] Tail the audit log (default 30)
|
|
53
|
+
|
|
54
|
+
Telegram:
|
|
55
|
+
orqlaude tg setup Configure bot token (interactive)
|
|
56
|
+
orqlaude tg whitelist <id> [--owner] [--label NAME]
|
|
57
|
+
orqlaude tg unwhitelist <id>
|
|
58
|
+
orqlaude tg show Show current Telegram config
|
|
59
|
+
orqlaude tg test <chat_id> Send a test message
|
|
60
|
+
orqlaude tg start Run the bot (foreground, monitors this project)
|
|
61
|
+
orqlaude tg help Telegram-specific help
|
|
62
|
+
|
|
63
|
+
State dir: ${STATE_DIR}`);
|
|
64
|
+
}
|
|
65
|
+
// ---- read-only inspection -------------------------------------------------
|
|
66
|
+
async function cmdList() {
|
|
67
|
+
const store = new StateStore(STATE_DIR);
|
|
68
|
+
const plans = await store.read((s) => Object.values(s.plans).sort((a, b) => b.createdAt - a.createdAt));
|
|
69
|
+
if (plans.length === 0) {
|
|
70
|
+
console.log("No plans yet in this project.");
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
for (const p of plans) {
|
|
74
|
+
const done = p.tasks.filter((t) => t.status === "done").length;
|
|
75
|
+
const running = p.tasks.filter((t) => t.status === "running" || t.status === "dispatched").length;
|
|
76
|
+
console.log(`${p.id} ${p.status.padEnd(22)} ${done}/${p.tasks.length} done${running ? `, ${running} running` : ""} — ${truncate(p.rootTask, 60)}`);
|
|
77
|
+
}
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
80
|
+
async function cmdStatus(planId) {
|
|
81
|
+
if (!planId) {
|
|
82
|
+
console.error("usage: orqlaude status <plan_id>");
|
|
83
|
+
return 2;
|
|
84
|
+
}
|
|
85
|
+
const store = new StateStore(STATE_DIR);
|
|
86
|
+
let plan;
|
|
87
|
+
try {
|
|
88
|
+
plan = await store.read((s) => requirePlan(s.plans, planId));
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
console.error(err.message);
|
|
92
|
+
return 1;
|
|
93
|
+
}
|
|
94
|
+
console.log(`Plan ${plan.id}`);
|
|
95
|
+
console.log(` status: ${plan.status}`);
|
|
96
|
+
console.log(` root_task: ${plan.rootTask}`);
|
|
97
|
+
console.log(` budget: ${plan.budgetCapTokens.toLocaleString()} tokens (${Math.round(plan.budgetCapTokens / 1000)}k)`);
|
|
98
|
+
console.log(` created: ${new Date(plan.createdAt).toISOString()}`);
|
|
99
|
+
if (plan.approvedAt)
|
|
100
|
+
console.log(` approved: ${new Date(plan.approvedAt).toISOString()}`);
|
|
101
|
+
console.log(` tasks:`);
|
|
102
|
+
let totalTokens = 0;
|
|
103
|
+
for (const t of plan.tasks) {
|
|
104
|
+
let extra = "";
|
|
105
|
+
if (t.spawnedSessionId) {
|
|
106
|
+
const snap = await snapshotSession(process.cwd(), t.spawnedSessionId);
|
|
107
|
+
totalTokens += snap.totalEffectiveTokens;
|
|
108
|
+
extra = ` ${snap.totalEffectiveTokens.toLocaleString()}t ${snap.terminated ? "✓" : snap.lastToolUse?.name ?? snap.lastEventType ?? ""}`;
|
|
109
|
+
}
|
|
110
|
+
console.log(` [${t.status.padEnd(10)}] ${t.title.padEnd(50)}${extra}`);
|
|
111
|
+
if (t.prUrl)
|
|
112
|
+
console.log(` PR: ${t.prUrl}`);
|
|
113
|
+
}
|
|
114
|
+
console.log(` used: ${totalTokens.toLocaleString()} tokens`);
|
|
115
|
+
if (plan.notes.length > 0)
|
|
116
|
+
console.log(` notes: ${plan.notes.length}`);
|
|
117
|
+
if (plan.claims.length > 0)
|
|
118
|
+
console.log(` claims: ${plan.claims.length}`);
|
|
119
|
+
return 0;
|
|
120
|
+
}
|
|
121
|
+
async function cmdShow(planId) {
|
|
122
|
+
if (!planId) {
|
|
123
|
+
console.error("usage: orqlaude show <plan_id>");
|
|
124
|
+
return 2;
|
|
125
|
+
}
|
|
126
|
+
const store = new StateStore(STATE_DIR);
|
|
127
|
+
try {
|
|
128
|
+
const plan = await store.read((s) => requirePlan(s.plans, planId));
|
|
129
|
+
console.log(JSON.stringify(plan, null, 2));
|
|
130
|
+
return 0;
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
console.error(err.message);
|
|
134
|
+
return 1;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async function cmdHistory(limit) {
|
|
138
|
+
const audit = new AuditLog(STATE_DIR);
|
|
139
|
+
const events = await audit.tail(limit);
|
|
140
|
+
if (events.length === 0) {
|
|
141
|
+
console.log("(no audit events yet)");
|
|
142
|
+
return 0;
|
|
143
|
+
}
|
|
144
|
+
for (const e of events) {
|
|
145
|
+
const ts = new Date(e.ts).toISOString().slice(11, 19);
|
|
146
|
+
const ok = e.ok ? " ok" : "ERR ";
|
|
147
|
+
const id = e.planId ? ` plan=${e.planId.slice(0, 8)}` : e.sessionId ? ` sess=${e.sessionId.slice(0, 8)}` : "";
|
|
148
|
+
console.log(`${ts} ${ok} ${e.tool.padEnd(18)} ${e.durationMs.toString().padStart(4)}ms${id} ${e.resultSummary ? truncate(e.resultSummary, 80) : e.error ?? ""}`);
|
|
149
|
+
}
|
|
150
|
+
return 0;
|
|
151
|
+
}
|
|
152
|
+
// ---- Telegram subcommands -------------------------------------------------
|
|
153
|
+
async function cmdTg(args) {
|
|
154
|
+
const [sub, ...rest] = args;
|
|
155
|
+
switch (sub) {
|
|
156
|
+
case undefined:
|
|
157
|
+
case "help":
|
|
158
|
+
printTgHelp();
|
|
159
|
+
return 0;
|
|
160
|
+
case "setup":
|
|
161
|
+
return await cmdTgSetup();
|
|
162
|
+
case "show":
|
|
163
|
+
return await cmdTgShow();
|
|
164
|
+
case "whitelist":
|
|
165
|
+
return await cmdTgWhitelist(rest);
|
|
166
|
+
case "unwhitelist":
|
|
167
|
+
return await cmdTgUnwhitelist(rest);
|
|
168
|
+
case "test":
|
|
169
|
+
return await cmdTgTest(rest);
|
|
170
|
+
case "start":
|
|
171
|
+
return await cmdTgStart();
|
|
172
|
+
default:
|
|
173
|
+
console.error(`Unknown tg subcommand: ${sub}`);
|
|
174
|
+
printTgHelp();
|
|
175
|
+
return 1;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function printTgHelp() {
|
|
179
|
+
console.log(`orqlaude tg — Telegram bot
|
|
180
|
+
|
|
181
|
+
Commands:
|
|
182
|
+
orqlaude tg setup Interactively set the bot token
|
|
183
|
+
orqlaude tg show Show current config
|
|
184
|
+
orqlaude tg whitelist <user_id> [--owner] [--label NAME]
|
|
185
|
+
orqlaude tg unwhitelist <user_id>
|
|
186
|
+
orqlaude tg test <chat_id> Send a test message
|
|
187
|
+
orqlaude tg start Run the bot (foreground)
|
|
188
|
+
|
|
189
|
+
Config file: ${CONFIG_PATH}
|
|
190
|
+
The bot watches the current working directory's .orqlaude state.`);
|
|
191
|
+
}
|
|
192
|
+
async function cmdTgSetup() {
|
|
193
|
+
const rl = readline.createInterface({ input: stdin, output: stdout });
|
|
194
|
+
const cfg = await loadConfig();
|
|
195
|
+
console.log("Telegram bot setup.\n");
|
|
196
|
+
console.log("1. Open Telegram, search for @BotFather, create a new bot, and copy the token.\n");
|
|
197
|
+
const token = (await rl.question(`Bot token${cfg.botToken ? " (press enter to keep existing)" : ""}: `)).trim();
|
|
198
|
+
if (token)
|
|
199
|
+
cfg.botToken = token;
|
|
200
|
+
rl.close();
|
|
201
|
+
if (!cfg.botToken) {
|
|
202
|
+
console.error("No token provided.");
|
|
203
|
+
return 1;
|
|
204
|
+
}
|
|
205
|
+
// Verify
|
|
206
|
+
const api = new TelegramApi(cfg.botToken);
|
|
207
|
+
try {
|
|
208
|
+
const me = await api.getMe();
|
|
209
|
+
console.log(`\n✓ Verified: @${me.username} (bot id ${me.id})`);
|
|
210
|
+
}
|
|
211
|
+
catch (err) {
|
|
212
|
+
console.error(`Failed to verify token: ${err.message}`);
|
|
213
|
+
return 1;
|
|
214
|
+
}
|
|
215
|
+
await saveConfig(cfg);
|
|
216
|
+
console.log(`Saved to ${CONFIG_PATH} (mode 600).`);
|
|
217
|
+
console.log("\nNext: message your bot with /start in Telegram. It'll print your user id. Then run:");
|
|
218
|
+
console.log(` orqlaude tg whitelist <your_user_id> --owner`);
|
|
219
|
+
console.log(`Then:`);
|
|
220
|
+
console.log(` orqlaude tg start`);
|
|
221
|
+
return 0;
|
|
222
|
+
}
|
|
223
|
+
async function cmdTgShow() {
|
|
224
|
+
const cfg = await loadConfig();
|
|
225
|
+
console.log(`Telegram config (${CONFIG_PATH}):`);
|
|
226
|
+
console.log(` bot token: ${cfg.botToken ? cfg.botToken.slice(0, 8) + "…" : "(not set)"}`);
|
|
227
|
+
console.log(` owner: ${cfg.ownerId ?? "(none)"}`);
|
|
228
|
+
console.log(` whitelist: ${cfg.whitelist.length} user(s)`);
|
|
229
|
+
for (const w of cfg.whitelist) {
|
|
230
|
+
console.log(` - ${w.userId}${w.label ? ` (${w.label})` : ""}`);
|
|
231
|
+
}
|
|
232
|
+
return 0;
|
|
233
|
+
}
|
|
234
|
+
async function cmdTgWhitelist(args) {
|
|
235
|
+
const userId = Number(args[0]);
|
|
236
|
+
if (!Number.isFinite(userId)) {
|
|
237
|
+
console.error("usage: orqlaude tg whitelist <user_id> [--owner] [--label NAME]");
|
|
238
|
+
return 2;
|
|
239
|
+
}
|
|
240
|
+
const isOwner = args.includes("--owner");
|
|
241
|
+
const labelIdx = args.indexOf("--label");
|
|
242
|
+
const label = labelIdx !== -1 ? args[labelIdx + 1] : undefined;
|
|
243
|
+
const cfg = await loadConfig();
|
|
244
|
+
if (!cfg.whitelist.some((w) => w.userId === userId)) {
|
|
245
|
+
cfg.whitelist.push({ userId, chatId: userId, label });
|
|
246
|
+
}
|
|
247
|
+
if (isOwner)
|
|
248
|
+
cfg.ownerId = userId;
|
|
249
|
+
await saveConfig(cfg);
|
|
250
|
+
console.log(`✓ Whitelisted ${userId}${label ? ` (${label})` : ""}${isOwner ? " — set as owner" : ""}.`);
|
|
251
|
+
return 0;
|
|
252
|
+
}
|
|
253
|
+
async function cmdTgUnwhitelist(args) {
|
|
254
|
+
const userId = Number(args[0]);
|
|
255
|
+
if (!Number.isFinite(userId)) {
|
|
256
|
+
console.error("usage: orqlaude tg unwhitelist <user_id>");
|
|
257
|
+
return 2;
|
|
258
|
+
}
|
|
259
|
+
const cfg = await loadConfig();
|
|
260
|
+
cfg.whitelist = cfg.whitelist.filter((w) => w.userId !== userId);
|
|
261
|
+
if (cfg.ownerId === userId)
|
|
262
|
+
cfg.ownerId = null;
|
|
263
|
+
await saveConfig(cfg);
|
|
264
|
+
console.log(`✓ Removed ${userId}.`);
|
|
265
|
+
return 0;
|
|
266
|
+
}
|
|
267
|
+
async function cmdTgTest(args) {
|
|
268
|
+
const chatId = Number(args[0]);
|
|
269
|
+
if (!Number.isFinite(chatId)) {
|
|
270
|
+
console.error("usage: orqlaude tg test <chat_id>");
|
|
271
|
+
return 2;
|
|
272
|
+
}
|
|
273
|
+
const cfg = await loadConfig();
|
|
274
|
+
if (!cfg.botToken) {
|
|
275
|
+
console.error("No bot token. Run `orqlaude tg setup` first.");
|
|
276
|
+
return 1;
|
|
277
|
+
}
|
|
278
|
+
const api = new TelegramApi(cfg.botToken);
|
|
279
|
+
try {
|
|
280
|
+
await api.sendMessage(chatId, "🦾 orqlaude bot test — if you see this, the wiring works.");
|
|
281
|
+
console.log("✓ Sent.");
|
|
282
|
+
return 0;
|
|
283
|
+
}
|
|
284
|
+
catch (err) {
|
|
285
|
+
console.error(`Failed: ${err.message}`);
|
|
286
|
+
return 1;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
async function cmdTgStart() {
|
|
290
|
+
try {
|
|
291
|
+
await runBot(process.cwd());
|
|
292
|
+
return 0;
|
|
293
|
+
}
|
|
294
|
+
catch (err) {
|
|
295
|
+
console.error(err.message);
|
|
296
|
+
return 1;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// ---- helpers --------------------------------------------------------------
|
|
300
|
+
function requirePlan(plans, planId) {
|
|
301
|
+
const full = plans[planId] ?? Object.values(plans).find((p) => p.id.startsWith(planId));
|
|
302
|
+
if (!full)
|
|
303
|
+
throw new Error(`Plan not found: ${planId}`);
|
|
304
|
+
return full;
|
|
305
|
+
}
|
|
306
|
+
function parseLimit(args) {
|
|
307
|
+
const idx = args.indexOf("--limit");
|
|
308
|
+
if (idx === -1 || !args[idx + 1])
|
|
309
|
+
return 30;
|
|
310
|
+
const n = parseInt(args[idx + 1], 10);
|
|
311
|
+
return Number.isFinite(n) && n > 0 ? n : 30;
|
|
312
|
+
}
|
|
313
|
+
function truncate(s, n) {
|
|
314
|
+
if (s.length <= n)
|
|
315
|
+
return s;
|
|
316
|
+
return s.slice(0, n - 1) + "…";
|
|
317
|
+
}
|
|
318
|
+
main().then((code) => process.exit(code), (err) => {
|
|
319
|
+
console.error(err);
|
|
320
|
+
process.exit(1);
|
|
321
|
+
});
|
|
322
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,QAAQ,MAAM,wBAAwB,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAa,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C;;;;;;;GAOG;AAEH,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC,CAAC;AAE1F,KAAK,UAAU,IAAI;IACjB,MAAM,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7C,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,SAAS,CAAC;QACf,KAAK,MAAM,CAAC;QACZ,KAAK,IAAI,CAAC;QACV,KAAK,QAAQ;YACX,SAAS,EAAE,CAAC;YACZ,OAAO,CAAC,CAAC;QACX,KAAK,MAAM;YACT,OAAO,MAAM,OAAO,EAAE,CAAC;QACzB,KAAK,QAAQ;YACX,OAAO,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,KAAK,MAAM;YACT,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAChC,KAAK,SAAS;YACZ,OAAO,MAAM,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;QAC5C,KAAK,IAAI;YACP,OAAO,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3B;YACE,OAAO,CAAC,KAAK,CAAC,uBAAuB,GAAG,EAAE,CAAC,CAAC;YAC5C,SAAS,EAAE,CAAC;YACZ,OAAO,CAAC,CAAC;IACb,CAAC;AACH,CAAC;AAED,SAAS,SAAS;IAChB,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;aAiBD,SAAS,EAAE,CAAC,CAAC;AAC1B,CAAC;AAED,8EAA8E;AAE9E,KAAK,UAAU,OAAO;IACpB,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IACxG,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;QAC7C,OAAO,CAAC,CAAC;IACX,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;QAC/D,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,KAAK,YAAY,CAAC,CAAC,MAAM,CAAC;QAClG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,QAAQ,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,UAAU,CAAC,CAAC,CAAC,EAAE,OAAO,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;IACxJ,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,MAA0B;IACjD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;QAClD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,IAAU,CAAC;IACf,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC;QACtC,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IAC/B,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC9C,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,CAAC,eAAe,CAAC,cAAc,EAAE,YAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3H,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACvE,IAAI,IAAI,CAAC,UAAU;QAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAC7F,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACxB,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC3B,IAAI,KAAK,GAAG,EAAE,CAAC;QACf,IAAI,CAAC,CAAC,gBAAgB,EAAE,CAAC;YACvB,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,gBAAgB,CAAC,CAAC;YACtE,WAAW,IAAI,IAAI,CAAC,oBAAoB,CAAC;YACzC,KAAK,GAAG,KAAK,IAAI,CAAC,oBAAoB,CAAC,cAAc,EAAE,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,IAAI,IAAI,CAAC,aAAa,IAAI,EAAE,EAAE,CAAC;QAC5I,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC;QAC1E,IAAI,CAAC,CAAC,KAAK;YAAE,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,iBAAiB,WAAW,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;IACpE,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7E,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/E,OAAO,CAAC,CAAC;AACX,CAAC;AAED,KAAK,UAAU,OAAO,CAAC,MAA0B;IAC/C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAChD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;QACnE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3C,OAAO,CAAC,CAAC;IACX,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC;QACtC,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,KAAa;IACrC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,SAAS,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;QACrC,OAAO,CAAC,CAAC;IACX,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACtD,MAAM,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;QAClC,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9G,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC,CAAC;IACtK,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,8EAA8E;AAE9E,KAAK,UAAU,KAAK,CAAC,IAAc;IACjC,MAAM,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAC5B,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,SAAS,CAAC;QACf,KAAK,MAAM;YACT,WAAW,EAAE,CAAC;YACd,OAAO,CAAC,CAAC;QACX,KAAK,OAAO;YACV,OAAO,MAAM,UAAU,EAAE,CAAC;QAC5B,KAAK,MAAM;YACT,OAAO,MAAM,SAAS,EAAE,CAAC;QAC3B,KAAK,WAAW;YACd,OAAO,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;QACpC,KAAK,aAAa;YAChB,OAAO,MAAM,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACtC,KAAK,MAAM;YACT,OAAO,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;QAC/B,KAAK,OAAO;YACV,OAAO,MAAM,UAAU,EAAE,CAAC;QAC5B;YACE,OAAO,CAAC,KAAK,CAAC,0BAA0B,GAAG,EAAE,CAAC,CAAC;YAC/C,WAAW,EAAE,CAAC;YACd,OAAO,CAAC,CAAC;IACb,CAAC;AACH,CAAC;AAED,SAAS,WAAW;IAClB,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;eAUC,WAAW;iEACuC,CAAC,CAAC;AACnE,CAAC;AAED,KAAK,UAAU,UAAU;IACvB,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACtE,MAAM,GAAG,GAAG,MAAM,UAAU,EAAE,CAAC;IAC/B,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,kFAAkF,CAAC,CAAC;IAChG,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,iCAAiC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAChH,IAAI,KAAK;QAAE,GAAG,CAAC,QAAQ,GAAG,KAAK,CAAC;IAChC,EAAE,CAAC,KAAK,EAAE,CAAC;IACX,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;QACpC,OAAO,CAAC,CAAC;IACX,CAAC;IACD,SAAS;IACT,MAAM,GAAG,GAAG,IAAI,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC1C,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,CAAC,QAAQ,YAAY,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;IACjE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,2BAA4B,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACnE,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC;IACtB,OAAO,CAAC,GAAG,CAAC,YAAY,WAAW,cAAc,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC,uFAAuF,CAAC,CAAC;IACrG,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;IAC9D,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACrB,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IACnC,OAAO,CAAC,CAAC;AACX,CAAC;AAED,KAAK,UAAU,SAAS;IACtB,MAAM,GAAG,GAAG,MAAM,UAAU,EAAE,CAAC;IAC/B,OAAO,CAAC,GAAG,CAAC,oBAAoB,WAAW,IAAI,CAAC,CAAC;IACjD,OAAO,CAAC,GAAG,CAAC,iBAAiB,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5F,OAAO,CAAC,GAAG,CAAC,iBAAiB,GAAG,CAAC,OAAO,IAAI,QAAQ,EAAE,CAAC,CAAC;IACxD,OAAO,CAAC,GAAG,CAAC,iBAAiB,GAAG,CAAC,SAAS,CAAC,MAAM,UAAU,CAAC,CAAC;IAC7D,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,IAAc;IAC1C,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7B,OAAO,CAAC,KAAK,CAAC,iEAAiE,CAAC,CAAC;QACjF,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/D,MAAM,GAAG,GAAG,MAAM,UAAU,EAAE,CAAC;IAC/B,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,EAAE,CAAC;QACpD,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,IAAI,OAAO;QAAE,GAAG,CAAC,OAAO,GAAG,MAAM,CAAC;IAClC,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC;IACtB,OAAO,CAAC,GAAG,CAAC,iBAAiB,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACxG,OAAO,CAAC,CAAC;AACX,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,IAAc;IAC5C,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7B,OAAO,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAC1D,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,UAAU,EAAE,CAAC;IAC/B,GAAG,CAAC,SAAS,GAAG,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;IACjE,IAAI,GAAG,CAAC,OAAO,KAAK,MAAM;QAAE,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC;IAC/C,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC;IACtB,OAAO,CAAC,GAAG,CAAC,aAAa,MAAM,GAAG,CAAC,CAAC;IACpC,OAAO,CAAC,CAAC;AACX,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,IAAc;IACrC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7B,OAAO,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;QACnD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,UAAU,EAAE,CAAC;IAC/B,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;QAC9D,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC1C,IAAI,CAAC;QACH,MAAM,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,2DAA2D,CAAC,CAAC;QAC3F,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvB,OAAO,CAAC,CAAC;IACX,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,WAAY,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACnD,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED,KAAK,UAAU,UAAU;IACvB,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QAC5B,OAAO,CAAC,CAAC;IACX,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC;QACtC,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED,8EAA8E;AAE9E,SAAS,WAAW,CAAC,KAA2B,EAAE,MAAc;IAC9D,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IACxF,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,MAAM,EAAE,CAAC,CAAC;IACxD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,UAAU,CAAC,IAAc;IAChC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACpC,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAC5C,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACtC,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9C,CAAC;AAED,SAAS,QAAQ,CAAC,CAAS,EAAE,CAAS;IACpC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IAC5B,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;AACjC,CAAC;AAED,IAAI,EAAE,CAAC,IAAI,CACT,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAC5B,CAAC,GAAG,EAAE,EAAE;IACN,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CACF,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Append-only audit log. Every MCP tool invocation writes one line.
|
|
3
|
+
*
|
|
4
|
+
* Format: JSONL — each line is { ts, tool, args, result_summary, ok, error?, plan_id? }
|
|
5
|
+
*
|
|
6
|
+
* Why append-only and not part of the state file: the state file is small and
|
|
7
|
+
* frequently rewritten (atomic temp+rename); folding audit events into it
|
|
8
|
+
* would churn it constantly. The audit log is unbounded and naturally
|
|
9
|
+
* line-oriented, so JSONL append is the right primitive.
|
|
10
|
+
*
|
|
11
|
+
* Inspect with: `tail -f .orqlaude/audit.jsonl | jq`
|
|
12
|
+
*/
|
|
13
|
+
export interface AuditEvent {
|
|
14
|
+
ts: number;
|
|
15
|
+
tool: string;
|
|
16
|
+
args: unknown;
|
|
17
|
+
ok: boolean;
|
|
18
|
+
durationMs: number;
|
|
19
|
+
resultSummary?: string;
|
|
20
|
+
error?: string;
|
|
21
|
+
planId?: string;
|
|
22
|
+
sessionId?: string;
|
|
23
|
+
}
|
|
24
|
+
export declare class AuditLog {
|
|
25
|
+
private filePath;
|
|
26
|
+
private writeQueue;
|
|
27
|
+
constructor(stateDir: string);
|
|
28
|
+
append(evt: AuditEvent): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Wrap a tool handler so every invocation is audited. Captures duration,
|
|
31
|
+
* success/failure, and a short summary of the result.
|
|
32
|
+
*/
|
|
33
|
+
wrap<TArgs extends Record<string, unknown>, TResult>(toolName: string, handler: (args: TArgs) => Promise<TResult>, extractIds?: (args: TArgs, result?: TResult) => {
|
|
34
|
+
planId?: string;
|
|
35
|
+
sessionId?: string;
|
|
36
|
+
}): (args: TArgs) => Promise<TResult>;
|
|
37
|
+
tail(limit?: number): Promise<AuditEvent[]>;
|
|
38
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export class AuditLog {
|
|
4
|
+
filePath;
|
|
5
|
+
writeQueue = Promise.resolve();
|
|
6
|
+
constructor(stateDir) {
|
|
7
|
+
this.filePath = path.join(stateDir, "audit.jsonl");
|
|
8
|
+
}
|
|
9
|
+
async append(evt) {
|
|
10
|
+
// Serialize writes to avoid interleaving from concurrent tool calls.
|
|
11
|
+
this.writeQueue = this.writeQueue.then(async () => {
|
|
12
|
+
try {
|
|
13
|
+
await fs.mkdir(path.dirname(this.filePath), { recursive: true });
|
|
14
|
+
await fs.appendFile(this.filePath, JSON.stringify(evt) + "\n");
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
// Best-effort: never fail a tool call because audit logging failed.
|
|
18
|
+
// The error message goes to stderr where MCP debug surfaces it.
|
|
19
|
+
process.stderr.write(`[orqlaude audit] write failed: ${err.message}\n`);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
return this.writeQueue;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Wrap a tool handler so every invocation is audited. Captures duration,
|
|
26
|
+
* success/failure, and a short summary of the result.
|
|
27
|
+
*/
|
|
28
|
+
wrap(toolName, handler, extractIds) {
|
|
29
|
+
return async (args) => {
|
|
30
|
+
const started = Date.now();
|
|
31
|
+
try {
|
|
32
|
+
const result = await handler(args);
|
|
33
|
+
const ids = extractIds?.(args, result) ?? {};
|
|
34
|
+
await this.append({
|
|
35
|
+
ts: started,
|
|
36
|
+
tool: toolName,
|
|
37
|
+
args: redactSecrets(args),
|
|
38
|
+
ok: true,
|
|
39
|
+
durationMs: Date.now() - started,
|
|
40
|
+
resultSummary: summarize(result),
|
|
41
|
+
...ids,
|
|
42
|
+
});
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
const ids = extractIds?.(args) ?? {};
|
|
47
|
+
await this.append({
|
|
48
|
+
ts: started,
|
|
49
|
+
tool: toolName,
|
|
50
|
+
args: redactSecrets(args),
|
|
51
|
+
ok: false,
|
|
52
|
+
durationMs: Date.now() - started,
|
|
53
|
+
error: err instanceof Error ? err.message : String(err),
|
|
54
|
+
...ids,
|
|
55
|
+
});
|
|
56
|
+
throw err;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async tail(limit = 50) {
|
|
61
|
+
try {
|
|
62
|
+
const raw = await fs.readFile(this.filePath, "utf8");
|
|
63
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
64
|
+
return lines
|
|
65
|
+
.slice(-limit)
|
|
66
|
+
.map((l) => {
|
|
67
|
+
try {
|
|
68
|
+
return JSON.parse(l);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
.filter((e) => e !== null);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
if (err.code === "ENOENT")
|
|
78
|
+
return [];
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function summarize(result) {
|
|
84
|
+
// MCP tool results are typically { content: [{ type: "text", text: "..." }] }.
|
|
85
|
+
// Extract the first text and truncate.
|
|
86
|
+
try {
|
|
87
|
+
const r = result;
|
|
88
|
+
const text = r?.content?.[0]?.text;
|
|
89
|
+
if (typeof text === "string")
|
|
90
|
+
return text.slice(0, 200);
|
|
91
|
+
return JSON.stringify(result).slice(0, 200);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return String(result).slice(0, 200);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function redactSecrets(args) {
|
|
98
|
+
// Approval tokens are single-use, but we still don't want them sitting in
|
|
99
|
+
// the audit log forever.
|
|
100
|
+
if (args && typeof args === "object") {
|
|
101
|
+
const clone = { ...args };
|
|
102
|
+
if ("approval_token" in clone)
|
|
103
|
+
clone.approval_token = "<redacted>";
|
|
104
|
+
return clone;
|
|
105
|
+
}
|
|
106
|
+
return args;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/lib/audit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { MODEL_PRICING } from "./pricing.js";
|
|
2
|
+
export interface DailyTokenUsage {
|
|
3
|
+
date: string;
|
|
4
|
+
tokens: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Read the Desktop app's daily token tally. Returns null if file missing
|
|
8
|
+
* (e.g. user isn't on the Desktop app). Safe to call repeatedly; the file is
|
|
9
|
+
* small and disk-resident.
|
|
10
|
+
*/
|
|
11
|
+
export declare function readDailyTokenUsage(): Promise<DailyTokenUsage | null>;
|
|
12
|
+
/**
|
|
13
|
+
* Rough token estimate per agent for a sub-task. Mirrors estimateAgentCost
|
|
14
|
+
* but in tokens so it composes with a token-based cap.
|
|
15
|
+
*
|
|
16
|
+
* Baselines tuned to one Haiku probe (~30k cache-creation + ~50k cache-read
|
|
17
|
+
* over the run + ~4k output ≈ 84k effective tokens). Tune as we collect more.
|
|
18
|
+
*/
|
|
19
|
+
export declare function estimateAgentTokens(effortMultiplier?: number): {
|
|
20
|
+
inputTokens: number;
|
|
21
|
+
cacheReadTokens: number;
|
|
22
|
+
cacheCreationTokens: number;
|
|
23
|
+
outputTokens: number;
|
|
24
|
+
totalEffective: number;
|
|
25
|
+
};
|
|
26
|
+
/** Convenience: compute USD cost AND token estimate together. */
|
|
27
|
+
export declare function estimateAgent(model: string, effortMultiplier?: number): {
|
|
28
|
+
tokens: {
|
|
29
|
+
inputTokens: number;
|
|
30
|
+
cacheReadTokens: number;
|
|
31
|
+
cacheCreationTokens: number;
|
|
32
|
+
outputTokens: number;
|
|
33
|
+
totalEffective: number;
|
|
34
|
+
};
|
|
35
|
+
costUsd: number;
|
|
36
|
+
};
|
|
37
|
+
export { MODEL_PRICING };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { MODEL_PRICING, estimateAgentCost } from "./pricing.js";
|
|
4
|
+
/**
|
|
5
|
+
* Token-based budgeting (Max x20 friendly).
|
|
6
|
+
*
|
|
7
|
+
* Max-plan users care about their daily/weekly token bucket, not USD. The
|
|
8
|
+
* Claude Desktop app already tracks daily usage in:
|
|
9
|
+
* ~/Library/Application Support/Claude/buddy-tokens.json
|
|
10
|
+
*
|
|
11
|
+
* We read that file to surface remaining quota, and we let the user set a
|
|
12
|
+
* per-fleet token cap. USD remains tracked (and shown informationally) because
|
|
13
|
+
* it's a useful sanity check for runaway agents and because not every user is
|
|
14
|
+
* on Max — but token caps are the primary enforcement mechanism.
|
|
15
|
+
*/
|
|
16
|
+
const HOME = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
17
|
+
const BUDDY_TOKENS_PATH = path.join(HOME, "Library", "Application Support", "Claude", "buddy-tokens.json");
|
|
18
|
+
/**
|
|
19
|
+
* Read the Desktop app's daily token tally. Returns null if file missing
|
|
20
|
+
* (e.g. user isn't on the Desktop app). Safe to call repeatedly; the file is
|
|
21
|
+
* small and disk-resident.
|
|
22
|
+
*/
|
|
23
|
+
export async function readDailyTokenUsage() {
|
|
24
|
+
try {
|
|
25
|
+
const raw = await fs.readFile(BUDDY_TOKENS_PATH, "utf8");
|
|
26
|
+
const parsed = JSON.parse(raw);
|
|
27
|
+
return parsed["tokens-today"] ?? null;
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
if (err.code === "ENOENT")
|
|
31
|
+
return null;
|
|
32
|
+
throw err;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Rough token estimate per agent for a sub-task. Mirrors estimateAgentCost
|
|
37
|
+
* but in tokens so it composes with a token-based cap.
|
|
38
|
+
*
|
|
39
|
+
* Baselines tuned to one Haiku probe (~30k cache-creation + ~50k cache-read
|
|
40
|
+
* over the run + ~4k output ≈ 84k effective tokens). Tune as we collect more.
|
|
41
|
+
*/
|
|
42
|
+
export function estimateAgentTokens(effortMultiplier = 1) {
|
|
43
|
+
const e = effortMultiplier;
|
|
44
|
+
const cacheCreate = Math.round(30_000 * e);
|
|
45
|
+
const input = Math.round(1_000 * e);
|
|
46
|
+
const cacheRead = Math.round(50_000 * e);
|
|
47
|
+
const output = Math.round(4_000 * e);
|
|
48
|
+
// "totalEffective" approximates buddy-bucket consumption; the Desktop app's
|
|
49
|
+
// accounting collapses everything into one counter so we sum without
|
|
50
|
+
// weighting.
|
|
51
|
+
return {
|
|
52
|
+
inputTokens: input,
|
|
53
|
+
cacheReadTokens: cacheRead,
|
|
54
|
+
cacheCreationTokens: cacheCreate,
|
|
55
|
+
outputTokens: output,
|
|
56
|
+
totalEffective: input + cacheRead + cacheCreate + output,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** Convenience: compute USD cost AND token estimate together. */
|
|
60
|
+
export function estimateAgent(model, effortMultiplier = 1) {
|
|
61
|
+
return {
|
|
62
|
+
tokens: estimateAgentTokens(effortMultiplier),
|
|
63
|
+
costUsd: estimateAgentCost(model, effortMultiplier),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export { MODEL_PRICING };
|
|
67
|
+
//# sourceMappingURL=budgeting.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budgeting.js","sourceRoot":"","sources":["../../src/lib/budgeting.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEhE;;;;;;;;;;;GAWG;AAEH,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC;AAC/D,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CACjC,IAAI,EACJ,SAAS,EACT,qBAAqB,EACrB,QAAQ,EACR,mBAAmB,CACpB,CAAC;AAOF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACvC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;QACzD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAyC,CAAC;QACvE,OAAO,MAAM,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC;IACxC,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QACvC,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,gBAAgB,GAAG,CAAC;IAOtD,MAAM,CAAC,GAAG,gBAAgB,CAAC;IAC3B,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACpC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACrC,4EAA4E;IAC5E,qEAAqE;IACrE,aAAa;IACb,OAAO;QACL,WAAW,EAAE,KAAK;QAClB,eAAe,EAAE,SAAS;QAC1B,mBAAmB,EAAE,WAAW;QAChC,YAAY,EAAE,MAAM;QACpB,cAAc,EAAE,KAAK,GAAG,SAAS,GAAG,WAAW,GAAG,MAAM;KACzD,CAAC;AACJ,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,aAAa,CAAC,KAAa,EAAE,gBAAgB,GAAG,CAAC;IAC/D,OAAO;QACL,MAAM,EAAE,mBAAmB,CAAC,gBAAgB,CAAC;QAC7C,OAAO,EAAE,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,CAAC;KACpD,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { SessionSnapshot } from "./jsonl_tail.js";
|
|
2
|
+
/**
|
|
3
|
+
* Hallucination detection for spawned agents.
|
|
4
|
+
*
|
|
5
|
+
* Two cheap deterministic checks, summed into a score. Higher score = more
|
|
6
|
+
* concerning. The primary Claude is told the score in `status()` and can
|
|
7
|
+
* decide whether to send a STOP message or just nudge.
|
|
8
|
+
*
|
|
9
|
+
* Check 1 — path-existence: scan recent tool_use entries for file paths
|
|
10
|
+
* (Read/Edit/Write inputs). If many reference paths that don't exist in the
|
|
11
|
+
* worktree, the agent is editing imaginary files.
|
|
12
|
+
*
|
|
13
|
+
* Check 2 — tool-pattern sanity:
|
|
14
|
+
* • commits without any prior Read on the file being edited
|
|
15
|
+
* • Write of a file that already exists (often an Edit-vs-Write mistake)
|
|
16
|
+
* • repeated identical tool calls (loop)
|
|
17
|
+
* • Edit failing repeatedly with "no match" (agent is guessing at the content)
|
|
18
|
+
*
|
|
19
|
+
* These are deliberately conservative. False positives are acceptable; we
|
|
20
|
+
* surface concerns, we don't kill agents automatically.
|
|
21
|
+
*
|
|
22
|
+
* NOTE: a future iteration can add a Check 3 — second-model cross-validation:
|
|
23
|
+
* periodically have a Haiku read the recent activity and flag suspect turns.
|
|
24
|
+
* That costs tokens though, so it's opt-in.
|
|
25
|
+
*/
|
|
26
|
+
export interface HallucinationReport {
|
|
27
|
+
score: number;
|
|
28
|
+
level: "clean" | "minor" | "moderate" | "severe";
|
|
29
|
+
concerns: string[];
|
|
30
|
+
}
|
|
31
|
+
interface ToolUseEvent {
|
|
32
|
+
name: string;
|
|
33
|
+
input: any;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Score a session by inspecting its full set of tool_use events (extracted by
|
|
37
|
+
* the caller from the JSONL). cwd is the worktree root to check paths against.
|
|
38
|
+
*/
|
|
39
|
+
export declare function detectHallucination(toolUses: ToolUseEvent[], cwd: string): Promise<HallucinationReport>;
|
|
40
|
+
/**
|
|
41
|
+
* Extract all tool_use events from a JSONL-derived snapshot's raw lines.
|
|
42
|
+
* The snapshot module currently only keeps the *last* tool_use; for
|
|
43
|
+
* hallucination detection we need the full history. This re-reads the JSONL.
|
|
44
|
+
*/
|
|
45
|
+
export declare function extractToolUses(jsonlPath: string): Promise<ToolUseEvent[]>;
|
|
46
|
+
export type { SessionSnapshot };
|