ardea 0.3.1 → 0.3.3

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 CHANGED
@@ -1,10 +1,20 @@
1
1
  # ardea
2
2
 
3
- MCP tools for AI agents to report API experiences. Record observations during tasks and submit structured feedback that gets scored and surfaced in the [Ardea dashboard](https://ardea-production.up.railway.app).
3
+ MCP tools for AI agents to report API experiences. Record observations during tasks and submit structured feedback that gets scored and surfaced in the [Canary dashboard](https://canary-dashboard.up.railway.app).
4
4
 
5
- ## Quick Start
5
+ ## Install
6
6
 
7
- Add to your MCP config (`.mcp.json`, Claude Desktop, etc.):
7
+ One command to add to Claude Code:
8
+
9
+ ```bash
10
+ claude mcp add ardea -- npx -y ardea
11
+ ```
12
+
13
+ That's it. On first run a browser window opens for signup — your API key is saved automatically.
14
+
15
+ ### Other MCP clients
16
+
17
+ Add to your `.mcp.json` (Claude Desktop, Cursor, Windsurf, etc.):
8
18
 
9
19
  ```json
10
20
  {
@@ -17,8 +27,6 @@ Add to your MCP config (`.mcp.json`, Claude Desktop, etc.):
17
27
  }
18
28
  ```
19
29
 
20
- On first run, a browser window opens for signup. Your API key is saved to `~/.ardea/config.json` automatically.
21
-
22
30
  ## Tools
23
31
 
24
32
  ### `ardea_annotate`
@@ -0,0 +1,7 @@
1
+ // src/constants.ts
2
+ var DEFAULT_ENDPOINT = "https://canary-production-89d8.up.railway.app";
3
+
4
+ export {
5
+ DEFAULT_ENDPOINT
6
+ };
7
+ //# sourceMappingURL=chunk-NE3455WO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/constants.ts"],"sourcesContent":["export const DEFAULT_ENDPOINT =\n \"https://canary-production-89d8.up.railway.app\";\n"],"mappings":";AAAO,IAAM,mBACX;","names":[]}
@@ -1,7 +1,7 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/constants.ts
2
- var DEFAULT_ENDPOINT = "https://ardea-production.up.railway.app";
2
+ var DEFAULT_ENDPOINT = "https://canary-production-89d8.up.railway.app";
3
3
 
4
4
 
5
5
 
6
6
  exports.DEFAULT_ENDPOINT = DEFAULT_ENDPOINT;
7
- //# sourceMappingURL=chunk-OREJ23CV.cjs.map
7
+ //# sourceMappingURL=chunk-SIKOQ77Q.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/stalapaneni/conductor/workspaces/canary/banjul/canary/dist/chunk-SIKOQ77Q.cjs","../src/constants.ts"],"names":[],"mappings":"AAAA;ACAO,IAAM,iBAAA,EACX,+CAAA;ADCF;AACA;AACE;AACF,4CAAC","file":"/Users/stalapaneni/conductor/workspaces/canary/banjul/canary/dist/chunk-SIKOQ77Q.cjs","sourcesContent":[null,"export const DEFAULT_ENDPOINT =\n \"https://canary-production-89d8.up.railway.app\";\n"]}
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } var _class;
2
2
 
3
- var _chunkOREJ23CVcjs = require('./chunk-OREJ23CV.cjs');
3
+ var _chunkSIKOQ77Qcjs = require('./chunk-SIKOQ77Q.cjs');
4
4
 
5
5
  // src/buffer.ts
6
6
  var _zlib = require('zlib');
@@ -25,7 +25,7 @@ var EventBuffer = (_class = class {
25
25
 
26
26
  /** Reference to the original (unpatched) http.request for anti-recursion. */
27
27
 
28
- constructor(apiKey, endpoint = _chunkOREJ23CVcjs.DEFAULT_ENDPOINT, autoFlush = true, originalHttpsRequest, originalHttpRequest) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);
28
+ constructor(apiKey, endpoint = _chunkSIKOQ77Qcjs.DEFAULT_ENDPOINT, autoFlush = true, originalHttpsRequest, originalHttpRequest) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);
29
29
  this._apiKey = apiKey;
30
30
  this._endpoint = endpoint.replace(/\/+$/, "");
31
31
  this._originalHttpsRequest = _nullishCoalesce(originalHttpsRequest, () => ( _https2.default.request));
@@ -334,7 +334,7 @@ function init(config) {
334
334
  "Invalid API key: must start with 'cnry_sk_'. Get your key at https://app.canary.dev"
335
335
  );
336
336
  }
337
- const endpoint = _nullishCoalesce(config.endpoint, () => ( _chunkOREJ23CVcjs.DEFAULT_ENDPOINT));
337
+ const endpoint = _nullishCoalesce(config.endpoint, () => ( _chunkSIKOQ77Qcjs.DEFAULT_ENDPOINT));
338
338
  const autoFlush = _nullishCoalesce(config.autoFlush, () => ( true));
339
339
  _buffer = new EventBuffer(config.apiKey, endpoint, autoFlush);
