New:Socket for Asana Is Now Available.Learn more
Get Started

@deerdawn/mcp-server

Package Overview
Dependencies
Maintainers
1
Versions
42
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@deerdawn/mcp-server - npm Package Compare versions

Comparing version
1.0.37
to
1.0.38
+593
dist/chunk-P2F6BVEV.js
#!/usr/bin/env node
import { createRequire } from "module";
const require = createRequire(import.meta.url);
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
if (typeof require !== "undefined") return require.apply(this, arguments);
throw Error('Dynamic require of "' + x + '" is not supported');
});
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __commonJS = (cb, mod) => function __require2() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
// node_modules/tsup/assets/esm_shims.js
import path from "path";
import { fileURLToPath } from "url";
var init_esm_shims = __esm({
"node_modules/tsup/assets/esm_shims.js"() {
"use strict";
}
});
// src/client.ts
init_esm_shims();
import axios from "axios";
import http from "http";
import https from "https";
import fs2 from "fs";
import path2 from "path";
import os from "os";
// src/utils/safe-fs.ts
init_esm_shims();
import fs from "fs";
function isUnsafeSymlink(filePath) {
try {
return fs.lstatSync(filePath).isSymbolicLink();
} catch {
return false;
}
}
function safeWriteFileSync(filePath, data, options) {
if (isUnsafeSymlink(filePath)) {
console.error(`[DeerDawn] Refusing to write through symlink at ${filePath} \u2014 remove it manually if this is expected.`);
return false;
}
fs.writeFileSync(filePath, data, options);
return true;
}
// src/client.ts
function isLocalApiUrl(apiUrl) {
try {
const host = new URL(apiUrl).hostname;
return host === "localhost" || host === "127.0.0.1" || host === "::1";
} catch {
return false;
}
}
function getDeerdawnHome() {
return process.env.DEERDAWN_HOME || path2.join(os.homedir(), ".deerdawn");
}
function getQueuePath() {
return path2.join(getDeerdawnHome(), "cloud-write-queue.json");
}
function ensureDeerdawnHome() {
fs2.mkdirSync(getDeerdawnHome(), { recursive: true });
}
function readQueueState() {
try {
return JSON.parse(fs2.readFileSync(getQueuePath(), "utf8"));
} catch {
return { entries: [] };
}
}
function writeQueueState(state) {
ensureDeerdawnHome();
safeWriteFileSync(getQueuePath(), JSON.stringify(state, null, 2));
}
function mapOrgUsageToSettings(data) {
const plan = data?.plan || {};
return {
org_id: plan.org_id || "",
name: data?.org_name || "",
plan: typeof plan.tier === "string" ? plan.tier : "free",
fail_mode: plan.fail_mode === "open" ? "open" : "closed",
redaction_mode: plan.redaction_mode === "permissive" ? "permissive" : "strict",
is_locked: !!plan.is_locked,
shadow_mode: !!plan.shadow_mode,
redaction_keys: Array.isArray(plan.redaction_keys) ? plan.redaction_keys : [],
created_at: (/* @__PURE__ */ new Date()).toISOString(),
usage: data?.usage,
limits: data?.limits
};
}
var DeerdawnClient = class {
client;
cache;
config;
MAX_CACHE_SIZE = 100;
MAX_CACHE_ITEM_SIZE_MB = 5;
constructor(config) {
if (!config.apiKey || config.apiKey.trim() !== config.apiKey) {
throw new Error("Invalid API key: cannot be empty or contain whitespace");
}
if (!isLocalApiUrl(config.apiUrl) && !config.apiKey.startsWith("dd_")) {
throw new Error("Invalid API key format: must start with dd_ prefix");
}
this.config = config;
this.cache = /* @__PURE__ */ new Map();
this.client = axios.create({
baseURL: config.apiUrl,
headers: {
"x-api-key": config.apiKey,
"content-type": "application/json"
},
timeout: 3e4,
// Reuse TCP+TLS connections across calls. Without this, axios opens a
// fresh socket per request and pays a full handshake (~50ms) every time —
// multiplied across the several sequential calls a single tool can make.
httpAgent: new http.Agent({ keepAlive: true, maxSockets: 16 }),
httpsAgent: new https.Agent({ keepAlive: true, maxSockets: 16 })
});
this.client.interceptors.response.use(
(response) => response,
async (error) => {
const cfg = error.config;
const response = error.response;
if (!cfg) {
throw error;
}
if (!cfg.__retryCount) {
cfg.__retryCount = 0;
cfg.__firstRetryTimestamp = Date.now();
}
const totalRetryTime = Date.now() - (cfg.__firstRetryTimestamp || Date.now());
if (totalRetryTime > 6e4) {
throw new Error("Retry timeout exceeded (60s)");
}
const shouldRetry = (response?.status === 429 || response?.status && response.status >= 500) && cfg.__retryCount < this.config.maxRetries;
if (shouldRetry) {
cfg.__retryCount++;
const delay = Math.min(
this.config.retryBackoffMs * Math.pow(2, cfg.__retryCount - 1),
3e4
);
await new Promise((resolve) => setTimeout(resolve, delay));
return this.client.request(cfg);
}
throw error;
}
);
}
async recordProductEvent(event) {
const { data } = await this.client.post("/api/v1/product-events", event);
return data;
}
async recordContextEvent(event) {
try {
const { data } = await this.client.post("/api/v1/context/events", event);
return data;
} catch (error) {
const status = error?.response?.status;
const shouldQueue = !status || status >= 500 || error?.code === "ECONNREFUSED" || error?.code === "ENOTFOUND";
if (!shouldQueue) throw error;
const state = readQueueState();
state.entries.push({
id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
method: "POST",
path: "/api/v1/context/events",
body: event,
created_at: (/* @__PURE__ */ new Date()).toISOString()
});
writeQueueState(state);
return { queued: true, queue_size: state.entries.length, event_type: event.event_type };
}
}
async flushQueuedContextWrites() {
const state = readQueueState();
if (!state.entries.length) return { flushed: 0, remaining: 0 };
const remaining = [];
let flushed = 0;
for (const entry of state.entries) {
try {
await this.client.post(entry.path, entry.body);
flushed += 1;
} catch {
remaining.push(entry);
}
}
writeQueueState({ entries: remaining });
return { flushed, remaining: remaining.length };
}
async getOrganizationSettings() {
const { data } = await this.client.get("/api/v1/orgs/usage");
return mapOrgUsageToSettings(data);
}
async getContextGraphProject(projectId) {
const { data } = await this.client.get(`/api/v1/context/graph/projects/${encodeURIComponent(projectId)}`);
return data.graph || data;
}
async getContextGraphSession(sessionId) {
const { data } = await this.client.get(`/api/v1/context/graph/sessions/${encodeURIComponent(sessionId)}`);
return data.graph || data;
}
async getContextGraphNode(nodeId) {
const { data } = await this.client.get(`/api/v1/context/graph/nodes/${encodeURIComponent(nodeId)}`);
return data.graph || data;
}
async queryContextGraph(input) {
const { data } = await this.client.post("/api/v1/context/graph/query", input);
return data.graph || data;
}
async updateOrganizationSettings(settings) {
const { data } = await this.client.patch("/api/v1/orgs/settings", settings);
return data;
}
async getAuditLogs(_params = {}) {
const { data } = await this.client.get("/api/v1/audit");
const logs = data.logs || [];
return { logs, total: logs.length };
}
async getActionRegistry() {
const cacheKey = "registry:actions";
const cached = this.getFromCache(cacheKey);
if (cached) return cached;
try {
const { data } = await this.client.get("/api/v1/registry/actions");
const actions = data.actions || data;
if (Array.isArray(actions) && actions.length > 0) {
this.setInCache(cacheKey, actions, 3e5);
return actions;
}
} catch {
}
return this.getDefaultActionRegistry();
}
getDefaultActionRegistry() {
return [
{
action_type: "auth.login",
category: "authentication",
description: "User login attempt",
risk_level: "medium"
},
{
action_type: "auth.password_reset",
category: "authentication",
description: "Password reset request",
risk_level: "medium"
},
{
action_type: "payment.process",
category: "financial",
description: "Payment processing",
risk_level: "high"
},
{
action_type: "user.delete",
category: "data_management",
description: "User account deletion",
risk_level: "high"
},
{
action_type: "data.export",
category: "data_management",
description: "Data export request",
risk_level: "medium"
}
];
}
getFromCache(key) {
if (!this.config.cacheEnabled) return null;
const entry = this.cache.get(key);
if (!entry) return null;
if (Date.now() > entry.expires) {
this.cache.delete(key);
return null;
}
return entry.data;
}
setInCache(key, data, ttlMs) {
if (!this.config.cacheEnabled) return;
const estimatedSize = JSON.stringify(data).length;
const estimatedSizeMB = estimatedSize / (1024 * 1024);
if (estimatedSizeMB > this.MAX_CACHE_ITEM_SIZE_MB) {
console.error(`[CACHE] Skipping large object (${estimatedSizeMB.toFixed(2)}MB)`);
return;
}
if (this.cache.size >= this.MAX_CACHE_SIZE) {
const firstKey = this.cache.keys().next().value;
if (firstKey) {
this.cache.delete(firstKey);
}
}
this.cache.set(key, {
data,
expires: Date.now() + ttlMs
});
}
invalidateCache(prefix) {
for (const key of this.cache.keys()) {
if (key.startsWith(prefix)) {
this.cache.delete(key);
}
}
}
// Context sync methods
async getActiveContext(projectId, tool = "claude_code", verbosity = "standard") {
if (projectId) {
const { data: data2 } = await this.client.get(
`/api/v1/context/projects/${encodeURIComponent(projectId)}/inject`,
{ params: { tool, verbosity } }
);
return {
brief: data2.payload?.body ?? null,
project_name: projectId,
project_id: projectId,
last_updated: null
};
}
const { data } = await this.client.get("/api/v1/context/active", { params: { tool, verbosity } });
return data;
}
// Brief shape (?shape=spine) — the product-voice 4-slot view (SHIPPED / DECIDED
// / OPEN / LANDMINE). Returns the 4-slot TEXT body plus the structured
// brief_spine (per-item trust). Opt-in: never changes the default brief text.
async getActiveBrief(projectId, tool = "claude_code", verbosity = "standard") {
if (projectId) {
const { data: data2 } = await this.client.get(
`/api/v1/context/projects/${encodeURIComponent(projectId)}/inject`,
{ params: { tool, verbosity, shape: "spine" } }
);
return {
brief: data2.payload?.body ?? null,
brief_spine: data2.payload?.brief_spine ?? null,
project_name: projectId,
project_id: projectId,
last_updated: null
};
}
const { data } = await this.client.get("/api/v1/context/active", { params: { tool, verbosity, shape: "spine" } });
return data;
}
async getActiveContextByCwd(cwd, tool = "claude_code", verbosity = "standard", markSession = false, workspaceKey = null) {
const params = { cwd, tool, verbosity };
if (markSession) params.mark_session = 1;
if (workspaceKey) params.workspace_key = workspaceKey;
const { data } = await this.client.get("/api/v1/context/active", { params });
return data;
}
async pushContextUpdate(projectId, summary, tool = "claude_code", commands) {
const sessionId = `mcp-${Date.now()}`;
const [extractionResult] = await Promise.all([
this.client.post("/api/v1/context/extraction", {
session_id: sessionId,
tool,
project_id: projectId,
chunks: [{ role: "assistant", text: summary }]
}).then((r) => r.data),
commands?.length ? this.client.patch(`/api/v1/context/${encodeURIComponent(projectId)}/context`, { commands }).catch((err) => {
process.stderr.write(`[DeerDawn] commands not saved: ${err?.message ?? "unknown"}
`);
return null;
}) : Promise.resolve(null)
]);
return extractionResult;
}
async listContextProjects() {
const { data } = await this.client.get("/api/v1/context/projects");
return data;
}
async listContextProjectTree() {
const { data } = await this.client.get("/api/v1/context/projects/tree");
return data;
}
async createContextProject(projectName, description, parentProjectId) {
const { data } = await this.client.post("/api/v1/context/projects", {
project_name: projectName,
description,
...parentProjectId ? { parent_project_id: parentProjectId } : {}
});
return data;
}
async switchSubproject(projectId, workspaceKey) {
const { data } = await this.client.post("/api/v1/context/subproject/switch", {
project_id: projectId,
workspace_key: workspaceKey
});
return data;
}
async archiveProject(projectId, unarchive = false) {
const { data } = await this.client.post(
`/api/v1/context/projects/${encodeURIComponent(projectId)}/archive`,
{ unarchive }
);
return data;
}
async setProjectParent(projectId, parentProjectId) {
const { data } = await this.client.post(
`/api/v1/context/projects/${encodeURIComponent(projectId)}/parent`,
{ parent_project_id: parentProjectId ?? null }
);
return data;
}
async renameProject(projectId, projectName) {
const { data } = await this.client.post(
`/api/v1/context/projects/${encodeURIComponent(projectId)}/rename`,
{ project_name: projectName }
);
return data;
}
async getContextSections(projectId, sections, tool = "claude_code", verbosity = "standard") {
const sectionsParam = sections.join(",");
if (projectId) {
const { data: data2 } = await this.client.get(
`/api/v1/context/projects/${encodeURIComponent(projectId)}/inject`,
{ params: { tool, verbosity, sections: sectionsParam } }
);
return {
brief: data2.payload?.body ?? null,
project_name: projectId,
project_id: projectId
};
}
const { data } = await this.client.get("/api/v1/context/active", { params: { tool, verbosity, sections: sectionsParam } });
return data;
}
async getContextMap(mcpAvailable = true, cwd, workspaceKey) {
const params = { mcp: mcpAvailable ? "true" : "false" };
if (cwd) params.cwd = cwd;
if (workspaceKey) params.workspace_key = workspaceKey;
const { data } = await this.client.get("/api/v1/context/map", { params });
return data;
}
async getContextJson(projectId) {
if (projectId) {
const { data: data2 } = await this.client.get(
`/api/v1/context/projects/${encodeURIComponent(projectId)}/inject`,
{ params: { format: "json" } }
);
return data2.context ?? data2;
}
const { data } = await this.client.get("/api/v1/context/active", { params: { format: "json" } });
return data;
}
async searchContext(query, limit = 5) {
const { data } = await this.client.post("/api/v1/context/search", { query, limit });
return data;
}
async getWorkspaceOverview(params = {}) {
const query = {};
if (params.mode) query.mode = params.mode;
if (params.verbosity) query.verbosity = params.verbosity;
if (params.include?.length) query.include = params.include.join(",");
if (params.format) query.format = params.format;
const { data } = await this.client.get("/api/v1/context/workspace/overview", { params: query });
return data.overview || data;
}
async getWorkspaceEntities(params = {}) {
const query = {};
if (params.types?.length) query.types = params.types.join(",");
if (params.query) query.query = params.query;
if (params.project_id) query.project_id = params.project_id;
if (params.related_project_id) query.related_project_id = params.related_project_id;
if (params.scope) query.scope = params.scope;
if (params.surface) query.surface = params.surface;
if (params.status) query.status = params.status;
if (typeof params.include_inferred === "boolean") query.include_inferred = params.include_inferred ? 1 : 0;
if (typeof params.include_resolved === "boolean") query.include_resolved = params.include_resolved ? 1 : 0;
if (typeof params.curated_only === "boolean") query.curated_only = params.curated_only ? 1 : 0;
if (typeof params.limit === "number") query.limit = params.limit;
if (params.format) query.format = params.format;
const { data } = await this.client.get("/api/v1/context/workspace/entities", { params: query });
return data;
}
async getProjectMap(params = {}) {
const projectId = params.project_id;
if (!projectId) {
const { data: data2 } = await this.client.get("/api/v1/context/active");
if (!data2?.project_id) return null;
params.project_id = data2.project_id;
}
const query = {};
if (params.verbosity) query.verbosity = params.verbosity;
if (params.format) query.format = params.format;
const resolvedProjectId = String(params.project_id);
const { data } = await this.client.get(`/api/v1/context/projects/${encodeURIComponent(resolvedProjectId)}/map`, { params: query });
return data.project_map || data;
}
async findKnownPaths(params) {
const query = { query: params.query };
if (params.project_id) query.project_id = params.project_id;
if (typeof params.limit === "number") query.limit = params.limit;
if (params.format) query.format = params.format;
const { data } = await this.client.get("/api/v1/context/workspace/known-paths", { params: query });
return data;
}
async getNextBestContext(params = {}) {
const query = {};
if (params.tool) query.tool = params.tool;
if (params.cwd) query.cwd = params.cwd;
if (params.project_id) query.project_id = params.project_id;
if (typeof params.limit === "number") query.limit = params.limit;
const { data } = await this.client.get("/api/v1/context/workspace/next", { params: query });
return data.result || data;
}
async upsertWorkspaceEntities(input) {
const { data } = await this.client.post("/api/v1/context/workspace/entities", input);
return data;
}
async buildSubagentBrief(params) {
const { data } = await this.client.post("/api/v1/context/subagent-brief", params);
return data.brief || data;
}
async recordHotPaths(projectId, items) {
const { data } = await this.client.post(`/api/v1/context/projects/${encodeURIComponent(projectId)}/hot-paths`, { items });
return data;
}
async recordDebugFinding(projectId, input) {
const { data } = await this.client.post(`/api/v1/context/projects/${encodeURIComponent(projectId)}/debug-findings`, input);
return data;
}
async confirmClaim(projectId, input) {
const { data } = await this.client.post(`/api/v1/context/projects/${encodeURIComponent(projectId)}/verify`, input);
return data;
}
async updateGlobalContext(patch) {
const { data } = await this.client.patch("/api/v1/context/global", patch);
return data;
}
async patchProjectContext(projectId, delta) {
const { data } = await this.client.patch(`/api/v1/context/${encodeURIComponent(projectId)}/context`, delta);
return data;
}
// ── Shared skills ──────────────────────────────────────────────────────────
async listSkills(projectId) {
const params = {};
if (projectId) params.project_id = projectId;
const { data } = await this.client.get("/api/v1/context/skills", { params });
return data?.skills ?? [];
}
async getSkill(idOrSlug) {
const { data } = await this.client.get(`/api/v1/context/skills/${encodeURIComponent(idOrSlug)}`);
return data?.skill ?? null;
}
async upsertSkill(input) {
const { data } = await this.client.post("/api/v1/context/skills", input);
return data?.skill ?? data;
}
// Import a shared skill by pasting a GitHub repo URL. The API fetches the
// repo's SKILL.md, parses its frontmatter, and creates the shared skill.
async importSkillFromGithub(repoUrl, projectId) {
const body = { repo_url: repoUrl };
if (projectId) body.project_id = projectId;
const { data } = await this.client.post("/api/v1/context/skills/import-github", body);
return data;
}
};
export {
__require,
__commonJS,
__toESM,
init_esm_shims,
safeWriteFileSync,
DeerdawnClient
};
#!/usr/bin/env node
import { createRequire } from "module";
const require = createRequire(import.meta.url);
import {
DeerdawnClient
} from "./chunk-P2F6BVEV.js";
export {
DeerdawnClient
};
+1
-1
{
"name": "@deerdawn/mcp-server",
"mcpName": "com.deerdawn/deerdawn",
"version": "1.0.37",
"version": "1.0.38",
"description": "MCP server for DeerDawn — AI session memory that briefs every new AI session",

@@ -6,0 +6,0 @@ "homepage": "https://deerdawn.com",

@@ -35,3 +35,3 @@ # DeerDawn MCP Server

The hosted server exposes 18 core session-memory tools. For repo-file writes, automatic capture hooks, and the full 35-tool set, use the local install below.
The hosted server exposes 19 core session-memory tools. For repo-file writes, automatic capture hooks, and the full 36-tool set, use the local install below.

@@ -46,3 +46,3 @@ ---

- Installs SessionStart / Stop hooks that capture and flush context automatically.
- Exposes the full set of 35 tools (the hosted server exposes 18 core ones).
- Exposes the full set of 36 tools (the hosted server exposes 19 core ones).

@@ -235,3 +235,3 @@ **Sign-in works the same way** — browser OAuth by default, no API key required. On first run without credentials, the server starts a device-flow sign-in: the URL is printed to stderr, saved to `~/.deerdawn/pending-auth.json`, and returned in-band by the `get_auth_status` tool (use that in GUI hosts where stderr isn't visible). After you approve in the browser, credentials are saved to `~/.deerdawn/credentials.json` and every future session is authenticated.

The local server exposes 35 tools (the hosted remote exposes 18 core ones). These are the ones to reach for in every session.
The local server exposes 36 tools (the hosted remote exposes 19 core ones). These are the ones to reach for in every session.

@@ -257,2 +257,19 @@ ### `start_session`

### `build_subagent_brief`
Build a small, task-scoped handoff for one AI subagent. Give DeerDawn the assignment and, optionally, a role, deliverable, success criteria, likely paths, verification steps, and a 120–600 token budget. DeerDawn selects relevant project decisions, constraints, known paths, debugging notes, and commands; redacts secrets; removes duplicates; and returns a brief with provenance.
```json
{
"task": "Add contract tests for the billing webhook retry path",
"role": "test engineer",
"deliverable": "Focused tests covering retries and terminal failure",
"acceptance_criteria": ["Tests fail before the fix", "No unrelated test changes"],
"focus_paths": ["apps/api/tests", "apps/api/src/routes/webhooks.js"],
"verification": ["cd apps/api && npm test"],
"max_tokens": 250
}
```
For best results, the orchestrator should create one brief per concrete subtask, launch the worker with no inherited conversation history, and pass only the returned brief. The worker brief starts with `[DeerDawn Subagent Brief v1]`, which tells DeerDawn-aware agents that their task context is already loaded.
### `import_local_context`

@@ -259,0 +276,0 @@ Import context from `CLAUDE.md`, `AGENTS.md`, or `.cursorrules` in the current directory. **Call without `confirmed: true` first** — it shows a free preview of what would be imported. Then call again with `confirmed: true` to commit.

#!/usr/bin/env node
import { createRequire } from "module";
const require = createRequire(import.meta.url);
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
if (typeof require !== "undefined") return require.apply(this, arguments);
throw Error('Dynamic require of "' + x + '" is not supported');
});
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __commonJS = (cb, mod) => function __require2() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
// node_modules/tsup/assets/esm_shims.js
import path from "path";
import { fileURLToPath } from "url";
var init_esm_shims = __esm({
"node_modules/tsup/assets/esm_shims.js"() {
"use strict";
}
});
// src/client.ts
init_esm_shims();
import axios from "axios";
import http from "http";
import https from "https";
import fs from "fs";
import path2 from "path";
import os from "os";
function isLocalApiUrl(apiUrl) {
try {
const host = new URL(apiUrl).hostname;
return host === "localhost" || host === "127.0.0.1" || host === "::1";
} catch {
return false;
}
}
function getDeerdawnHome() {
return process.env.DEERDAWN_HOME || path2.join(os.homedir(), ".deerdawn");
}
function getQueuePath() {
return path2.join(getDeerdawnHome(), "cloud-write-queue.json");
}
function ensureDeerdawnHome() {
fs.mkdirSync(getDeerdawnHome(), { recursive: true });
}
function readQueueState() {
try {
return JSON.parse(fs.readFileSync(getQueuePath(), "utf8"));
} catch {
return { entries: [] };
}
}
function writeQueueState(state) {
ensureDeerdawnHome();
fs.writeFileSync(getQueuePath(), JSON.stringify(state, null, 2));
}
function mapOrgUsageToSettings(data) {
const plan = data?.plan || {};
return {
org_id: plan.org_id || "",
name: data?.org_name || "",
plan: typeof plan.tier === "string" ? plan.tier : "free",
fail_mode: plan.fail_mode === "open" ? "open" : "closed",
redaction_mode: plan.redaction_mode === "permissive" ? "permissive" : "strict",
is_locked: !!plan.is_locked,
shadow_mode: !!plan.shadow_mode,
redaction_keys: Array.isArray(plan.redaction_keys) ? plan.redaction_keys : [],
created_at: (/* @__PURE__ */ new Date()).toISOString(),
usage: data?.usage,
limits: data?.limits
};
}
var DeerdawnClient = class {
client;
cache;
config;
MAX_CACHE_SIZE = 100;
MAX_CACHE_ITEM_SIZE_MB = 5;
constructor(config) {
if (!config.apiKey || config.apiKey.trim() !== config.apiKey) {
throw new Error("Invalid API key: cannot be empty or contain whitespace");
}
if (!isLocalApiUrl(config.apiUrl) && !config.apiKey.startsWith("dd_")) {
throw new Error("Invalid API key format: must start with dd_ prefix");
}
this.config = config;
this.cache = /* @__PURE__ */ new Map();
this.client = axios.create({
baseURL: config.apiUrl,
headers: {
"x-api-key": config.apiKey,
"content-type": "application/json"
},
timeout: 3e4,
// Reuse TCP+TLS connections across calls. Without this, axios opens a
// fresh socket per request and pays a full handshake (~50ms) every time —
// multiplied across the several sequential calls a single tool can make.
httpAgent: new http.Agent({ keepAlive: true, maxSockets: 16 }),
httpsAgent: new https.Agent({ keepAlive: true, maxSockets: 16 })
});
this.client.interceptors.response.use(
(response) => response,
async (error) => {
const cfg = error.config;
const response = error.response;
if (!cfg) {
throw error;
}
if (!cfg.__retryCount) {
cfg.__retryCount = 0;
cfg.__firstRetryTimestamp = Date.now();
}
const totalRetryTime = Date.now() - (cfg.__firstRetryTimestamp || Date.now());
if (totalRetryTime > 6e4) {
throw new Error("Retry timeout exceeded (60s)");
}
const shouldRetry = (response?.status === 429 || response?.status && response.status >= 500) && cfg.__retryCount < this.config.maxRetries;
if (shouldRetry) {
cfg.__retryCount++;
const delay = Math.min(
this.config.retryBackoffMs * Math.pow(2, cfg.__retryCount - 1),
3e4
);
await new Promise((resolve) => setTimeout(resolve, delay));
return this.client.request(cfg);
}
throw error;
}
);
}
async recordProductEvent(event) {
const { data } = await this.client.post("/api/v1/product-events", event);
return data;
}
async recordContextEvent(event) {
try {
const { data } = await this.client.post("/api/v1/context/events", event);
return data;
} catch (error) {
const status = error?.response?.status;
const shouldQueue = !status || status >= 500 || error?.code === "ECONNREFUSED" || error?.code === "ENOTFOUND";
if (!shouldQueue) throw error;
const state = readQueueState();
state.entries.push({
id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
method: "POST",
path: "/api/v1/context/events",
body: event,
created_at: (/* @__PURE__ */ new Date()).toISOString()
});
writeQueueState(state);
return { queued: true, queue_size: state.entries.length, event_type: event.event_type };
}
}
async flushQueuedContextWrites() {
const state = readQueueState();
if (!state.entries.length) return { flushed: 0, remaining: 0 };
const remaining = [];
let flushed = 0;
for (const entry of state.entries) {
try {
await this.client.post(entry.path, entry.body);
flushed += 1;
} catch {
remaining.push(entry);
}
}
writeQueueState({ entries: remaining });
return { flushed, remaining: remaining.length };
}
async getOrganizationSettings() {
const { data } = await this.client.get("/api/v1/orgs/usage");
return mapOrgUsageToSettings(data);
}
async getContextGraphProject(projectId) {
const { data } = await this.client.get(`/api/v1/context/graph/projects/${encodeURIComponent(projectId)}`);
return data.graph || data;
}
async getContextGraphSession(sessionId) {
const { data } = await this.client.get(`/api/v1/context/graph/sessions/${encodeURIComponent(sessionId)}`);
return data.graph || data;
}
async getContextGraphNode(nodeId) {
const { data } = await this.client.get(`/api/v1/context/graph/nodes/${encodeURIComponent(nodeId)}`);
return data.graph || data;
}
async queryContextGraph(input) {
const { data } = await this.client.post("/api/v1/context/graph/query", input);
return data.graph || data;
}
async updateOrganizationSettings(settings) {
const { data } = await this.client.patch("/api/v1/orgs/settings", settings);
return data;
}
async getAuditLogs(_params = {}) {
const { data } = await this.client.get("/api/v1/audit");
const logs = data.logs || [];
return { logs, total: logs.length };
}
async getActionRegistry() {
const cacheKey = "registry:actions";
const cached = this.getFromCache(cacheKey);
if (cached) return cached;
try {
const { data } = await this.client.get("/api/v1/registry/actions");
const actions = data.actions || data;
if (Array.isArray(actions) && actions.length > 0) {
this.setInCache(cacheKey, actions, 3e5);
return actions;
}
} catch {
}
return this.getDefaultActionRegistry();
}
getDefaultActionRegistry() {
return [
{
action_type: "auth.login",
category: "authentication",
description: "User login attempt",
risk_level: "medium"
},
{
action_type: "auth.password_reset",
category: "authentication",
description: "Password reset request",
risk_level: "medium"
},
{
action_type: "payment.process",
category: "financial",
description: "Payment processing",
risk_level: "high"
},
{
action_type: "user.delete",
category: "data_management",
description: "User account deletion",
risk_level: "high"
},
{
action_type: "data.export",
category: "data_management",
description: "Data export request",
risk_level: "medium"
}
];
}
getFromCache(key) {
if (!this.config.cacheEnabled) return null;
const entry = this.cache.get(key);
if (!entry) return null;
if (Date.now() > entry.expires) {
this.cache.delete(key);
return null;
}
return entry.data;
}
setInCache(key, data, ttlMs) {
if (!this.config.cacheEnabled) return;
const estimatedSize = JSON.stringify(data).length;
const estimatedSizeMB = estimatedSize / (1024 * 1024);
if (estimatedSizeMB > this.MAX_CACHE_ITEM_SIZE_MB) {
console.error(`[CACHE] Skipping large object (${estimatedSizeMB.toFixed(2)}MB)`);
return;
}
if (this.cache.size >= this.MAX_CACHE_SIZE) {
const firstKey = this.cache.keys().next().value;
if (firstKey) {
this.cache.delete(firstKey);
}
}
this.cache.set(key, {
data,
expires: Date.now() + ttlMs
});
}
invalidateCache(prefix) {
for (const key of this.cache.keys()) {
if (key.startsWith(prefix)) {
this.cache.delete(key);
}
}
}
// Context sync methods
async getActiveContext(projectId, tool = "claude_code", verbosity = "standard") {
if (projectId) {
const { data: data2 } = await this.client.get(
`/api/v1/context/projects/${encodeURIComponent(projectId)}/inject`,
{ params: { tool, verbosity } }
);
return {
brief: data2.payload?.body ?? null,
project_name: projectId,
project_id: projectId,
last_updated: null
};
}
const { data } = await this.client.get("/api/v1/context/active", { params: { tool, verbosity } });
return data;
}
// Brief shape (?shape=spine) — the product-voice 4-slot view (SHIPPED / DECIDED
// / OPEN / LANDMINE). Returns the 4-slot TEXT body plus the structured
// brief_spine (per-item trust). Opt-in: never changes the default brief text.
async getActiveBrief(projectId, tool = "claude_code", verbosity = "standard") {
if (projectId) {
const { data: data2 } = await this.client.get(
`/api/v1/context/projects/${encodeURIComponent(projectId)}/inject`,
{ params: { tool, verbosity, shape: "spine" } }
);
return {
brief: data2.payload?.body ?? null,
brief_spine: data2.payload?.brief_spine ?? null,
project_name: projectId,
project_id: projectId,
last_updated: null
};
}
const { data } = await this.client.get("/api/v1/context/active", { params: { tool, verbosity, shape: "spine" } });
return data;
}
async getActiveContextByCwd(cwd, tool = "claude_code", verbosity = "standard", markSession = false, workspaceKey = null) {
const params = { cwd, tool, verbosity };
if (markSession) params.mark_session = 1;
if (workspaceKey) params.workspace_key = workspaceKey;
const { data } = await this.client.get("/api/v1/context/active", { params });
return data;
}
async pushContextUpdate(projectId, summary, tool = "claude_code", commands) {
const sessionId = `mcp-${Date.now()}`;
const [extractionResult] = await Promise.all([
this.client.post("/api/v1/context/extraction", {
session_id: sessionId,
tool,
project_id: projectId,
chunks: [{ role: "assistant", text: summary }]
}).then((r) => r.data),
commands?.length ? this.client.patch(`/api/v1/context/${encodeURIComponent(projectId)}/context`, { commands }).catch((err) => {
process.stderr.write(`[DeerDawn] commands not saved: ${err?.message ?? "unknown"}
`);
return null;
}) : Promise.resolve(null)
]);
return extractionResult;
}
async listContextProjects() {
const { data } = await this.client.get("/api/v1/context/projects");
return data;
}
async listContextProjectTree() {
const { data } = await this.client.get("/api/v1/context/projects/tree");
return data;
}
async createContextProject(projectName, description, parentProjectId) {
const { data } = await this.client.post("/api/v1/context/projects", {
project_name: projectName,
description,
...parentProjectId ? { parent_project_id: parentProjectId } : {}
});
return data;
}
async switchSubproject(projectId, workspaceKey) {
const { data } = await this.client.post("/api/v1/context/subproject/switch", {
project_id: projectId,
workspace_key: workspaceKey
});
return data;
}
async archiveProject(projectId, unarchive = false) {
const { data } = await this.client.post(
`/api/v1/context/projects/${encodeURIComponent(projectId)}/archive`,
{ unarchive }
);
return data;
}
async setProjectParent(projectId, parentProjectId) {
const { data } = await this.client.post(
`/api/v1/context/projects/${encodeURIComponent(projectId)}/parent`,
{ parent_project_id: parentProjectId ?? null }
);
return data;
}
async renameProject(projectId, projectName) {
const { data } = await this.client.post(
`/api/v1/context/projects/${encodeURIComponent(projectId)}/rename`,
{ project_name: projectName }
);
return data;
}
async getContextSections(projectId, sections, tool = "claude_code", verbosity = "standard") {
const sectionsParam = sections.join(",");
if (projectId) {
const { data: data2 } = await this.client.get(
`/api/v1/context/projects/${encodeURIComponent(projectId)}/inject`,
{ params: { tool, verbosity, sections: sectionsParam } }
);
return {
brief: data2.payload?.body ?? null,
project_name: projectId,
project_id: projectId
};
}
const { data } = await this.client.get("/api/v1/context/active", { params: { tool, verbosity, sections: sectionsParam } });
return data;
}
async getContextMap(mcpAvailable = true, cwd, workspaceKey) {
const params = { mcp: mcpAvailable ? "true" : "false" };
if (cwd) params.cwd = cwd;
if (workspaceKey) params.workspace_key = workspaceKey;
const { data } = await this.client.get("/api/v1/context/map", { params });
return data;
}
async getContextJson(projectId) {
if (projectId) {
const { data: data2 } = await this.client.get(
`/api/v1/context/projects/${encodeURIComponent(projectId)}/inject`,
{ params: { format: "json" } }
);
return data2.context ?? data2;
}
const { data } = await this.client.get("/api/v1/context/active", { params: { format: "json" } });
return data;
}
async searchContext(query, limit = 5) {
const { data } = await this.client.post("/api/v1/context/search", { query, limit });
return data;
}
async getWorkspaceOverview(params = {}) {
const query = {};
if (params.mode) query.mode = params.mode;
if (params.verbosity) query.verbosity = params.verbosity;
if (params.include?.length) query.include = params.include.join(",");
if (params.format) query.format = params.format;
const { data } = await this.client.get("/api/v1/context/workspace/overview", { params: query });
return data.overview || data;
}
async getWorkspaceEntities(params = {}) {
const query = {};
if (params.types?.length) query.types = params.types.join(",");
if (params.query) query.query = params.query;
if (params.project_id) query.project_id = params.project_id;
if (params.related_project_id) query.related_project_id = params.related_project_id;
if (params.scope) query.scope = params.scope;
if (params.surface) query.surface = params.surface;
if (params.status) query.status = params.status;
if (typeof params.include_inferred === "boolean") query.include_inferred = params.include_inferred ? 1 : 0;
if (typeof params.include_resolved === "boolean") query.include_resolved = params.include_resolved ? 1 : 0;
if (typeof params.curated_only === "boolean") query.curated_only = params.curated_only ? 1 : 0;
if (typeof params.limit === "number") query.limit = params.limit;
if (params.format) query.format = params.format;
const { data } = await this.client.get("/api/v1/context/workspace/entities", { params: query });
return data;
}
async getProjectMap(params = {}) {
const projectId = params.project_id;
if (!projectId) {
const { data: data2 } = await this.client.get("/api/v1/context/active");
if (!data2?.project_id) return null;
params.project_id = data2.project_id;
}
const query = {};
if (params.verbosity) query.verbosity = params.verbosity;
if (params.format) query.format = params.format;
const resolvedProjectId = String(params.project_id);
const { data } = await this.client.get(`/api/v1/context/projects/${encodeURIComponent(resolvedProjectId)}/map`, { params: query });
return data.project_map || data;
}
async findKnownPaths(params) {
const query = { query: params.query };
if (params.project_id) query.project_id = params.project_id;
if (typeof params.limit === "number") query.limit = params.limit;
if (params.format) query.format = params.format;
const { data } = await this.client.get("/api/v1/context/workspace/known-paths", { params: query });
return data;
}
async getNextBestContext(params = {}) {
const query = {};
if (params.tool) query.tool = params.tool;
if (params.cwd) query.cwd = params.cwd;
if (params.project_id) query.project_id = params.project_id;
if (typeof params.limit === "number") query.limit = params.limit;
const { data } = await this.client.get("/api/v1/context/workspace/next", { params: query });
return data.result || data;
}
async upsertWorkspaceEntities(input) {
const { data } = await this.client.post("/api/v1/context/workspace/entities", input);
return data;
}
async recordHotPaths(projectId, items) {
const { data } = await this.client.post(`/api/v1/context/projects/${encodeURIComponent(projectId)}/hot-paths`, { items });
return data;
}
async recordDebugFinding(projectId, input) {
const { data } = await this.client.post(`/api/v1/context/projects/${encodeURIComponent(projectId)}/debug-findings`, input);
return data;
}
async confirmClaim(projectId, input) {
const { data } = await this.client.post(`/api/v1/context/projects/${encodeURIComponent(projectId)}/verify`, input);
return data;
}
async updateGlobalContext(patch) {
const { data } = await this.client.patch("/api/v1/context/global", patch);
return data;
}
async patchProjectContext(projectId, delta) {
const { data } = await this.client.patch(`/api/v1/context/${encodeURIComponent(projectId)}/context`, delta);
return data;
}
// ── Shared skills ──────────────────────────────────────────────────────────
async listSkills(projectId) {
const params = {};
if (projectId) params.project_id = projectId;
const { data } = await this.client.get("/api/v1/context/skills", { params });
return data?.skills ?? [];
}
async getSkill(idOrSlug) {
const { data } = await this.client.get(`/api/v1/context/skills/${encodeURIComponent(idOrSlug)}`);
return data?.skill ?? null;
}
async upsertSkill(input) {
const { data } = await this.client.post("/api/v1/context/skills", input);
return data?.skill ?? data;
}
// Import a shared skill by pasting a GitHub repo URL. The API fetches the
// repo's SKILL.md, parses its frontmatter, and creates the shared skill.
async importSkillFromGithub(repoUrl, projectId) {
const body = { repo_url: repoUrl };
if (projectId) body.project_id = projectId;
const { data } = await this.client.post("/api/v1/context/skills/import-github", body);
return data;
}
};
export {
__require,
__commonJS,
__toESM,
init_esm_shims,
DeerdawnClient
};
#!/usr/bin/env node
import { createRequire } from "module";
const require = createRequire(import.meta.url);
import {
DeerdawnClient
} from "./chunk-LRWQ2CHH.js";
export {
DeerdawnClient
};

Sorry, the diff of this file is too big to display