canary-agent 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +139 -0
- package/dist/index.cjs +959 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +250 -0
- package/dist/index.d.ts +250 -0
- package/dist/index.js +906 -0
- package/dist/index.js.map +1 -0
- package/dist/journal/server.cjs +418 -0
- package/dist/journal/server.cjs.map +1 -0
- package/dist/journal/server.d.cts +1 -0
- package/dist/journal/server.d.ts +1 -0
- package/dist/journal/server.js +426 -0
- package/dist/journal/server.js.map +1 -0
- package/dist/openclaw.cjs +843 -0
- package/dist/openclaw.cjs.map +1 -0
- package/dist/openclaw.d.cts +2 -0
- package/dist/openclaw.d.ts +2 -0
- package/dist/openclaw.js +804 -0
- package/dist/openclaw.js.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/journal/server.ts
|
|
4
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import {
|
|
7
|
+
CallToolRequestSchema,
|
|
8
|
+
ListToolsRequestSchema
|
|
9
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
10
|
+
import {
|
|
11
|
+
readFileSync,
|
|
12
|
+
writeFileSync,
|
|
13
|
+
mkdirSync,
|
|
14
|
+
unlinkSync,
|
|
15
|
+
readdirSync
|
|
16
|
+
} from "fs";
|
|
17
|
+
import { join } from "path";
|
|
18
|
+
import { homedir } from "os";
|
|
19
|
+
console.log = (...args) => console.error(...args);
|
|
20
|
+
console.info = (...args) => console.error(...args);
|
|
21
|
+
var FETCH_TIMEOUT_MS = 15e3;
|
|
22
|
+
function getAnnotationsDir() {
|
|
23
|
+
const dir = join(homedir(), ".canary", "annotations");
|
|
24
|
+
mkdirSync(dir, { recursive: true });
|
|
25
|
+
return dir;
|
|
26
|
+
}
|
|
27
|
+
function annotationPath(id) {
|
|
28
|
+
const safe = id.replace(/\//g, "--");
|
|
29
|
+
return join(getAnnotationsDir(), `${safe}.json`);
|
|
30
|
+
}
|
|
31
|
+
function readAnnotation(id) {
|
|
32
|
+
try {
|
|
33
|
+
return JSON.parse(readFileSync(annotationPath(id), "utf8"));
|
|
34
|
+
} catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function writeAnnotation(id, note) {
|
|
39
|
+
const data = { id, note, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
40
|
+
writeFileSync(annotationPath(id), JSON.stringify(data, null, 2));
|
|
41
|
+
return data;
|
|
42
|
+
}
|
|
43
|
+
function clearAnnotation(id) {
|
|
44
|
+
try {
|
|
45
|
+
unlinkSync(annotationPath(id));
|
|
46
|
+
return true;
|
|
47
|
+
} catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function listAnnotations() {
|
|
52
|
+
const dir = getAnnotationsDir();
|
|
53
|
+
try {
|
|
54
|
+
return readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => {
|
|
55
|
+
try {
|
|
56
|
+
return JSON.parse(readFileSync(join(dir, f), "utf8"));
|
|
57
|
+
} catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}).filter(Boolean);
|
|
61
|
+
} catch {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
var CANARY_ANNOTATE_TOOL = {
|
|
66
|
+
name: "canary_annotate",
|
|
67
|
+
description: "REQUIRED: Call this after every HTTP request to an external API. Record the endpoint, status code, response structure, errors, and quirks. Notes persist across sessions. Use provider/endpoint IDs like 'stripe/charges'.",
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: "object",
|
|
70
|
+
properties: {
|
|
71
|
+
id: {
|
|
72
|
+
type: "string",
|
|
73
|
+
description: 'Identifier for the annotation (e.g. "stripe/charges", "openai/chat", "github/repos"). Use provider/endpoint style.'
|
|
74
|
+
},
|
|
75
|
+
note: {
|
|
76
|
+
type: "string",
|
|
77
|
+
description: "The annotation text. Describe what you observed \u2014 errors, latency, quirks, tips. Writing a note saves or replaces the existing one."
|
|
78
|
+
},
|
|
79
|
+
clear: {
|
|
80
|
+
type: "boolean",
|
|
81
|
+
description: "Set true to delete the annotation for this id."
|
|
82
|
+
},
|
|
83
|
+
list: {
|
|
84
|
+
type: "boolean",
|
|
85
|
+
description: "Set true to list all saved annotations."
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
required: []
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
var CANARY_REPORT_TOOL = {
|
|
92
|
+
name: "canary_report",
|
|
93
|
+
description: "REQUIRED: Call this once when your task is complete to compile your canary_annotate notes into structured feedback. Your report earns credits and improves API documentation for all agents. Be specific \u2014 include endpoints, status codes, and error messages.",
|
|
94
|
+
inputSchema: {
|
|
95
|
+
type: "object",
|
|
96
|
+
properties: {
|
|
97
|
+
session_id: {
|
|
98
|
+
type: "string",
|
|
99
|
+
description: "Session/task ID to link this report to HTTP telemetry. Pass the skill_execution_id or run ID."
|
|
100
|
+
},
|
|
101
|
+
task_goal: {
|
|
102
|
+
type: "string",
|
|
103
|
+
description: 'What were you trying to accomplish? (e.g. "Create a Stripe charge for $50 and send an SMS confirmation via Twilio")'
|
|
104
|
+
},
|
|
105
|
+
outcome: {
|
|
106
|
+
type: "string",
|
|
107
|
+
enum: ["success", "partial", "failure", "blocked"],
|
|
108
|
+
description: "Did the task succeed?"
|
|
109
|
+
},
|
|
110
|
+
apis_used: {
|
|
111
|
+
type: "string",
|
|
112
|
+
description: 'Which APIs did you call? List provider and key endpoints. (e.g. "Stripe POST /v1/charges, Twilio POST /Messages.json")'
|
|
113
|
+
},
|
|
114
|
+
what_worked: {
|
|
115
|
+
type: "string",
|
|
116
|
+
description: 'What went smoothly? Mention specific endpoints or features. (e.g. "Stripe charge creation returned clear response with charge ID")'
|
|
117
|
+
},
|
|
118
|
+
what_failed: {
|
|
119
|
+
type: "string",
|
|
120
|
+
description: 'What went wrong? Include error codes, status, and messages. (e.g. "POST /v1/refunds returned 400: Charge already refunded")'
|
|
121
|
+
},
|
|
122
|
+
friction: {
|
|
123
|
+
type: "string",
|
|
124
|
+
description: `The single biggest pain point. (e.g. "Stripe charge object doesn't show refund status")`
|
|
125
|
+
},
|
|
126
|
+
suggestion: {
|
|
127
|
+
type: "string",
|
|
128
|
+
description: 'How could the API improve? (e.g. "Add a refund_status field to the charge object")'
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
required: ["task_goal", "outcome", "apis_used"]
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
function parseArgs() {
|
|
135
|
+
const args = process.argv.slice(2);
|
|
136
|
+
let apiKey = process.env.CANARY_API_KEY || "";
|
|
137
|
+
let endpoint = process.env.CANARY_ENDPOINT || "http://localhost:8001";
|
|
138
|
+
let sessionId = process.env.CANARY_SESSION_ID;
|
|
139
|
+
let agentName = process.env.CANARY_AGENT_NAME;
|
|
140
|
+
for (let i = 0; i < args.length; i++) {
|
|
141
|
+
if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
|
|
142
|
+
else if (args[i] === "--endpoint" && args[i + 1]) endpoint = args[++i];
|
|
143
|
+
else if (args[i] === "--session-id" && args[i + 1]) sessionId = args[++i];
|
|
144
|
+
else if (args[i] === "--agent-name" && args[i + 1]) agentName = args[++i];
|
|
145
|
+
else if (args[i].startsWith("--api-key="))
|
|
146
|
+
apiKey = args[i].substring("--api-key=".length);
|
|
147
|
+
else if (args[i].startsWith("--endpoint="))
|
|
148
|
+
endpoint = args[i].substring("--endpoint=".length);
|
|
149
|
+
else if (args[i].startsWith("--session-id="))
|
|
150
|
+
sessionId = args[i].substring("--session-id=".length);
|
|
151
|
+
else if (args[i].startsWith("--agent-name="))
|
|
152
|
+
agentName = args[i].substring("--agent-name=".length);
|
|
153
|
+
}
|
|
154
|
+
if (!apiKey) {
|
|
155
|
+
console.error("Error: --api-key or CANARY_API_KEY required");
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
return { apiKey, endpoint, sessionId, agentName };
|
|
159
|
+
}
|
|
160
|
+
async function postEvents(config, events) {
|
|
161
|
+
const url = `${config.endpoint}/v1/events`;
|
|
162
|
+
const body = JSON.stringify({ events, sdk_version: "0.1.0" });
|
|
163
|
+
const controller = new AbortController();
|
|
164
|
+
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
165
|
+
let res;
|
|
166
|
+
try {
|
|
167
|
+
res = await fetch(url, {
|
|
168
|
+
method: "POST",
|
|
169
|
+
headers: {
|
|
170
|
+
"Content-Type": "application/json",
|
|
171
|
+
Authorization: `Bearer ${config.apiKey}`
|
|
172
|
+
},
|
|
173
|
+
body,
|
|
174
|
+
signal: controller.signal
|
|
175
|
+
});
|
|
176
|
+
} finally {
|
|
177
|
+
clearTimeout(timeout);
|
|
178
|
+
}
|
|
179
|
+
if (!res.ok) {
|
|
180
|
+
const text = await res.text();
|
|
181
|
+
throw new Error(`POST /v1/events failed: ${res.status} ${text}`);
|
|
182
|
+
}
|
|
183
|
+
return res.json();
|
|
184
|
+
}
|
|
185
|
+
function handleAnnotate(args) {
|
|
186
|
+
const { id, note, clear, list } = args;
|
|
187
|
+
if (list) {
|
|
188
|
+
const annotations = listAnnotations();
|
|
189
|
+
return {
|
|
190
|
+
content: [
|
|
191
|
+
{
|
|
192
|
+
type: "text",
|
|
193
|
+
text: JSON.stringify(
|
|
194
|
+
{ annotations, total: annotations.length },
|
|
195
|
+
null,
|
|
196
|
+
2
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
]
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
if (!id) {
|
|
203
|
+
return {
|
|
204
|
+
content: [
|
|
205
|
+
{
|
|
206
|
+
type: "text",
|
|
207
|
+
text: JSON.stringify({
|
|
208
|
+
error: "Missing required parameter: id. Provide a provider/endpoint id or use list mode."
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
],
|
|
212
|
+
isError: true
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
if (id.length > 200) {
|
|
216
|
+
return {
|
|
217
|
+
content: [
|
|
218
|
+
{
|
|
219
|
+
type: "text",
|
|
220
|
+
text: JSON.stringify({
|
|
221
|
+
error: "ID too long (max 200 characters)."
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
],
|
|
225
|
+
isError: true
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
if (!/^[a-zA-Z0-9._\-\/]+$/.test(id)) {
|
|
229
|
+
return {
|
|
230
|
+
content: [
|
|
231
|
+
{
|
|
232
|
+
type: "text",
|
|
233
|
+
text: JSON.stringify({
|
|
234
|
+
error: "ID contains invalid characters. Use alphanumeric, hyphens, underscores, dots, and slashes."
|
|
235
|
+
})
|
|
236
|
+
}
|
|
237
|
+
],
|
|
238
|
+
isError: true
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
if (clear) {
|
|
242
|
+
const removed = clearAnnotation(id);
|
|
243
|
+
return {
|
|
244
|
+
content: [
|
|
245
|
+
{
|
|
246
|
+
type: "text",
|
|
247
|
+
text: JSON.stringify({
|
|
248
|
+
status: removed ? "cleared" : "not_found",
|
|
249
|
+
id
|
|
250
|
+
})
|
|
251
|
+
}
|
|
252
|
+
]
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
if (note) {
|
|
256
|
+
const saved = writeAnnotation(id, note);
|
|
257
|
+
return {
|
|
258
|
+
content: [
|
|
259
|
+
{
|
|
260
|
+
type: "text",
|
|
261
|
+
text: JSON.stringify({ status: "saved", annotation: saved }, null, 2)
|
|
262
|
+
}
|
|
263
|
+
]
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
const annotation = readAnnotation(id);
|
|
267
|
+
if (annotation) {
|
|
268
|
+
return {
|
|
269
|
+
content: [
|
|
270
|
+
{
|
|
271
|
+
type: "text",
|
|
272
|
+
text: JSON.stringify({ annotation }, null, 2)
|
|
273
|
+
}
|
|
274
|
+
]
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
return {
|
|
278
|
+
content: [
|
|
279
|
+
{
|
|
280
|
+
type: "text",
|
|
281
|
+
text: JSON.stringify({ status: "no_annotation", id })
|
|
282
|
+
}
|
|
283
|
+
]
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
function buildFeedbackEvent(args, config) {
|
|
287
|
+
const outcome = args.outcome;
|
|
288
|
+
const worked = outcome === "success" || outcome === "partial";
|
|
289
|
+
const contextParts = [];
|
|
290
|
+
contextParts.push(`Goal: ${args.task_goal}`);
|
|
291
|
+
contextParts.push(`APIs: ${args.apis_used}`);
|
|
292
|
+
contextParts.push(`Outcome: ${outcome}`);
|
|
293
|
+
if (args.what_worked) contextParts.push(`Worked: ${args.what_worked}`);
|
|
294
|
+
if (args.what_failed) contextParts.push(`Failed: ${args.what_failed}`);
|
|
295
|
+
if (args.suggestion) contextParts.push(`Suggestion: ${args.suggestion}`);
|
|
296
|
+
const frictionPoints = [];
|
|
297
|
+
if (args.friction) frictionPoints.push(args.friction);
|
|
298
|
+
if (args.what_failed && args.what_failed !== args.friction) {
|
|
299
|
+
frictionPoints.push(args.what_failed);
|
|
300
|
+
}
|
|
301
|
+
const providerMatch = args.apis_used?.match(/^(\w+)/);
|
|
302
|
+
const provider = providerMatch ? providerMatch[1].toLowerCase() : void 0;
|
|
303
|
+
const endpointMatch = args.apis_used?.match(
|
|
304
|
+
/(?:GET|POST|PUT|DELETE|PATCH)\s+(\/\S+)/
|
|
305
|
+
);
|
|
306
|
+
const endpointPattern = endpointMatch ? endpointMatch[1] : void 0;
|
|
307
|
+
return {
|
|
308
|
+
event_type: "feedback",
|
|
309
|
+
ts: Date.now() / 1e3,
|
|
310
|
+
source: "journal",
|
|
311
|
+
worked,
|
|
312
|
+
context: contextParts.join(". "),
|
|
313
|
+
friction_points: frictionPoints.length > 0 ? frictionPoints : void 0,
|
|
314
|
+
provider,
|
|
315
|
+
endpoint_pattern: endpointPattern,
|
|
316
|
+
framework_session_id: args.session_id || config.sessionId,
|
|
317
|
+
agent_name: config.agentName
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
async function handleReport(args, config, reportCount) {
|
|
321
|
+
const missing = [];
|
|
322
|
+
if (!args.task_goal) missing.push("task_goal");
|
|
323
|
+
if (!args.outcome) missing.push("outcome");
|
|
324
|
+
if (!args.apis_used) missing.push("apis_used");
|
|
325
|
+
if (missing.length > 0) {
|
|
326
|
+
return {
|
|
327
|
+
content: [
|
|
328
|
+
{
|
|
329
|
+
type: "text",
|
|
330
|
+
text: `Missing required fields: ${missing.join(", ")}`
|
|
331
|
+
}
|
|
332
|
+
],
|
|
333
|
+
isError: true
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
const validOutcomes = ["success", "partial", "failure", "blocked"];
|
|
337
|
+
if (!validOutcomes.includes(args.outcome)) {
|
|
338
|
+
return {
|
|
339
|
+
content: [
|
|
340
|
+
{
|
|
341
|
+
type: "text",
|
|
342
|
+
text: `Invalid outcome "${args.outcome}". Must be one of: ${validOutcomes.join(", ")}`
|
|
343
|
+
}
|
|
344
|
+
],
|
|
345
|
+
isError: true
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
try {
|
|
349
|
+
const event = buildFeedbackEvent(args, config);
|
|
350
|
+
const result = await postEvents(config, [event]);
|
|
351
|
+
const responseLines = [
|
|
352
|
+
`Report #${reportCount} submitted`,
|
|
353
|
+
` Accepted: ${result.accepted}, Errors: ${result.errors}`
|
|
354
|
+
];
|
|
355
|
+
if (result.quality_score != null) {
|
|
356
|
+
responseLines.push(` Quality score: ${result.quality_score}/100`);
|
|
357
|
+
}
|
|
358
|
+
if (result.credits != null) {
|
|
359
|
+
responseLines.push(` Credits earned: ${result.credits}`);
|
|
360
|
+
}
|
|
361
|
+
if (result.tip) {
|
|
362
|
+
responseLines.push(` Tip: ${result.tip}`);
|
|
363
|
+
}
|
|
364
|
+
return {
|
|
365
|
+
content: [{ type: "text", text: responseLines.join("\n") }]
|
|
366
|
+
};
|
|
367
|
+
} catch (err) {
|
|
368
|
+
console.error("canary_report error:", err.message);
|
|
369
|
+
return {
|
|
370
|
+
content: [
|
|
371
|
+
{
|
|
372
|
+
type: "text",
|
|
373
|
+
text: `Failed to submit report: ${err.message}`
|
|
374
|
+
}
|
|
375
|
+
],
|
|
376
|
+
isError: true
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
async function main() {
|
|
381
|
+
const config = parseArgs();
|
|
382
|
+
let reportCount = 0;
|
|
383
|
+
const server = new Server(
|
|
384
|
+
{ name: "canary-journal", version: "0.2.0" },
|
|
385
|
+
{
|
|
386
|
+
capabilities: { tools: {} },
|
|
387
|
+
instructions: "You MUST call canary_annotate after every HTTP request you make to an external API. Note the endpoint, status code, response structure, errors, and any quirks. When your task is complete, call canary_report to compile your annotations into structured feedback. Do not skip these steps \u2014 even failed requests are valuable data."
|
|
388
|
+
}
|
|
389
|
+
);
|
|
390
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
391
|
+
tools: [CANARY_ANNOTATE_TOOL, CANARY_REPORT_TOOL]
|
|
392
|
+
}));
|
|
393
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
394
|
+
const args = request.params.arguments || {};
|
|
395
|
+
switch (request.params.name) {
|
|
396
|
+
case "canary_annotate":
|
|
397
|
+
return handleAnnotate(args);
|
|
398
|
+
case "canary_report":
|
|
399
|
+
reportCount++;
|
|
400
|
+
return handleReport(args, config, reportCount);
|
|
401
|
+
default:
|
|
402
|
+
return {
|
|
403
|
+
content: [
|
|
404
|
+
{
|
|
405
|
+
type: "text",
|
|
406
|
+
text: `Unknown tool: ${request.params.name}`
|
|
407
|
+
}
|
|
408
|
+
],
|
|
409
|
+
isError: true
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
const transport = new StdioServerTransport();
|
|
414
|
+
console.error("Canary journal server v0.2.0 starting...");
|
|
415
|
+
console.error(` Endpoint: ${config.endpoint}`);
|
|
416
|
+
console.error(` Session: ${config.sessionId || "(auto)"}`);
|
|
417
|
+
console.error(` Agent: ${config.agentName || "(unnamed)"}`);
|
|
418
|
+
console.error(` Tools: canary_annotate, canary_report`);
|
|
419
|
+
await server.connect(transport);
|
|
420
|
+
console.error("Canary journal server connected via stdio");
|
|
421
|
+
}
|
|
422
|
+
main().catch((err) => {
|
|
423
|
+
console.error("Fatal:", err);
|
|
424
|
+
process.exit(1);
|
|
425
|
+
});
|
|
426
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/journal/server.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * Canary MCP Journal Server — stdio transport.\n *\n * Tools:\n * canary_annotate — Save/read/list/clear local notes about API experiences.\n * canary_report — Submit structured feedback to the Canary backend.\n *\n * All logging goes to stderr to avoid corrupting the MCP protocol.\n */\n\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport {\n readFileSync,\n writeFileSync,\n mkdirSync,\n unlinkSync,\n readdirSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\n// Redirect console to stderr to prevent stdout corruption\nconsole.log = (...args: any[]) => console.error(...args);\nconsole.info = (...args: any[]) => console.error(...args);\n\nconst FETCH_TIMEOUT_MS = 15_000;\n\n// ─── Annotations (local persistence) ────────────────────────────────\n\nfunction getAnnotationsDir(): string {\n const dir = join(homedir(), \".canary\", \"annotations\");\n mkdirSync(dir, { recursive: true });\n return dir;\n}\n\nfunction annotationPath(id: string): string {\n const safe = id.replace(/\\//g, \"--\");\n return join(getAnnotationsDir(), `${safe}.json`);\n}\n\nfunction readAnnotation(id: string): any | null {\n try {\n return JSON.parse(readFileSync(annotationPath(id), \"utf8\"));\n } catch {\n return null;\n }\n}\n\nfunction writeAnnotation(\n id: string,\n note: string\n): { id: string; note: string; updatedAt: string } {\n const data = { id, note, updatedAt: new Date().toISOString() };\n writeFileSync(annotationPath(id), JSON.stringify(data, null, 2));\n return data;\n}\n\nfunction clearAnnotation(id: string): boolean {\n try {\n unlinkSync(annotationPath(id));\n return true;\n } catch {\n return false;\n }\n}\n\nfunction listAnnotations(): any[] {\n const dir = getAnnotationsDir();\n try {\n return readdirSync(dir)\n .filter((f) => f.endsWith(\".json\"))\n .map((f) => {\n try {\n return JSON.parse(readFileSync(join(dir, f), \"utf8\"));\n } catch {\n return null;\n }\n })\n .filter(Boolean);\n } catch {\n return [];\n }\n}\n\n// ─── Tool definitions ────────────────────────────────────────────────\n\nconst CANARY_ANNOTATE_TOOL = {\n name: \"canary_annotate\",\n description:\n \"REQUIRED: Call this after every HTTP request to an external API. \" +\n \"Record the endpoint, status code, response structure, errors, and quirks. \" +\n \"Notes persist across sessions. Use provider/endpoint IDs like 'stripe/charges'.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n id: {\n type: \"string\",\n description:\n 'Identifier for the annotation (e.g. \"stripe/charges\", \"openai/chat\", \"github/repos\"). ' +\n \"Use provider/endpoint style.\",\n },\n note: {\n type: \"string\",\n description:\n \"The annotation text. Describe what you observed — errors, latency, \" +\n \"quirks, tips. Writing a note saves or replaces the existing one.\",\n },\n clear: {\n type: \"boolean\",\n description: \"Set true to delete the annotation for this id.\",\n },\n list: {\n type: \"boolean\",\n description: \"Set true to list all saved annotations.\",\n },\n },\n required: [],\n },\n};\n\nconst CANARY_REPORT_TOOL = {\n name: \"canary_report\",\n description:\n \"REQUIRED: Call this once when your task is complete to compile your \" +\n \"canary_annotate notes into structured feedback. Your report earns credits \" +\n \"and improves API documentation for all agents. Be specific — include \" +\n \"endpoints, status codes, and error messages.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n session_id: {\n type: \"string\",\n description:\n \"Session/task ID to link this report to HTTP telemetry. Pass the skill_execution_id or run ID.\",\n },\n task_goal: {\n type: \"string\",\n description:\n 'What were you trying to accomplish? (e.g. \"Create a Stripe charge for $50 and send an SMS confirmation via Twilio\")',\n },\n outcome: {\n type: \"string\",\n enum: [\"success\", \"partial\", \"failure\", \"blocked\"],\n description: \"Did the task succeed?\",\n },\n apis_used: {\n type: \"string\",\n description:\n 'Which APIs did you call? List provider and key endpoints. (e.g. \"Stripe POST /v1/charges, Twilio POST /Messages.json\")',\n },\n what_worked: {\n type: \"string\",\n description:\n 'What went smoothly? Mention specific endpoints or features. (e.g. \"Stripe charge creation returned clear response with charge ID\")',\n },\n what_failed: {\n type: \"string\",\n description:\n 'What went wrong? Include error codes, status, and messages. (e.g. \"POST /v1/refunds returned 400: Charge already refunded\")',\n },\n friction: {\n type: \"string\",\n description:\n \"The single biggest pain point. (e.g. \\\"Stripe charge object doesn't show refund status\\\")\",\n },\n suggestion: {\n type: \"string\",\n description:\n 'How could the API improve? (e.g. \"Add a refund_status field to the charge object\")',\n },\n },\n required: [\"task_goal\", \"outcome\", \"apis_used\"],\n },\n};\n\n// ─── Server config ───────────────────────────────────────────────────\n\ninterface ServerConfig {\n apiKey: string;\n endpoint?: string;\n sessionId?: string;\n agentName?: string;\n}\n\nfunction parseArgs(): ServerConfig {\n const args = process.argv.slice(2);\n let apiKey = process.env.CANARY_API_KEY || \"\";\n let endpoint = process.env.CANARY_ENDPOINT || \"http://localhost:8001\";\n let sessionId = process.env.CANARY_SESSION_ID;\n let agentName = process.env.CANARY_AGENT_NAME;\n\n for (let i = 0; i < args.length; i++) {\n if (args[i] === \"--api-key\" && args[i + 1]) apiKey = args[++i];\n else if (args[i] === \"--endpoint\" && args[i + 1]) endpoint = args[++i];\n else if (args[i] === \"--session-id\" && args[i + 1]) sessionId = args[++i];\n else if (args[i] === \"--agent-name\" && args[i + 1]) agentName = args[++i];\n else if (args[i].startsWith(\"--api-key=\"))\n apiKey = args[i].substring(\"--api-key=\".length);\n else if (args[i].startsWith(\"--endpoint=\"))\n endpoint = args[i].substring(\"--endpoint=\".length);\n else if (args[i].startsWith(\"--session-id=\"))\n sessionId = args[i].substring(\"--session-id=\".length);\n else if (args[i].startsWith(\"--agent-name=\"))\n agentName = args[i].substring(\"--agent-name=\".length);\n }\n\n if (!apiKey) {\n console.error(\"Error: --api-key or CANARY_API_KEY required\");\n process.exit(1);\n }\n\n return { apiKey, endpoint, sessionId, agentName };\n}\n\n// ─── Backend communication ───────────────────────────────────────────\n\nasync function postEvents(\n config: ServerConfig,\n events: any[]\n): Promise<any> {\n const url = `${config.endpoint}/v1/events`;\n const body = JSON.stringify({ events, sdk_version: \"0.1.0\" });\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n\n let res: Response;\n try {\n res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${config.apiKey}`,\n },\n body,\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timeout);\n }\n\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`POST /v1/events failed: ${res.status} ${text}`);\n }\n\n return res.json();\n}\n\n// ─── Handlers ────────────────────────────────────────────────────────\n\nfunction handleAnnotate(args: Record<string, any>) {\n const { id, note, clear, list } = args;\n\n if (list) {\n const annotations = listAnnotations();\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify(\n { annotations, total: annotations.length },\n null,\n 2\n ),\n },\n ],\n };\n }\n\n if (!id) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify({\n error:\n \"Missing required parameter: id. Provide a provider/endpoint id or use list mode.\",\n }),\n },\n ],\n isError: true,\n };\n }\n\n // Validate id\n if (id.length > 200) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify({\n error: \"ID too long (max 200 characters).\",\n }),\n },\n ],\n isError: true,\n };\n }\n if (!/^[a-zA-Z0-9._\\-\\/]+$/.test(id)) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify({\n error:\n \"ID contains invalid characters. Use alphanumeric, hyphens, underscores, dots, and slashes.\",\n }),\n },\n ],\n isError: true,\n };\n }\n\n if (clear) {\n const removed = clearAnnotation(id);\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify({\n status: removed ? \"cleared\" : \"not_found\",\n id,\n }),\n },\n ],\n };\n }\n\n if (note) {\n const saved = writeAnnotation(id, note);\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify({ status: \"saved\", annotation: saved }, null, 2),\n },\n ],\n };\n }\n\n // Read mode\n const annotation = readAnnotation(id);\n if (annotation) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify({ annotation }, null, 2),\n },\n ],\n };\n }\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify({ status: \"no_annotation\", id }),\n },\n ],\n };\n}\n\nfunction buildFeedbackEvent(args: Record<string, any>, config: ServerConfig) {\n const outcome = args.outcome as string;\n const worked = outcome === \"success\" || outcome === \"partial\";\n\n const contextParts: string[] = [];\n contextParts.push(`Goal: ${args.task_goal}`);\n contextParts.push(`APIs: ${args.apis_used}`);\n contextParts.push(`Outcome: ${outcome}`);\n if (args.what_worked) contextParts.push(`Worked: ${args.what_worked}`);\n if (args.what_failed) contextParts.push(`Failed: ${args.what_failed}`);\n if (args.suggestion) contextParts.push(`Suggestion: ${args.suggestion}`);\n\n const frictionPoints: string[] = [];\n if (args.friction) frictionPoints.push(args.friction);\n if (args.what_failed && args.what_failed !== args.friction) {\n frictionPoints.push(args.what_failed);\n }\n\n const providerMatch = args.apis_used?.match(/^(\\w+)/);\n const provider = providerMatch ? providerMatch[1].toLowerCase() : undefined;\n\n const endpointMatch = args.apis_used?.match(\n /(?:GET|POST|PUT|DELETE|PATCH)\\s+(\\/\\S+)/\n );\n const endpointPattern = endpointMatch ? endpointMatch[1] : undefined;\n\n return {\n event_type: \"feedback\",\n ts: Date.now() / 1000,\n source: \"journal\",\n worked,\n context: contextParts.join(\". \"),\n friction_points: frictionPoints.length > 0 ? frictionPoints : undefined,\n provider,\n endpoint_pattern: endpointPattern,\n framework_session_id: args.session_id || config.sessionId,\n agent_name: config.agentName,\n };\n}\n\nasync function handleReport(\n args: Record<string, any>,\n config: ServerConfig,\n reportCount: number\n) {\n // Validate required fields\n const missing: string[] = [];\n if (!args.task_goal) missing.push(\"task_goal\");\n if (!args.outcome) missing.push(\"outcome\");\n if (!args.apis_used) missing.push(\"apis_used\");\n if (missing.length > 0) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Missing required fields: ${missing.join(\", \")}`,\n },\n ],\n isError: true,\n };\n }\n\n const validOutcomes = [\"success\", \"partial\", \"failure\", \"blocked\"];\n if (!validOutcomes.includes(args.outcome)) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Invalid outcome \"${args.outcome}\". Must be one of: ${validOutcomes.join(\", \")}`,\n },\n ],\n isError: true,\n };\n }\n\n try {\n const event = buildFeedbackEvent(args, config);\n const result = await postEvents(config, [event]);\n\n const responseLines = [\n `Report #${reportCount} submitted`,\n ` Accepted: ${result.accepted}, Errors: ${result.errors}`,\n ];\n\n if (result.quality_score != null) {\n responseLines.push(` Quality score: ${result.quality_score}/100`);\n }\n if (result.credits != null) {\n responseLines.push(` Credits earned: ${result.credits}`);\n }\n if (result.tip) {\n responseLines.push(` Tip: ${result.tip}`);\n }\n\n return {\n content: [{ type: \"text\" as const, text: responseLines.join(\"\\n\") }],\n };\n } catch (err: any) {\n console.error(\"canary_report error:\", err.message);\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Failed to submit report: ${err.message}`,\n },\n ],\n isError: true,\n };\n }\n}\n\n// ─── Main ────────────────────────────────────────────────────────────\n\nasync function main() {\n const config = parseArgs();\n let reportCount = 0;\n\n const server = new Server(\n { name: \"canary-journal\", version: \"0.2.0\" },\n {\n capabilities: { tools: {} },\n instructions:\n \"You MUST call canary_annotate after every HTTP request you make to an external API. \" +\n \"Note the endpoint, status code, response structure, errors, and any quirks. \" +\n \"When your task is complete, call canary_report to compile your annotations into structured feedback. \" +\n \"Do not skip these steps — even failed requests are valuable data.\",\n }\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [CANARY_ANNOTATE_TOOL, CANARY_REPORT_TOOL],\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const args = (request.params.arguments || {}) as Record<string, any>;\n\n switch (request.params.name) {\n case \"canary_annotate\":\n return handleAnnotate(args);\n\n case \"canary_report\":\n reportCount++;\n return handleReport(args, config, reportCount);\n\n default:\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Unknown tool: ${request.params.name}`,\n },\n ],\n isError: true,\n };\n }\n });\n\n const transport = new StdioServerTransport();\n console.error(\"Canary journal server v0.2.0 starting...\");\n console.error(` Endpoint: ${config.endpoint}`);\n console.error(` Session: ${config.sessionId || \"(auto)\"}`);\n console.error(` Agent: ${config.agentName || \"(unnamed)\"}`);\n console.error(` Tools: canary_annotate, canary_report`);\n await server.connect(transport);\n console.error(\"Canary journal server connected via stdio\");\n}\n\nmain().catch((err) => {\n console.error(\"Fatal:\", err);\n process.exit(1);\n});\n"],"mappings":";;;AAWA,SAAS,cAAc;AACvB,SAAS,4BAA4B;AACrC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,YAAY;AACrB,SAAS,eAAe;AAGxB,QAAQ,MAAM,IAAI,SAAgB,QAAQ,MAAM,GAAG,IAAI;AACvD,QAAQ,OAAO,IAAI,SAAgB,QAAQ,MAAM,GAAG,IAAI;AAExD,IAAM,mBAAmB;AAIzB,SAAS,oBAA4B;AACnC,QAAM,MAAM,KAAK,QAAQ,GAAG,WAAW,aAAa;AACpD,YAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAClC,SAAO;AACT;AAEA,SAAS,eAAe,IAAoB;AAC1C,QAAM,OAAO,GAAG,QAAQ,OAAO,IAAI;AACnC,SAAO,KAAK,kBAAkB,GAAG,GAAG,IAAI,OAAO;AACjD;AAEA,SAAS,eAAe,IAAwB;AAC9C,MAAI;AACF,WAAO,KAAK,MAAM,aAAa,eAAe,EAAE,GAAG,MAAM,CAAC;AAAA,EAC5D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,gBACP,IACA,MACiD;AACjD,QAAM,OAAO,EAAE,IAAI,MAAM,YAAW,oBAAI,KAAK,GAAE,YAAY,EAAE;AAC7D,gBAAc,eAAe,EAAE,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAC/D,SAAO;AACT;AAEA,SAAS,gBAAgB,IAAqB;AAC5C,MAAI;AACF,eAAW,eAAe,EAAE,CAAC;AAC7B,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,kBAAyB;AAChC,QAAM,MAAM,kBAAkB;AAC9B,MAAI;AACF,WAAO,YAAY,GAAG,EACnB,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC,EACjC,IAAI,CAAC,MAAM;AACV,UAAI;AACF,eAAO,KAAK,MAAM,aAAa,KAAK,KAAK,CAAC,GAAG,MAAM,CAAC;AAAA,MACtD,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF,CAAC,EACA,OAAO,OAAO;AAAA,EACnB,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAIA,IAAM,uBAAuB;AAAA,EAC3B,MAAM;AAAA,EACN,aACE;AAAA,EAGF,aAAa;AAAA,IACX,MAAM;AAAA,IACN,YAAY;AAAA,MACV,IAAI;AAAA,QACF,MAAM;AAAA,QACN,aACE;AAAA,MAEJ;AAAA,MACA,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,aACE;AAAA,MAEJ;AAAA,MACA,OAAO;AAAA,QACL,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,UAAU,CAAC;AAAA,EACb;AACF;AAEA,IAAM,qBAAqB;AAAA,EACzB,MAAM;AAAA,EACN,aACE;AAAA,EAIF,aAAa;AAAA,IACX,MAAM;AAAA,IACN,YAAY;AAAA,MACV,YAAY;AAAA,QACV,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,MACA,WAAW;AAAA,QACT,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,MACA,SAAS;AAAA,QACP,MAAM;AAAA,QACN,MAAM,CAAC,WAAW,WAAW,WAAW,SAAS;AAAA,QACjD,aAAa;AAAA,MACf;AAAA,MACA,WAAW;AAAA,QACT,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,MACA,aAAa;AAAA,QACX,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,MACA,aAAa;AAAA,QACX,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,MACA,UAAU;AAAA,QACR,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,MACA,YAAY;AAAA,QACV,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,IACF;AAAA,IACA,UAAU,CAAC,aAAa,WAAW,WAAW;AAAA,EAChD;AACF;AAWA,SAAS,YAA0B;AACjC,QAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,MAAI,SAAS,QAAQ,IAAI,kBAAkB;AAC3C,MAAI,WAAW,QAAQ,IAAI,mBAAmB;AAC9C,MAAI,YAAY,QAAQ,IAAI;AAC5B,MAAI,YAAY,QAAQ,IAAI;AAE5B,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,QAAI,KAAK,CAAC,MAAM,eAAe,KAAK,IAAI,CAAC,EAAG,UAAS,KAAK,EAAE,CAAC;AAAA,aACpD,KAAK,CAAC,MAAM,gBAAgB,KAAK,IAAI,CAAC,EAAG,YAAW,KAAK,EAAE,CAAC;AAAA,aAC5D,KAAK,CAAC,MAAM,kBAAkB,KAAK,IAAI,CAAC,EAAG,aAAY,KAAK,EAAE,CAAC;AAAA,aAC/D,KAAK,CAAC,MAAM,kBAAkB,KAAK,IAAI,CAAC,EAAG,aAAY,KAAK,EAAE,CAAC;AAAA,aAC/D,KAAK,CAAC,EAAE,WAAW,YAAY;AACtC,eAAS,KAAK,CAAC,EAAE,UAAU,aAAa,MAAM;AAAA,aACvC,KAAK,CAAC,EAAE,WAAW,aAAa;AACvC,iBAAW,KAAK,CAAC,EAAE,UAAU,cAAc,MAAM;AAAA,aAC1C,KAAK,CAAC,EAAE,WAAW,eAAe;AACzC,kBAAY,KAAK,CAAC,EAAE,UAAU,gBAAgB,MAAM;AAAA,aAC7C,KAAK,CAAC,EAAE,WAAW,eAAe;AACzC,kBAAY,KAAK,CAAC,EAAE,UAAU,gBAAgB,MAAM;AAAA,EACxD;AAEA,MAAI,CAAC,QAAQ;AACX,YAAQ,MAAM,6CAA6C;AAC3D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,SAAO,EAAE,QAAQ,UAAU,WAAW,UAAU;AAClD;AAIA,eAAe,WACb,QACA,QACc;AACd,QAAM,MAAM,GAAG,OAAO,QAAQ;AAC9B,QAAM,OAAO,KAAK,UAAU,EAAE,QAAQ,aAAa,QAAQ,CAAC;AAE5D,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,gBAAgB;AAErE,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,MAAM,KAAK;AAAA,MACrB,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,eAAe,UAAU,OAAO,MAAM;AAAA,MACxC;AAAA,MACA;AAAA,MACA,QAAQ,WAAW;AAAA,IACrB,CAAC;AAAA,EACH,UAAE;AACA,iBAAa,OAAO;AAAA,EACtB;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,UAAM,IAAI,MAAM,2BAA2B,IAAI,MAAM,IAAI,IAAI,EAAE;AAAA,EACjE;AAEA,SAAO,IAAI,KAAK;AAClB;AAIA,SAAS,eAAe,MAA2B;AACjD,QAAM,EAAE,IAAI,MAAM,OAAO,KAAK,IAAI;AAElC,MAAI,MAAM;AACR,UAAM,cAAc,gBAAgB;AACpC,WAAO;AAAA,MACL,SAAS;AAAA,QACP;AAAA,UACE,MAAM;AAAA,UACN,MAAM,KAAK;AAAA,YACT,EAAE,aAAa,OAAO,YAAY,OAAO;AAAA,YACzC;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,IAAI;AACP,WAAO;AAAA,MACL,SAAS;AAAA,QACP;AAAA,UACE,MAAM;AAAA,UACN,MAAM,KAAK,UAAU;AAAA,YACnB,OACE;AAAA,UACJ,CAAC;AAAA,QACH;AAAA,MACF;AAAA,MACA,SAAS;AAAA,IACX;AAAA,EACF;AAGA,MAAI,GAAG,SAAS,KAAK;AACnB,WAAO;AAAA,MACL,SAAS;AAAA,QACP;AAAA,UACE,MAAM;AAAA,UACN,MAAM,KAAK,UAAU;AAAA,YACnB,OAAO;AAAA,UACT,CAAC;AAAA,QACH;AAAA,MACF;AAAA,MACA,SAAS;AAAA,IACX;AAAA,EACF;AACA,MAAI,CAAC,uBAAuB,KAAK,EAAE,GAAG;AACpC,WAAO;AAAA,MACL,SAAS;AAAA,QACP;AAAA,UACE,MAAM;AAAA,UACN,MAAM,KAAK,UAAU;AAAA,YACnB,OACE;AAAA,UACJ,CAAC;AAAA,QACH;AAAA,MACF;AAAA,MACA,SAAS;AAAA,IACX;AAAA,EACF;AAEA,MAAI,OAAO;AACT,UAAM,UAAU,gBAAgB,EAAE;AAClC,WAAO;AAAA,MACL,SAAS;AAAA,QACP;AAAA,UACE,MAAM;AAAA,UACN,MAAM,KAAK,UAAU;AAAA,YACnB,QAAQ,UAAU,YAAY;AAAA,YAC9B;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,MAAM;AACR,UAAM,QAAQ,gBAAgB,IAAI,IAAI;AACtC,WAAO;AAAA,MACL,SAAS;AAAA,QACP;AAAA,UACE,MAAM;AAAA,UACN,MAAM,KAAK,UAAU,EAAE,QAAQ,SAAS,YAAY,MAAM,GAAG,MAAM,CAAC;AAAA,QACtE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,QAAM,aAAa,eAAe,EAAE;AACpC,MAAI,YAAY;AACd,WAAO;AAAA,MACL,SAAS;AAAA,QACP;AAAA,UACE,MAAM;AAAA,UACN,MAAM,KAAK,UAAU,EAAE,WAAW,GAAG,MAAM,CAAC;AAAA,QAC9C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AAAA,IACL,SAAS;AAAA,MACP;AAAA,QACE,MAAM;AAAA,QACN,MAAM,KAAK,UAAU,EAAE,QAAQ,iBAAiB,GAAG,CAAC;AAAA,MACtD;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,mBAAmB,MAA2B,QAAsB;AAC3E,QAAM,UAAU,KAAK;AACrB,QAAM,SAAS,YAAY,aAAa,YAAY;AAEpD,QAAM,eAAyB,CAAC;AAChC,eAAa,KAAK,SAAS,KAAK,SAAS,EAAE;AAC3C,eAAa,KAAK,SAAS,KAAK,SAAS,EAAE;AAC3C,eAAa,KAAK,YAAY,OAAO,EAAE;AACvC,MAAI,KAAK,YAAa,cAAa,KAAK,WAAW,KAAK,WAAW,EAAE;AACrE,MAAI,KAAK,YAAa,cAAa,KAAK,WAAW,KAAK,WAAW,EAAE;AACrE,MAAI,KAAK,WAAY,cAAa,KAAK,eAAe,KAAK,UAAU,EAAE;AAEvE,QAAM,iBAA2B,CAAC;AAClC,MAAI,KAAK,SAAU,gBAAe,KAAK,KAAK,QAAQ;AACpD,MAAI,KAAK,eAAe,KAAK,gBAAgB,KAAK,UAAU;AAC1D,mBAAe,KAAK,KAAK,WAAW;AAAA,EACtC;AAEA,QAAM,gBAAgB,KAAK,WAAW,MAAM,QAAQ;AACpD,QAAM,WAAW,gBAAgB,cAAc,CAAC,EAAE,YAAY,IAAI;AAElE,QAAM,gBAAgB,KAAK,WAAW;AAAA,IACpC;AAAA,EACF;AACA,QAAM,kBAAkB,gBAAgB,cAAc,CAAC,IAAI;AAE3D,SAAO;AAAA,IACL,YAAY;AAAA,IACZ,IAAI,KAAK,IAAI,IAAI;AAAA,IACjB,QAAQ;AAAA,IACR;AAAA,IACA,SAAS,aAAa,KAAK,IAAI;AAAA,IAC/B,iBAAiB,eAAe,SAAS,IAAI,iBAAiB;AAAA,IAC9D;AAAA,IACA,kBAAkB;AAAA,IAClB,sBAAsB,KAAK,cAAc,OAAO;AAAA,IAChD,YAAY,OAAO;AAAA,EACrB;AACF;AAEA,eAAe,aACb,MACA,QACA,aACA;AAEA,QAAM,UAAoB,CAAC;AAC3B,MAAI,CAAC,KAAK,UAAW,SAAQ,KAAK,WAAW;AAC7C,MAAI,CAAC,KAAK,QAAS,SAAQ,KAAK,SAAS;AACzC,MAAI,CAAC,KAAK,UAAW,SAAQ,KAAK,WAAW;AAC7C,MAAI,QAAQ,SAAS,GAAG;AACtB,WAAO;AAAA,MACL,SAAS;AAAA,QACP;AAAA,UACE,MAAM;AAAA,UACN,MAAM,4BAA4B,QAAQ,KAAK,IAAI,CAAC;AAAA,QACtD;AAAA,MACF;AAAA,MACA,SAAS;AAAA,IACX;AAAA,EACF;AAEA,QAAM,gBAAgB,CAAC,WAAW,WAAW,WAAW,SAAS;AACjE,MAAI,CAAC,cAAc,SAAS,KAAK,OAAO,GAAG;AACzC,WAAO;AAAA,MACL,SAAS;AAAA,QACP;AAAA,UACE,MAAM;AAAA,UACN,MAAM,oBAAoB,KAAK,OAAO,sBAAsB,cAAc,KAAK,IAAI,CAAC;AAAA,QACtF;AAAA,MACF;AAAA,MACA,SAAS;AAAA,IACX;AAAA,EACF;AAEA,MAAI;AACF,UAAM,QAAQ,mBAAmB,MAAM,MAAM;AAC7C,UAAM,SAAS,MAAM,WAAW,QAAQ,CAAC,KAAK,CAAC;AAE/C,UAAM,gBAAgB;AAAA,MACpB,WAAW,WAAW;AAAA,MACtB,eAAe,OAAO,QAAQ,aAAa,OAAO,MAAM;AAAA,IAC1D;AAEA,QAAI,OAAO,iBAAiB,MAAM;AAChC,oBAAc,KAAK,oBAAoB,OAAO,aAAa,MAAM;AAAA,IACnE;AACA,QAAI,OAAO,WAAW,MAAM;AAC1B,oBAAc,KAAK,qBAAqB,OAAO,OAAO,EAAE;AAAA,IAC1D;AACA,QAAI,OAAO,KAAK;AACd,oBAAc,KAAK,UAAU,OAAO,GAAG,EAAE;AAAA,IAC3C;AAEA,WAAO;AAAA,MACL,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,cAAc,KAAK,IAAI,EAAE,CAAC;AAAA,IACrE;AAAA,EACF,SAAS,KAAU;AACjB,YAAQ,MAAM,wBAAwB,IAAI,OAAO;AACjD,WAAO;AAAA,MACL,SAAS;AAAA,QACP;AAAA,UACE,MAAM;AAAA,UACN,MAAM,4BAA4B,IAAI,OAAO;AAAA,QAC/C;AAAA,MACF;AAAA,MACA,SAAS;AAAA,IACX;AAAA,EACF;AACF;AAIA,eAAe,OAAO;AACpB,QAAM,SAAS,UAAU;AACzB,MAAI,cAAc;AAElB,QAAM,SAAS,IAAI;AAAA,IACjB,EAAE,MAAM,kBAAkB,SAAS,QAAQ;AAAA,IAC3C;AAAA,MACE,cAAc,EAAE,OAAO,CAAC,EAAE;AAAA,MAC1B,cACE;AAAA,IAIJ;AAAA,EACF;AAEA,SAAO,kBAAkB,wBAAwB,aAAa;AAAA,IAC5D,OAAO,CAAC,sBAAsB,kBAAkB;AAAA,EAClD,EAAE;AAEF,SAAO,kBAAkB,uBAAuB,OAAO,YAAY;AACjE,UAAM,OAAQ,QAAQ,OAAO,aAAa,CAAC;AAE3C,YAAQ,QAAQ,OAAO,MAAM;AAAA,MAC3B,KAAK;AACH,eAAO,eAAe,IAAI;AAAA,MAE5B,KAAK;AACH;AACA,eAAO,aAAa,MAAM,QAAQ,WAAW;AAAA,MAE/C;AACE,eAAO;AAAA,UACL,SAAS;AAAA,YACP;AAAA,cACE,MAAM;AAAA,cACN,MAAM,iBAAiB,QAAQ,OAAO,IAAI;AAAA,YAC5C;AAAA,UACF;AAAA,UACA,SAAS;AAAA,QACX;AAAA,IACJ;AAAA,EACF,CAAC;AAED,QAAM,YAAY,IAAI,qBAAqB;AAC3C,UAAQ,MAAM,0CAA0C;AACxD,UAAQ,MAAM,eAAe,OAAO,QAAQ,EAAE;AAC9C,UAAQ,MAAM,eAAe,OAAO,aAAa,QAAQ,EAAE;AAC3D,UAAQ,MAAM,eAAe,OAAO,aAAa,WAAW,EAAE;AAC9D,UAAQ,MAAM,4CAA4C;AAC1D,QAAM,OAAO,QAAQ,SAAS;AAC9B,UAAQ,MAAM,2CAA2C;AAC3D;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,UAAQ,MAAM,UAAU,GAAG;AAC3B,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
|