340
340
  _initialized = true;
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/stalapaneni/conductor/workspaces/canary/bucharest/canary/dist/index.cjs","../src/buffer.ts","../src/context.ts","../src/agent-detect.ts","../src/voucher.ts","../src/index.ts"],"names":[],"mappings":"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","file":"/Users/stalapaneni/conductor/workspaces/canary/bucharest/canary/dist/index.cjs","sourcesContent":[null,"/**\n * Event buffer with ring buffer, gzip flush, retry, and session cache.\n *\n * Mirrors sdk/src/ardea/buffer.py.\n */\n\nimport { gzipSync } from \"node:zlib\";\nimport https from \"node:https\";\nimport http from \"node:http\";\nimport type { CanaryEvent } from \"./types.js\";\nimport { DEFAULT_ENDPOINT } from \"./constants.js\";\n\nconst MAX_LEN = 10_000;\nconst BATCH_SIZE = 100;\nconst FLUSH_INTERVAL_MS = 10_000;\nconst MAX_EVENTS_PER_SESSION = 500;\nconst MAX_SESSION_CACHE_EVENTS = 5_000;\nconst RETRY_ATTEMPTS = 3;\nconst RETRY_BACKOFF_MS = [1000, 2000, 4000];\n\nexport class EventBuffer {\n private _buffer: CanaryEvent[] = [];\n private _sessionCache: Map<string, CanaryEvent[]> = new Map();\n private _sessionCacheEventCount = 0;\n private _apiKey: string;\n private _endpoint: string;\n private _timer: ReturnType<typeof setInterval> | null = null;\n private _stopped = false;\n /** Reference to the original (unpatched) https.request for anti-recursion. */\n private _originalHttpsRequest: typeof https.request;\n /** Reference to the original (unpatched) http.request for anti-recursion. */\n private _originalHttpRequest: typeof http.request;\n\n constructor(\n apiKey: string,\n endpoint = DEFAULT_ENDPOINT,\n autoFlush = true,\n originalHttpsRequest?: typeof https.request,\n originalHttpRequest?: typeof http.request\n ) {\n this._apiKey = apiKey;\n this._endpoint = endpoint.replace(/\\/+$/, \"\");\n this._originalHttpsRequest = originalHttpsRequest ?? https.request;\n this._originalHttpRequest = originalHttpRequest ?? http.request;\n\n if (autoFlush) {\n this._timer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS);\n this._timer.unref();\n }\n }\n\n /** Add an event to the ring buffer. */\n push(event: CanaryEvent): void {\n if (this._buffer.length >= MAX_LEN) {\n this._buffer.shift(); // drop oldest\n }\n this._buffer.push(event);\n\n // Retain in session cache\n const sessionId = (event as unknown as Record<string, unknown>).framework_session_id;\n if (typeof sessionId === \"string\" && sessionId) {\n if (this._sessionCacheEventCount < MAX_SESSION_CACHE_EVENTS) {\n let sessionEvents = this._sessionCache.get(sessionId);\n if (!sessionEvents) {\n sessionEvents = [];\n this._sessionCache.set(sessionId, sessionEvents);\n }\n if (sessionEvents.length < MAX_EVENTS_PER_SESSION) {\n sessionEvents.push(event);\n this._sessionCacheEventCount++;\n }\n }\n }\n }\n\n /** Return a copy of events for the given session from the cache. */\n getSessionEvents(sessionId: string): CanaryEvent[] {\n return [...(this._sessionCache.get(sessionId) ?? [])];\n }\n\n /** Remove session events from the cache. */\n clearSession(sessionId: string): void {\n const events = this._sessionCache.get(sessionId);\n if (events) {\n this._sessionCacheEventCount = Math.max(\n 0,\n this._sessionCacheEventCount - events.length\n );\n this._sessionCache.delete(sessionId);\n }\n }\n\n /** Remove and return up to `count` events from the buffer. */\n drain(count: number): CanaryEvent[] {\n const n = Math.min(count, this._buffer.length);\n return this._buffer.splice(0, n);\n }\n\n /** Get all buffered events (without draining). For reporter access. */\n peek(): CanaryEvent[] {\n return [...this._buffer];\n }\n\n /** Number of buffered events. */\n get length(): number {\n return this._buffer.length;\n }\n\n /** Drain a batch and send to backend. */\n async flush(): Promise<void> {\n const events = this.drain(BATCH_SIZE);\n if (events.length === 0) return;\n\n for (let attempt = 0; attempt < RETRY_ATTEMPTS; attempt++) {\n try {\n await this._send(events);\n return;\n } catch {\n if (attempt < RETRY_ATTEMPTS - 1) {\n await sleep(RETRY_BACKOFF_MS[attempt]);\n }\n }\n }\n // All retries failed — events are lost (no disk persistence in Node SDK v1)\n }\n\n /** Stop flush timer and drain all remaining events. */\n async shutdown(): Promise<void> {\n this._stopped = true;\n if (this._timer) {\n clearInterval(this._timer);\n this._timer = null;\n }\n\n while (this._buffer.length > 0) {\n const events = this.drain(BATCH_SIZE);\n if (events.length === 0) break;\n\n for (let attempt = 0; attempt < RETRY_ATTEMPTS; attempt++) {\n try {\n await this._send(events);\n break;\n } catch {\n if (attempt === RETRY_ATTEMPTS - 1) {\n // Final failure — events lost\n } else {\n await sleep(RETRY_BACKOFF_MS[attempt]);\n }\n }\n }\n }\n }\n\n /** POST events to the backend with gzip compression. */\n private _send(events: CanaryEvent[]): Promise<void> {\n const payload = JSON.stringify({\n events,\n sdk_version: \"0.1.0\",\n });\n const compressed = gzipSync(Buffer.from(payload, \"utf-8\"));\n\n const url = new URL(`${this._endpoint}/v1/events`);\n const isHttps = url.protocol === \"https:\";\n const requestFn = isHttps ? this._originalHttpsRequest : this._originalHttpRequest;\n\n return new Promise<void>((resolve, reject) => {\n const req = requestFn(\n {\n hostname: url.hostname,\n port: url.port || (isHttps ? 443 : 80),\n path: url.pathname,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Content-Encoding\": \"gzip\",\n Authorization: `Bearer ${this._apiKey}`,\n \"Content-Length\": compressed.length,\n },\n timeout: 5000,\n },\n (res) => {\n // Consume the response\n res.resume();\n if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {\n resolve();\n } else {\n reject(new Error(`HTTP ${res.statusCode}`));\n }\n }\n );\n\n req.on(\"error\", reject);\n req.on(\"timeout\", () => {\n req.destroy();\n reject(new Error(\"Request timeout\"));\n });\n req.write(compressed);\n req.end();\n });\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * Session correlation via AsyncLocalStorage.\n *\n * Node.js equivalent of sdk/src/canary/context.py using\n * AsyncLocalStorage instead of ContextVar.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { randomUUID } from \"node:crypto\";\nimport { detectAgent } from \"./agent-detect.js\";\n\ninterface SessionContext {\n frameworkSessionId?: string;\n}\n\nconst sessionStorage = new AsyncLocalStorage<SessionContext>();\nconst sdkInstanceId = randomUUID().replace(/-/g, \"\");\nlet callSequence = 0;\nconst defaultContext: SessionContext = {\n frameworkSessionId: undefined,\n};\n\nfunction getMutableContext(): SessionContext {\n return sessionStorage.getStore() ?? defaultContext;\n}\n\nfunction nextCallSequence(): number {\n callSequence += 1;\n return callSequence;\n}\n\nexport interface SessionSignals {\n sdk_instance_id: string;\n process_id: number;\n parent_process_id?: number;\n process_kind?: string;\n process_label?: string;\n thread_id: number;\n call_sequence: number;\n framework_session_id?: string;\n agent_name?: string;\n agent_version?: string;\n}\n\nfunction getBaseSignals(): Omit<SessionSignals, \"call_sequence\"> {\n const ctx = getMutableContext();\n const configuredParentProcessId = Number.parseInt(\n process.env.ARDEA_PARENT_PROCESS_ID || process.env.CANARY_PARENT_PROCESS_ID || \"\",\n 10\n );\n const configuredProcessKind = process.env.ARDEA_PROCESS_KIND || process.env.CANARY_PROCESS_KIND || \"agent\";\n const configuredProcessLabel = process.env.ARDEA_PROCESS_LABEL || process.env.CANARY_PROCESS_LABEL || undefined;\n const signals: Omit<SessionSignals, \"call_sequence\"> = {\n sdk_instance_id: sdkInstanceId,\n process_id: process.pid,\n thread_id: 0,\n };\n\n if (Number.isFinite(configuredParentProcessId)) {\n signals.parent_process_id = configuredParentProcessId;\n }\n if (configuredProcessKind) {\n signals.process_kind = configuredProcessKind;\n }\n if (configuredProcessLabel) {\n signals.process_label = configuredProcessLabel;\n }\n\n if (ctx.frameworkSessionId) {\n signals.framework_session_id = ctx.frameworkSessionId;\n }\n\n const agentInfo = detectAgent();\n if (agentInfo.agent_name) {\n signals.agent_name = agentInfo.agent_name;\n }\n if (agentInfo.agent_version) {\n signals.agent_version = agentInfo.agent_version;\n }\n\n return signals;\n}\n\n/**\n * Return session correlation signals for the current execution context.\n */\nexport function getSessionSignals(): SessionSignals {\n return {\n ...getBaseSignals(),\n call_sequence: nextCallSequence(),\n };\n}\n\n/**\n * Set the framework session ID for the current async context.\n *\n * If no async context is active, this becomes the default session ID used\n * by subsequent feedback/reporting calls on this process.\n */\nexport function setFrameworkSession(sessionId: string): void {\n getMutableContext().frameworkSessionId = sessionId;\n}\n\n/**\n * Clear the framework session ID for the current async context.\n */\nexport function clearFrameworkSession(): void {\n getMutableContext().frameworkSessionId = undefined;\n}\n\n/**\n * Run a function with a scoped framework session ID.\n *\n * The session ID propagates through async call chains automatically.\n */\nexport function runWithSession<T>(sessionId: string, fn: () => T): T {\n return sessionStorage.run(\n {\n frameworkSessionId: sessionId,\n },\n fn\n );\n}\n\n/**\n * Reset all context state. For testing only.\n */\nexport function resetContext(): void {\n callSequence = 0;\n defaultContext.frameworkSessionId = undefined;\n}\n","/**\n * Detect which AI agent framework is running via environment variables.\n */\n\nexport interface AgentInfo {\n agent_name: string | null;\n agent_version: string | null;\n}\n\nexport const AGENT_ENV_MAP: ReadonlyArray<{ envKey: string; name: string; versionKey?: string; useValue?: boolean }> = [\n { envKey: \"ARDEA_AGENT_NAME\", name: \"\", useValue: true },\n { envKey: \"CANARY_AGENT_NAME\", name: \"\", useValue: true },\n { envKey: \"CLAUDE_CODE\", name: \"claude_code\" },\n { envKey: \"CURSOR_TRACE_ID\", name: \"cursor\" },\n { envKey: \"WINDSURF_SESSION_ID\", name: \"windsurf\" },\n { envKey: \"CLINE_TASK_ID\", name: \"cline\" },\n { envKey: \"AIDER_VERSION\", name: \"aider\", versionKey: \"AIDER_VERSION\" },\n { envKey: \"DEVIN_SESSION_ID\", name: \"devin\" },\n { envKey: \"CODEX_CLI\", name: \"codex\" },\n { envKey: \"GITHUB_COPILOT\", name: \"github_copilot\" },\n { envKey: \"CODY_SESSION_ID\", name: \"sourcegraph_cody\" },\n];\n\nlet cachedInfo: AgentInfo | null = null;\n\nexport function detectAgent(): AgentInfo {\n if (cachedInfo) return cachedInfo;\n\n for (const entry of AGENT_ENV_MAP) {\n const val = process.env[entry.envKey];\n // Check !== undefined so empty strings (presence flags) still detect\n if (val !== undefined) {\n cachedInfo = {\n agent_name: entry.useValue ? (val || null) : entry.name,\n agent_version: entry.versionKey ? (process.env[entry.versionKey] ?? null) : null,\n };\n if (cachedInfo.agent_name) return cachedInfo;\n }\n }\n\n cachedInfo = { agent_name: null, agent_version: null };\n return cachedInfo;\n}\n\nexport function resetAgentCache(): void {\n cachedInfo = null;\n}\n","/**\n * Auto-redeem a voucher code on SDK init.\n * Non-blocking — fires in the background, never delays SDK startup.\n * Tracks redeemed codes in ~/.ardea/redeemed-vouchers.json.\n */\n\nimport { readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\ntype VoucherRedeemResponse = {\n credits?: number;\n vendor?: string;\n};\n\nfunction getRedeemedPath(): string {\n const dir = join(homedir(), \".ardea\");\n mkdirSync(dir, { recursive: true });\n return join(dir, \"redeemed-vouchers.json\");\n}\n\nfunction getRedeemedSet(): Set<string> {\n try {\n const data = JSON.parse(readFileSync(getRedeemedPath(), \"utf8\"));\n return new Set(Array.isArray(data) ? data : []);\n } catch {\n return new Set();\n }\n}\n\nfunction markRedeemed(code: string): void {\n const set = getRedeemedSet();\n set.add(code);\n writeFileSync(getRedeemedPath(), JSON.stringify([...set]));\n}\n\nexport function autoRedeemVoucher(\n apiKey: string,\n endpoint: string,\n code: string,\n): void {\n if (!code.startsWith(\"CVR-\")) return;\n\n const redeemed = getRedeemedSet();\n if (redeemed.has(code)) return;\n\n // Fire-and-forget — don't block SDK init\n fetch(`${endpoint}/v1/vouchers/redeem`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ code }),\n })\n .then(async (res) => {\n if (res.ok) {\n const data = (await res.json()) as VoucherRedeemResponse;\n markRedeemed(code);\n // Log to stderr to avoid corrupting any MCP stdio\n console.error(\n `[ardea] Voucher redeemed: +${data.credits ?? 0} ${data.vendor ?? \"unknown\"} credits`,\n );\n } else if (res.status === 410) {\n // Already redeemed, expired, or revoked — mark locally to avoid retries\n markRedeemed(code);\n }\n })\n .catch(() => {\n // Silently fail — SDK init must not be affected\n });\n}\n","/**\n * ardea — MCP tools for AI agents to report API experiences.\n */\n\nimport { EventBuffer } from \"./buffer.js\";\nimport {\n runWithSession,\n setFrameworkSession,\n clearFrameworkSession,\n getSessionSignals,\n} from \"./context.js\";\nimport type { CanaryConfig, CanaryEvent, FeedbackEvent } from \"./types.js\";\nimport { DEFAULT_ENDPOINT } from \"./constants.js\";\nimport { autoRedeemVoucher } from \"./voucher.js\";\n\nlet _initialized = false;\nlet _buffer: EventBuffer | null = null;\n\n/**\n * Initialize Canary SDK for manual feedback submission.\n */\nexport function init(config: CanaryConfig): void {\n if (_initialized) return;\n\n if (!config.apiKey.startsWith(\"cnry_sk_\")) {\n throw new Error(\n \"Invalid API key: must start with 'cnry_sk_'. \" +\n \"Get your key at https://app.canary.dev\"\n );\n }\n\n const endpoint = config.endpoint ?? DEFAULT_ENDPOINT;\n const autoFlush = config.autoFlush ?? true;\n\n _buffer = new EventBuffer(config.apiKey, endpoint, autoFlush);\n _initialized = true;\n\n // Auto-redeem voucher if ARDEA_VOUCHER or CANARY_VOUCHER env var is set (non-blocking)\n const voucherCode = process.env.ARDEA_VOUCHER || process.env.CANARY_VOUCHER;\n if (voucherCode) {\n autoRedeemVoucher(config.apiKey, endpoint, voucherCode);\n }\n}\n\n/**\n * Shut down the SDK and flush any queued feedback events.\n */\nexport async function shutdown(): Promise<void> {\n if (!_initialized || !_buffer) return;\n\n await _buffer.shutdown();\n _buffer = null;\n _initialized = false;\n}\n\n/**\n * Submit manual feedback.\n */\nexport function survey(options: {\n worked?: boolean;\n context?: string;\n frictionPoints?: string[];\n provider?: string;\n sessionId?: string;\n}): void {\n if (!_buffer) {\n throw new Error(\"Canary SDK not initialized. Call init() first.\");\n }\n\n // Pick up session context from AsyncLocalStorage (set by runWithSession)\n const signals = getSessionSignals();\n const event: FeedbackEvent = {\n event_type: \"feedback\",\n source_plane: \"declared\",\n capture_mode: \"declared\",\n source: \"manual\",\n worked: options.worked ?? true,\n context: options.context ?? \"\",\n ts: Date.now() / 1000,\n agent_name: signals.agent_name ?? undefined,\n agent_version: signals.agent_version ?? undefined,\n framework_session_id: options.sessionId ?? signals.framework_session_id ?? undefined,\n };\n\n if (options.frictionPoints && options.frictionPoints.length > 0) {\n event.friction_points = options.frictionPoints;\n }\n if (options.provider) {\n event.provider = options.provider;\n }\n\n _buffer.push(event);\n}\n\n/**\n * Get the global EventBuffer (for advanced usage / adapters).\n */\nexport function getBuffer(): EventBuffer | null {\n return _buffer;\n}\n\n// Re-export session helpers\nexport {\n runWithSession,\n setFrameworkSession,\n clearFrameworkSession,\n getSessionSignals,\n};\n\n// Re-export types\nexport type { ArdeaConfig, CanaryConfig, CanaryEvent, FeedbackEvent } from \"./types.js\";\nexport { detectAgent, resetAgentCache } from \"./agent-detect.js\";\n\n/** Alias for init — Ardea rebrand entry point. */\nexport const Ardea = { init, shutdown, survey, getBuffer };\n\n/** @deprecated Use Ardea instead. */\nexport const Canary = Ardea;\n"]}
1
+ {"version":3,"sources":["/Users/stalapaneni/conductor/workspaces/canary/banjul/canary/dist/index.cjs","../src/buffer.ts","../src/context.ts","../src/agent-detect.ts","../src/voucher.ts","../src/index.ts"],"names":[],"mappings":"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","file":"/Users/stalapaneni/conductor/workspaces/canary/banjul/canary/dist/index.cjs","sourcesContent":[null,"/**\n * Event buffer with ring buffer, gzip flush, retry, and session cache.\n *\n * Mirrors sdk/src/ardea/buffer.py.\n */\n\nimport { gzipSync } from \"node:zlib\";\nimport https from \"node:https\";\nimport http from \"node:http\";\nimport type { CanaryEvent } from \"./types.js\";\nimport { DEFAULT_ENDPOINT } from \"./constants.js\";\n\nconst MAX_LEN = 10_000;\nconst BATCH_SIZE = 100;\nconst FLUSH_INTERVAL_MS = 10_000;\nconst MAX_EVENTS_PER_SESSION = 500;\nconst MAX_SESSION_CACHE_EVENTS = 5_000;\nconst RETRY_ATTEMPTS = 3;\nconst RETRY_BACKOFF_MS = [1000, 2000, 4000];\n\nexport class EventBuffer {\n private _buffer: CanaryEvent[] = [];\n private _sessionCache: Map<string, CanaryEvent[]> = new Map();\n private _sessionCacheEventCount = 0;\n private _apiKey: string;\n private _endpoint: string;\n private _timer: ReturnType<typeof setInterval> | null = null;\n private _stopped = false;\n /** Reference to the original (unpatched) https.request for anti-recursion. */\n private _originalHttpsRequest: typeof https.request;\n /** Reference to the original (unpatched) http.request for anti-recursion. */\n private _originalHttpRequest: typeof http.request;\n\n constructor(\n apiKey: string,\n endpoint = DEFAULT_ENDPOINT,\n autoFlush = true,\n originalHttpsRequest?: typeof https.request,\n originalHttpRequest?: typeof http.request\n ) {\n this._apiKey = apiKey;\n this._endpoint = endpoint.replace(/\\/+$/, \"\");\n this._originalHttpsRequest = originalHttpsRequest ?? https.request;\n this._originalHttpRequest = originalHttpRequest ?? http.request;\n\n if (autoFlush) {\n this._timer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS);\n this._timer.unref();\n }\n }\n\n /** Add an event to the ring buffer. */\n push(event: CanaryEvent): void {\n if (this._buffer.length >= MAX_LEN) {\n this._buffer.shift(); // drop oldest\n }\n this._buffer.push(event);\n\n // Retain in session cache\n const sessionId = (event as unknown as Record<string, unknown>).framework_session_id;\n if (typeof sessionId === \"string\" && sessionId) {\n if (this._sessionCacheEventCount < MAX_SESSION_CACHE_EVENTS) {\n let sessionEvents = this._sessionCache.get(sessionId);\n if (!sessionEvents) {\n sessionEvents = [];\n this._sessionCache.set(sessionId, sessionEvents);\n }\n if (sessionEvents.length < MAX_EVENTS_PER_SESSION) {\n sessionEvents.push(event);\n this._sessionCacheEventCount++;\n }\n }\n }\n }\n\n /** Return a copy of events for the given session from the cache. */\n getSessionEvents(sessionId: string): CanaryEvent[] {\n return [...(this._sessionCache.get(sessionId) ?? [])];\n }\n\n /** Remove session events from the cache. */\n clearSession(sessionId: string): void {\n const events = this._sessionCache.get(sessionId);\n if (events) {\n this._sessionCacheEventCount = Math.max(\n 0,\n this._sessionCacheEventCount - events.length\n );\n this._sessionCache.delete(sessionId);\n }\n }\n\n /** Remove and return up to `count` events from the buffer. */\n drain(count: number): CanaryEvent[] {\n const n = Math.min(count, this._buffer.length);\n return this._buffer.splice(0, n);\n }\n\n /** Get all buffered events (without draining). For reporter access. */\n peek(): CanaryEvent[] {\n return [...this._buffer];\n }\n\n /** Number of buffered events. */\n get length(): number {\n return this._buffer.length;\n }\n\n /** Drain a batch and send to backend. */\n async flush(): Promise<void> {\n const events = this.drain(BATCH_SIZE);\n if (events.length === 0) return;\n\n for (let attempt = 0; attempt < RETRY_ATTEMPTS; attempt++) {\n try {\n await this._send(events);\n return;\n } catch {\n if (attempt < RETRY_ATTEMPTS - 1) {\n await sleep(RETRY_BACKOFF_MS[attempt]);\n }\n }\n }\n // All retries failed — events are lost (no disk persistence in Node SDK v1)\n }\n\n /** Stop flush timer and drain all remaining events. */\n async shutdown(): Promise<void> {\n this._stopped = true;\n if (this._timer) {\n clearInterval(this._timer);\n this._timer = null;\n }\n\n while (this._buffer.length > 0) {\n const events = this.drain(BATCH_SIZE);\n if (events.length === 0) break;\n\n for (let attempt = 0; attempt < RETRY_ATTEMPTS; attempt++) {\n try {\n await this._send(events);\n break;\n } catch {\n if (attempt === RETRY_ATTEMPTS - 1) {\n // Final failure — events lost\n } else {\n await sleep(RETRY_BACKOFF_MS[attempt]);\n }\n }\n }\n }\n }\n\n /** POST events to the backend with gzip compression. */\n private _send(events: CanaryEvent[]): Promise<void> {\n const payload = JSON.stringify({\n events,\n sdk_version: \"0.1.0\",\n });\n const compressed = gzipSync(Buffer.from(payload, \"utf-8\"));\n\n const url = new URL(`${this._endpoint}/v1/events`);\n const isHttps = url.protocol === \"https:\";\n const requestFn = isHttps ? this._originalHttpsRequest : this._originalHttpRequest;\n\n return new Promise<void>((resolve, reject) => {\n const req = requestFn(\n {\n hostname: url.hostname,\n port: url.port || (isHttps ? 443 : 80),\n path: url.pathname,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Content-Encoding\": \"gzip\",\n Authorization: `Bearer ${this._apiKey}`,\n \"Content-Length\": compressed.length,\n },\n timeout: 5000,\n },\n (res) => {\n // Consume the response\n res.resume();\n if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {\n resolve();\n } else {\n reject(new Error(`HTTP ${res.statusCode}`));\n }\n }\n );\n\n req.on(\"error\", reject);\n req.on(\"timeout\", () => {\n req.destroy();\n reject(new Error(\"Request timeout\"));\n });\n req.write(compressed);\n req.end();\n });\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * Session correlation via AsyncLocalStorage.\n *\n * Node.js equivalent of sdk/src/canary/context.py using\n * AsyncLocalStorage instead of ContextVar.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { randomUUID } from \"node:crypto\";\nimport { detectAgent } from \"./agent-detect.js\";\n\ninterface SessionContext {\n frameworkSessionId?: string;\n}\n\nconst sessionStorage = new AsyncLocalStorage<SessionContext>();\nconst sdkInstanceId = randomUUID().replace(/-/g, \"\");\nlet callSequence = 0;\nconst defaultContext: SessionContext = {\n frameworkSessionId: undefined,\n};\n\nfunction getMutableContext(): SessionContext {\n return sessionStorage.getStore() ?? defaultContext;\n}\n\nfunction nextCallSequence(): number {\n callSequence += 1;\n return callSequence;\n}\n\nexport interface SessionSignals {\n sdk_instance_id: string;\n process_id: number;\n parent_process_id?: number;\n process_kind?: string;\n process_label?: string;\n thread_id: number;\n call_sequence: number;\n framework_session_id?: string;\n agent_name?: string;\n agent_version?: string;\n}\n\nfunction getBaseSignals(): Omit<SessionSignals, \"call_sequence\"> {\n const ctx = getMutableContext();\n const configuredParentProcessId = Number.parseInt(\n process.env.ARDEA_PARENT_PROCESS_ID || process.env.CANARY_PARENT_PROCESS_ID || \"\",\n 10\n );\n const configuredProcessKind = process.env.ARDEA_PROCESS_KIND || process.env.CANARY_PROCESS_KIND || \"agent\";\n const configuredProcessLabel = process.env.ARDEA_PROCESS_LABEL || process.env.CANARY_PROCESS_LABEL || undefined;\n const signals: Omit<SessionSignals, \"call_sequence\"> = {\n sdk_instance_id: sdkInstanceId,\n process_id: process.pid,\n thread_id: 0,\n };\n\n if (Number.isFinite(configuredParentProcessId)) {\n signals.parent_process_id = configuredParentProcessId;\n }\n if (configuredProcessKind) {\n signals.process_kind = configuredProcessKind;\n }\n if (configuredProcessLabel) {\n signals.process_label = configuredProcessLabel;\n }\n\n if (ctx.frameworkSessionId) {\n signals.framework_session_id = ctx.frameworkSessionId;\n }\n\n const agentInfo = detectAgent();\n if (agentInfo.agent_name) {\n signals.agent_name = agentInfo.agent_name;\n }\n if (agentInfo.agent_version) {\n signals.agent_version = agentInfo.agent_version;\n }\n\n return signals;\n}\n\n/**\n * Return session correlation signals for the current execution context.\n */\nexport function getSessionSignals(): SessionSignals {\n return {\n ...getBaseSignals(),\n call_sequence: nextCallSequence(),\n };\n}\n\n/**\n * Set the framework session ID for the current async context.\n *\n * If no async context is active, this becomes the default session ID used\n * by subsequent feedback/reporting calls on this process.\n */\nexport function setFrameworkSession(sessionId: string): void {\n getMutableContext().frameworkSessionId = sessionId;\n}\n\n/**\n * Clear the framework session ID for the current async context.\n */\nexport function clearFrameworkSession(): void {\n getMutableContext().frameworkSessionId = undefined;\n}\n\n/**\n * Run a function with a scoped framework session ID.\n *\n * The session ID propagates through async call chains automatically.\n */\nexport function runWithSession<T>(sessionId: string, fn: () => T): T {\n return sessionStorage.run(\n {\n frameworkSessionId: sessionId,\n },\n fn\n );\n}\n\n/**\n * Reset all context state. For testing only.\n */\nexport function resetContext(): void {\n callSequence = 0;\n defaultContext.frameworkSessionId = undefined;\n}\n","/**\n * Detect which AI agent framework is running via environment variables.\n */\n\nexport interface AgentInfo {\n agent_name: string | null;\n agent_version: string | null;\n}\n\nexport const AGENT_ENV_MAP: ReadonlyArray<{ envKey: string; name: string; versionKey?: string; useValue?: boolean }> = [\n { envKey: \"ARDEA_AGENT_NAME\", name: \"\", useValue: true },\n { envKey: \"CANARY_AGENT_NAME\", name: \"\", useValue: true },\n { envKey: \"CLAUDE_CODE\", name: \"claude_code\" },\n { envKey: \"CURSOR_TRACE_ID\", name: \"cursor\" },\n { envKey: \"WINDSURF_SESSION_ID\", name: \"windsurf\" },\n { envKey: \"CLINE_TASK_ID\", name: \"cline\" },\n { envKey: \"AIDER_VERSION\", name: \"aider\", versionKey: \"AIDER_VERSION\" },\n { envKey: \"DEVIN_SESSION_ID\", name: \"devin\" },\n { envKey: \"CODEX_CLI\", name: \"codex\" },\n { envKey: \"GITHUB_COPILOT\", name: \"github_copilot\" },\n { envKey: \"CODY_SESSION_ID\", name: \"sourcegraph_cody\" },\n];\n\nlet cachedInfo: AgentInfo | null = null;\n\nexport function detectAgent(): AgentInfo {\n if (cachedInfo) return cachedInfo;\n\n for (const entry of AGENT_ENV_MAP) {\n const val = process.env[entry.envKey];\n // Check !== undefined so empty strings (presence flags) still detect\n if (val !== undefined) {\n cachedInfo = {\n agent_name: entry.useValue ? (val || null) : entry.name,\n agent_version: entry.versionKey ? (process.env[entry.versionKey] ?? null) : null,\n };\n if (cachedInfo.agent_name) return cachedInfo;\n }\n }\n\n cachedInfo = { agent_name: null, agent_version: null };\n return cachedInfo;\n}\n\nexport function resetAgentCache(): void {\n cachedInfo = null;\n}\n","/**\n * Auto-redeem a voucher code on SDK init.\n * Non-blocking — fires in the background, never delays SDK startup.\n * Tracks redeemed codes in ~/.ardea/redeemed-vouchers.json.\n */\n\nimport { readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\ntype VoucherRedeemResponse = {\n credits?: number;\n vendor?: string;\n};\n\nfunction getRedeemedPath(): string {\n const dir = join(homedir(), \".ardea\");\n mkdirSync(dir, { recursive: true });\n return join(dir, \"redeemed-vouchers.json\");\n}\n\nfunction getRedeemedSet(): Set<string> {\n try {\n const data = JSON.parse(readFileSync(getRedeemedPath(), \"utf8\"));\n return new Set(Array.isArray(data) ? data : []);\n } catch {\n return new Set();\n }\n}\n\nfunction markRedeemed(code: string): void {\n const set = getRedeemedSet();\n set.add(code);\n writeFileSync(getRedeemedPath(), JSON.stringify([...set]));\n}\n\nexport function autoRedeemVoucher(\n apiKey: string,\n endpoint: string,\n code: string,\n): void {\n if (!code.startsWith(\"CVR-\")) return;\n\n const redeemed = getRedeemedSet();\n if (redeemed.has(code)) return;\n\n // Fire-and-forget — don't block SDK init\n fetch(`${endpoint}/v1/vouchers/redeem`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ code }),\n })\n .then(async (res) => {\n if (res.ok) {\n const data = (await res.json()) as VoucherRedeemResponse;\n markRedeemed(code);\n // Log to stderr to avoid corrupting any MCP stdio\n console.error(\n `[ardea] Voucher redeemed: +${data.credits ?? 0} ${data.vendor ?? \"unknown\"} credits`,\n );\n } else if (res.status === 410) {\n // Already redeemed, expired, or revoked — mark locally to avoid retries\n markRedeemed(code);\n }\n })\n .catch(() => {\n // Silently fail — SDK init must not be affected\n });\n}\n","/**\n * ardea — MCP tools for AI agents to report API experiences.\n */\n\nimport { EventBuffer } from \"./buffer.js\";\nimport {\n runWithSession,\n setFrameworkSession,\n clearFrameworkSession,\n getSessionSignals,\n} from \"./context.js\";\nimport type { CanaryConfig, CanaryEvent, FeedbackEvent } from \"./types.js\";\nimport { DEFAULT_ENDPOINT } from \"./constants.js\";\nimport { autoRedeemVoucher } from \"./voucher.js\";\n\nlet _initialized = false;\nlet _buffer: EventBuffer | null = null;\n\n/**\n * Initialize Canary SDK for manual feedback submission.\n */\nexport function init(config: CanaryConfig): void {\n if (_initialized) return;\n\n if (!config.apiKey.startsWith(\"cnry_sk_\")) {\n throw new Error(\n \"Invalid API key: must start with 'cnry_sk_'. \" +\n \"Get your key at https://app.canary.dev\"\n );\n }\n\n const endpoint = config.endpoint ?? DEFAULT_ENDPOINT;\n const autoFlush = config.autoFlush ?? true;\n\n _buffer = new EventBuffer(config.apiKey, endpoint, autoFlush);\n _initialized = true;\n\n // Auto-redeem voucher if ARDEA_VOUCHER or CANARY_VOUCHER env var is set (non-blocking)\n const voucherCode = process.env.ARDEA_VOUCHER || process.env.CANARY_VOUCHER;\n if (voucherCode) {\n autoRedeemVoucher(config.apiKey, endpoint, voucherCode);\n }\n}\n\n/**\n * Shut down the SDK and flush any queued feedback events.\n */\nexport async function shutdown(): Promise<void> {\n if (!_initialized || !_buffer) return;\n\n await _buffer.shutdown();\n _buffer = null;\n _initialized = false;\n}\n\n/**\n * Submit manual feedback.\n */\nexport function survey(options: {\n worked?: boolean;\n context?: string;\n frictionPoints?: string[];\n provider?: string;\n sessionId?: string;\n}): void {\n if (!_buffer) {\n throw new Error(\"Canary SDK not initialized. Call init() first.\");\n }\n\n // Pick up session context from AsyncLocalStorage (set by runWithSession)\n const signals = getSessionSignals();\n const event: FeedbackEvent = {\n event_type: \"feedback\",\n source_plane: \"declared\",\n capture_mode: \"declared\",\n source: \"manual\",\n worked: options.worked ?? true,\n context: options.context ?? \"\",\n ts: Date.now() / 1000,\n agent_name: signals.agent_name ?? undefined,\n agent_version: signals.agent_version ?? undefined,\n framework_session_id: options.sessionId ?? signals.framework_session_id ?? undefined,\n };\n\n if (options.frictionPoints && options.frictionPoints.length > 0) {\n event.friction_points = options.frictionPoints;\n }\n if (options.provider) {\n event.provider = options.provider;\n }\n\n _buffer.push(event);\n}\n\n/**\n * Get the global EventBuffer (for advanced usage / adapters).\n */\nexport function getBuffer(): EventBuffer | null {\n return _buffer;\n}\n\n// Re-export session helpers\nexport {\n runWithSession,\n setFrameworkSession,\n clearFrameworkSession,\n getSessionSignals,\n};\n\n// Re-export types\nexport type { ArdeaConfig, CanaryConfig, CanaryEvent, FeedbackEvent } from \"./types.js\";\nexport { detectAgent, resetAgentCache } from \"./agent-detect.js\";\n\n/** Alias for init — Ardea rebrand entry point. */\nexport const Ardea = { init, shutdown, survey, getBuffer };\n\n/** @deprecated Use Ardea instead. */\nexport const Canary = Ardea;\n"]}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  DEFAULT_ENDPOINT
3
- } from "./chunk-UL6WUTYP.js";
3
+ } from "./chunk-NE3455WO.js";
4
4
 
5
5
  // src/buffer.ts
6
6
  import { gzipSync } from "zlib";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
3
3
 
4
- var _chunkOREJ23CVcjs = require('../chunk-OREJ23CV.cjs');
4
+ var _chunkSIKOQ77Qcjs = require('../chunk-SIKOQ77Q.cjs');
5
5
 
6
6
  // src/journal/server.ts
7
7
  var _indexjs = require('@modelcontextprotocol/sdk/server/index.js');
@@ -87,7 +87,7 @@ function saveConfig(apiKey, endpoint) {
87
87
  JSON.stringify({ apiKey, endpoint, savedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
88
88
  );
89
89
  }
90
- var PRODUCTION_ENDPOINT = _chunkOREJ23CVcjs.DEFAULT_ENDPOINT;
90
+ var PRODUCTION_ENDPOINT = _chunkSIKOQ77Qcjs.DEFAULT_ENDPOINT;
91
91
  function openBrowser(url) {
92
92
  const platform = process.platform;
93
93
  const cmd = platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open";
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/stalapaneni/conductor/workspaces/canary/bucharest/canary/dist/journal/server.cjs","../../src/journal/server.ts"],"names":[],"mappings":"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","file":"/Users/stalapaneni/conductor/workspaces/canary/bucharest/canary/dist/journal/server.cjs","sourcesContent":[null,"#!/usr/bin/env node\n/**\n * Ardea MCP Journal Server — stdio transport.\n *\n * Tools:\n * ardea_annotate — Save/read/list/clear local notes about API experiences.\n * ardea_report — Submit structured feedback to the Ardea 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\";\nimport { exec } from \"node:child_process\";\nimport { DEFAULT_ENDPOINT } from \"../constants.js\";\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(), \".ardea\", \"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// ─── Config persistence (~/.ardea/config.json) ──────────────────────\n\nfunction getConfigPath(): string {\n return join(homedir(), \".ardea\", \"config.json\");\n}\n\nfunction loadSavedConfig(): { apiKey?: string; endpoint?: string } | null {\n try {\n const data = JSON.parse(readFileSync(getConfigPath(), \"utf8\"));\n return data;\n } catch {\n return null;\n }\n}\n\nfunction saveConfig(apiKey: string, endpoint: string): void {\n const dir = join(homedir(), \".ardea\");\n mkdirSync(dir, { recursive: true });\n writeFileSync(\n getConfigPath(),\n JSON.stringify({ apiKey, endpoint, savedAt: new Date().toISOString() }, null, 2)\n );\n}\n\n// ─── Browser auth (onboarding flow) ─────────────────────────────────\n\nconst PRODUCTION_ENDPOINT = DEFAULT_ENDPOINT;\n\nfunction openBrowser(url: string): void {\n const platform = process.platform;\n const cmd =\n platform === \"darwin\" ? \"open\" :\n platform === \"win32\" ? \"start\" :\n \"xdg-open\";\n exec(`${cmd} \"${url}\"`, (err) => {\n if (err) console.error(`Could not open browser: ${err.message}`);\n });\n}\n\nasync function browserAuth(endpoint: string): Promise<string | null> {\n console.error(\"No API key found. Starting browser signup...\");\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n\n let initData: { plugin_secret: string; onboarding_url: string };\n try {\n const res = await fetch(`${endpoint}/v1/onboarding/init`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n signal: controller.signal,\n });\n if (!res.ok) {\n console.error(`Onboarding init failed: ${res.status}`);\n return null;\n }\n initData = (await res.json()) as any;\n } catch (err: any) {\n console.error(`Could not reach backend for onboarding: ${err.message}`);\n return null;\n } finally {\n clearTimeout(timeout);\n }\n\n console.error(`Opening browser: ${initData.onboarding_url}`);\n openBrowser(initData.onboarding_url);\n\n // Poll for claim (every 2s, up to 5 minutes)\n const maxAttempts = 150;\n for (let i = 0; i < maxAttempts; i++) {\n await new Promise((r) => setTimeout(r, 2000));\n\n try {\n const res = await fetch(`${endpoint}/v1/onboarding/claim`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ plugin_secret: initData.plugin_secret }),\n });\n const data = (await res.json()) as any;\n\n if (data.api_key) {\n console.error(\"Signup complete — API key received.\");\n saveConfig(data.api_key, endpoint);\n return data.api_key;\n }\n\n if (data.status === \"expired\") {\n console.error(\"Onboarding token expired.\");\n return null;\n }\n } catch {\n // Backend unreachable during poll — keep trying\n }\n }\n\n console.error(\"Onboarding timed out after 5 minutes.\");\n return null;\n}\n\n// ─── Tool definitions ────────────────────────────────────────────────\n\nconst ARDEA_ANNOTATE_TOOL = {\n name: \"ardea_annotate\",\n description:\n \"Record an API observation: endpoint, status, latency, errors, surprises.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n id: {\n type: \"string\",\n description:\n 'Annotation ID like \"stripe/charges\".',\n },\n intent: {\n type: \"string\",\n description:\n \"Before the call: goal and reason.\",\n },\n note: {\n type: \"string\",\n description:\n \"After the call: result, errors, latency, next action.\",\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 ARDEA_REPORT_TOOL = {\n name: \"ardea_report\",\n description:\n \"Final step: submit the API experience report.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n session_id: {\n type: \"string\",\n description:\n \"Session or run ID.\",\n },\n intent: {\n type: \"string\",\n description:\n \"Original plan.\",\n },\n task_goal: {\n type: \"string\",\n description:\n \"Task goal.\",\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 \"APIs and endpoints used.\",\n },\n what_worked: {\n type: \"string\",\n description:\n \"What worked.\",\n },\n what_failed: {\n type: \"string\",\n description:\n \"What failed, with status and errors.\",\n },\n error_handling: {\n type: \"string\",\n description:\n \"How you handled failures.\",\n },\n service_switch: {\n type: \"string\",\n description:\n \"Any switch to another service and why.\",\n },\n friction: {\n type: \"string\",\n description:\n \"Biggest pain point.\",\n },\n suggestion: {\n type: \"string\",\n description:\n \"Best improvement suggestion.\",\n },\n },\n required: [\"task_goal\", \"outcome\", \"apis_used\"],\n },\n};\n\n// ─── Server config ───────────────────────────────────────────────────\n\nexport interface 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.ARDEA_API_KEY || process.env.CANARY_API_KEY || \"\";\n let endpoint = process.env.ARDEA_ENDPOINT || process.env.CANARY_ENDPOINT || PRODUCTION_ENDPOINT;\n let sessionId = process.env.ARDEA_SESSION_ID || process.env.CANARY_SESSION_ID;\n let agentName = process.env.ARDEA_AGENT_NAME || 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 // Fallback: check saved config from browser auth\n if (!apiKey) {\n const saved = loadSavedConfig();\n if (saved?.apiKey) {\n apiKey = saved.apiKey;\n if (saved.endpoint) endpoint = saved.endpoint;\n console.error(\"Loaded API key from ~/.ardea/config.json\");\n }\n }\n\n return { apiKey, endpoint, sessionId, agentName };\n}\n\n// ─── Backend communication ───────────────────────────────────────────\n\nexport async 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\nexport function 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\nexport function 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 if (args.intent) contextParts.push(`Intent: ${args.intent}`);\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.error_handling) contextParts.push(`Error Handling: ${args.error_handling}`);\n if (args.service_switch) contextParts.push(`Service Switch: ${args.service_switch}`);\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 if (args.error_handling) frictionPoints.push(`Error handling: ${args.error_handling}`);\n if (args.service_switch) frictionPoints.push(`Service switch: ${args.service_switch}`);\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\nexport async 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(\"ardea_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\nconst INSTRUCTIONS_AUTHENTICATED =\n \"After each HTTP/API call, use ardea_annotate to capture endpoint, status, response shape, errors, and surprises.\\n\\n\" +\n \"When done, use ardea_report with task goal, outcome, APIs used, what worked, what failed, friction, and a suggestion.\";\n\nconst INSTRUCTIONS_SETUP =\n \"Ardea is not yet configured. \" +\n \"A browser window should have opened for signup. \" +\n \"Once signup is complete, Ardea tools will activate automatically.\";\n\nasync function main() {\n const config = parseArgs();\n let reportCount = 0;\n\n // If no API key, try browser auth before starting\n if (!config.apiKey) {\n const key = await browserAuth(config.endpoint || PRODUCTION_ENDPOINT);\n if (key) {\n config.apiKey = key;\n }\n }\n\n const authenticated = !!config.apiKey;\n\n const server = new Server(\n { name: \"ardea-journal\", version: \"0.3.0\" },\n {\n capabilities: { tools: {} },\n instructions: authenticated\n ? INSTRUCTIONS_AUTHENTICATED\n : INSTRUCTIONS_SETUP,\n }\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [ARDEA_ANNOTATE_TOOL, ARDEA_REPORT_TOOL],\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const args = (request.params.arguments || {}) as Record<string, any>;\n\n // Auth guard — annotate works locally without auth, others need it\n if (!config.apiKey && request.params.name !== \"ardea_annotate\") {\n return {\n content: [\n {\n type: \"text\" as const,\n text: \"Ardea is not configured yet. Please complete signup in your browser \" +\n \"or set the ARDEA_API_KEY environment variable, then restart.\",\n },\n ],\n isError: true,\n };\n }\n\n switch (request.params.name) {\n case \"ardea_annotate\":\n return handleAnnotate(args);\n\n case \"ardea_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(\"Ardea journal server v0.3.0 starting...\");\n console.error(` Endpoint: ${config.endpoint}`);\n console.error(` Auth: ${authenticated ? \"configured\" : \"not configured\"}`);\n console.error(` Session: ${config.sessionId || \"(auto)\"}`);\n console.error(` Agent: ${config.agentName || \"(unnamed)\"}`);\n console.error(` Tools: ardea_annotate, ardea_report`);\n await server.connect(transport);\n console.error(\"Ardea journal server connected via stdio\");\n}\n\n// Only auto-run when executed directly (not when imported for testing)\nimport { fileURLToPath } from \"node:url\";\nimport { realpathSync } from \"node:fs\";\n\nfunction checkDirectRun(): boolean {\n try {\n const scriptPath = realpathSync(process.argv[1] || \"\");\n const modulePath = realpathSync(fileURLToPath(import.meta.url));\n return scriptPath === modulePath;\n } catch {\n return false;\n }\n}\n\nif (checkDirectRun()) {\n main().catch((err) => {\n console.error(\"Fatal:\", err);\n process.exit(1);\n });\n}\n"]}
1
+ {"version":3,"sources":["/Users/stalapaneni/conductor/workspaces/canary/banjul/canary/dist/journal/server.cjs","../../src/journal/server.ts"],"names":[],"mappings":"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","file":"/Users/stalapaneni/conductor/workspaces/canary/banjul/canary/dist/journal/server.cjs","sourcesContent":[null,"#!/usr/bin/env node\n/**\n * Ardea MCP Journal Server — stdio transport.\n *\n * Tools:\n * ardea_annotate — Save/read/list/clear local notes about API experiences.\n * ardea_report — Submit structured feedback to the Ardea 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\";\nimport { exec } from \"node:child_process\";\nimport { DEFAULT_ENDPOINT } from \"../constants.js\";\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(), \".ardea\", \"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// ─── Config persistence (~/.ardea/config.json) ──────────────────────\n\nfunction getConfigPath(): string {\n return join(homedir(), \".ardea\", \"config.json\");\n}\n\nfunction loadSavedConfig(): { apiKey?: string; endpoint?: string } | null {\n try {\n const data = JSON.parse(readFileSync(getConfigPath(), \"utf8\"));\n return data;\n } catch {\n return null;\n }\n}\n\nfunction saveConfig(apiKey: string, endpoint: string): void {\n const dir = join(homedir(), \".ardea\");\n mkdirSync(dir, { recursive: true });\n writeFileSync(\n getConfigPath(),\n JSON.stringify({ apiKey, endpoint, savedAt: new Date().toISOString() }, null, 2)\n );\n}\n\n// ─── Browser auth (onboarding flow) ─────────────────────────────────\n\nconst PRODUCTION_ENDPOINT = DEFAULT_ENDPOINT;\n\nfunction openBrowser(url: string): void {\n const platform = process.platform;\n const cmd =\n platform === \"darwin\" ? \"open\" :\n platform === \"win32\" ? \"start\" :\n \"xdg-open\";\n exec(`${cmd} \"${url}\"`, (err) => {\n if (err) console.error(`Could not open browser: ${err.message}`);\n });\n}\n\nasync function browserAuth(endpoint: string): Promise<string | null> {\n console.error(\"No API key found. Starting browser signup...\");\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n\n let initData: { plugin_secret: string; onboarding_url: string };\n try {\n const res = await fetch(`${endpoint}/v1/onboarding/init`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n signal: controller.signal,\n });\n if (!res.ok) {\n console.error(`Onboarding init failed: ${res.status}`);\n return null;\n }\n initData = (await res.json()) as any;\n } catch (err: any) {\n console.error(`Could not reach backend for onboarding: ${err.message}`);\n return null;\n } finally {\n clearTimeout(timeout);\n }\n\n console.error(`Opening browser: ${initData.onboarding_url}`);\n openBrowser(initData.onboarding_url);\n\n // Poll for claim (every 2s, up to 5 minutes)\n const maxAttempts = 150;\n for (let i = 0; i < maxAttempts; i++) {\n await new Promise((r) => setTimeout(r, 2000));\n\n try {\n const res = await fetch(`${endpoint}/v1/onboarding/claim`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ plugin_secret: initData.plugin_secret }),\n });\n const data = (await res.json()) as any;\n\n if (data.api_key) {\n console.error(\"Signup complete — API key received.\");\n saveConfig(data.api_key, endpoint);\n return data.api_key;\n }\n\n if (data.status === \"expired\") {\n console.error(\"Onboarding token expired.\");\n return null;\n }\n } catch {\n // Backend unreachable during poll — keep trying\n }\n }\n\n console.error(\"Onboarding timed out after 5 minutes.\");\n return null;\n}\n\n// ─── Tool definitions ────────────────────────────────────────────────\n\nconst ARDEA_ANNOTATE_TOOL = {\n name: \"ardea_annotate\",\n description:\n \"Record an API observation: endpoint, status, latency, errors, surprises.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n id: {\n type: \"string\",\n description:\n 'Annotation ID like \"stripe/charges\".',\n },\n intent: {\n type: \"string\",\n description:\n \"Before the call: goal and reason.\",\n },\n note: {\n type: \"string\",\n description:\n \"After the call: result, errors, latency, next action.\",\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 ARDEA_REPORT_TOOL = {\n name: \"ardea_report\",\n description:\n \"Final step: submit the API experience report.\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n session_id: {\n type: \"string\",\n description:\n \"Session or run ID.\",\n },\n intent: {\n type: \"string\",\n description:\n \"Original plan.\",\n },\n task_goal: {\n type: \"string\",\n description:\n \"Task goal.\",\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 \"APIs and endpoints used.\",\n },\n what_worked: {\n type: \"string\",\n description:\n \"What worked.\",\n },\n what_failed: {\n type: \"string\",\n description:\n \"What failed, with status and errors.\",\n },\n error_handling: {\n type: \"string\",\n description:\n \"How you handled failures.\",\n },\n service_switch: {\n type: \"string\",\n description:\n \"Any switch to another service and why.\",\n },\n friction: {\n type: \"string\",\n description:\n \"Biggest pain point.\",\n },\n suggestion: {\n type: \"string\",\n description:\n \"Best improvement suggestion.\",\n },\n },\n required: [\"task_goal\", \"outcome\", \"apis_used\"],\n },\n};\n\n// ─── Server config ───────────────────────────────────────────────────\n\nexport interface 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.ARDEA_API_KEY || process.env.CANARY_API_KEY || \"\";\n let endpoint = process.env.ARDEA_ENDPOINT || process.env.CANARY_ENDPOINT || PRODUCTION_ENDPOINT;\n let sessionId = process.env.ARDEA_SESSION_ID || process.env.CANARY_SESSION_ID;\n let agentName = process.env.ARDEA_AGENT_NAME || 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 // Fallback: check saved config from browser auth\n if (!apiKey) {\n const saved = loadSavedConfig();\n if (saved?.apiKey) {\n apiKey = saved.apiKey;\n if (saved.endpoint) endpoint = saved.endpoint;\n console.error(\"Loaded API key from ~/.ardea/config.json\");\n }\n }\n\n return { apiKey, endpoint, sessionId, agentName };\n}\n\n// ─── Backend communication ───────────────────────────────────────────\n\nexport async 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\nexport function 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\nexport function 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 if (args.intent) contextParts.push(`Intent: ${args.intent}`);\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.error_handling) contextParts.push(`Error Handling: ${args.error_handling}`);\n if (args.service_switch) contextParts.push(`Service Switch: ${args.service_switch}`);\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 if (args.error_handling) frictionPoints.push(`Error handling: ${args.error_handling}`);\n if (args.service_switch) frictionPoints.push(`Service switch: ${args.service_switch}`);\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\nexport async 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(\"ardea_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\nconst INSTRUCTIONS_AUTHENTICATED =\n \"After each HTTP/API call, use ardea_annotate to capture endpoint, status, response shape, errors, and surprises.\\n\\n\" +\n \"When done, use ardea_report with task goal, outcome, APIs used, what worked, what failed, friction, and a suggestion.\";\n\nconst INSTRUCTIONS_SETUP =\n \"Ardea is not yet configured. \" +\n \"A browser window should have opened for signup. \" +\n \"Once signup is complete, Ardea tools will activate automatically.\";\n\nasync function main() {\n const config = parseArgs();\n let reportCount = 0;\n\n // If no API key, try browser auth before starting\n if (!config.apiKey) {\n const key = await browserAuth(config.endpoint || PRODUCTION_ENDPOINT);\n if (key) {\n config.apiKey = key;\n }\n }\n\n const authenticated = !!config.apiKey;\n\n const server = new Server(\n { name: \"ardea-journal\", version: \"0.3.0\" },\n {\n capabilities: { tools: {} },\n instructions: authenticated\n ? INSTRUCTIONS_AUTHENTICATED\n : INSTRUCTIONS_SETUP,\n }\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [ARDEA_ANNOTATE_TOOL, ARDEA_REPORT_TOOL],\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const args = (request.params.arguments || {}) as Record<string, any>;\n\n // Auth guard — annotate works locally without auth, others need it\n if (!config.apiKey && request.params.name !== \"ardea_annotate\") {\n return {\n content: [\n {\n type: \"text\" as const,\n text: \"Ardea is not configured yet. Please complete signup in your browser \" +\n \"or set the ARDEA_API_KEY environment variable, then restart.\",\n },\n ],\n isError: true,\n };\n }\n\n switch (request.params.name) {\n case \"ardea_annotate\":\n return handleAnnotate(args);\n\n case \"ardea_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(\"Ardea journal server v0.3.0 starting...\");\n console.error(` Endpoint: ${config.endpoint}`);\n console.error(` Auth: ${authenticated ? \"configured\" : \"not configured\"}`);\n console.error(` Session: ${config.sessionId || \"(auto)\"}`);\n console.error(` Agent: ${config.agentName || \"(unnamed)\"}`);\n console.error(` Tools: ardea_annotate, ardea_report`);\n await server.connect(transport);\n console.error(\"Ardea journal server connected via stdio\");\n}\n\n// Only auto-run when executed directly (not when imported for testing)\nimport { fileURLToPath } from \"node:url\";\nimport { realpathSync } from \"node:fs\";\n\nfunction checkDirectRun(): boolean {\n try {\n const scriptPath = realpathSync(process.argv[1] || \"\");\n const modulePath = realpathSync(fileURLToPath(import.meta.url));\n return scriptPath === modulePath;\n } catch {\n return false;\n }\n}\n\nif (checkDirectRun()) {\n main().catch((err) => {\n console.error(\"Fatal:\", err);\n process.exit(1);\n });\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  DEFAULT_ENDPOINT
4
- } from "../chunk-UL6WUTYP.js";
4
+ } from "../chunk-NE3455WO.js";
5
5
 
6
6
  // src/journal/server.ts
7
7
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ardea",
3
- "version": "0.3.1",
4
- "description": "MCP tools for AI agents to report API experiences",
3
+ "version": "0.3.3",
4
+ "description": "MCP tools for AI agents to report API experiences — install: claude mcp add ardea -- npx -y ardea",
5
5
  "license": "MIT",
6
6
  "engines": {
7
7
  "node": ">=18.0.0"
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/Users/stalapaneni/conductor/workspaces/canary/bucharest/canary/dist/chunk-OREJ23CV.cjs","../src/constants.ts"],"names":[],"mappings":"AAAA;ACAO,IAAM,iBAAA,EACX,yCAAA;ADCF;AACA;AACE;AACF,4CAAC","file":"/Users/stalapaneni/conductor/workspaces/canary/bucharest/canary/dist/chunk-OREJ23CV.cjs","sourcesContent":[null,"export const DEFAULT_ENDPOINT =\n \"https://ardea-production.up.railway.app\";\n"]}
@@ -1,7 +0,0 @@
1
- // src/constants.ts
2
- var DEFAULT_ENDPOINT = "https://ardea-production.up.railway.app";
3
-
4
- export {
5
- DEFAULT_ENDPOINT
6
- };
7
- //# sourceMappingURL=chunk-UL6WUTYP.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/constants.ts"],"sourcesContent":["export const DEFAULT_ENDPOINT =\n \"https://ardea-production.up.railway.app\";\n"],"mappings":";AAAO,IAAM,mBACX;","names":[]}