Sign In

@deerdawn/mcp-server

Package Overview
Dependencies
Maintainers
1
Versions
41
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.39
to
1.0.40
+602
dist/chunk-AGVTTUZL.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;
}
/**
* The project's raw structured context — ALWAYS the flat context object
* (`{project_id, project_name, updated_at, ...context}`), whichever endpoint
* served it.
*
* These two branches used to return different shapes: the projectId path
* unwrapped `data.context`, while the no-projectId path returned the whole
* `/active` envelope, which has no `context` key at all. Callers therefore had
* to guess, and `manage_todos` guessed wrong in the one direction that fails
* silently — reading `ctx.context.todo_items` off a shape where `.context` is
* undefined yields no error, just a permanently empty board.
*
* One shape, decided here, so no caller has to branch on it again.
*/
async getContextJson(projectId) {
const path3 = projectId ? `/api/v1/context/projects/${encodeURIComponent(projectId)}/inject` : "/api/v1/context/active";
const { data } = await this.client.get(path3, { params: { format: "json" } });
if (data && typeof data === "object" && "context" in data) return data.context ?? {};
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-AGVTTUZL.js";
export {
DeerdawnClient
};
+1
-1
{
"name": "@deerdawn/mcp-server",
"mcpName": "com.deerdawn/deerdawn",
"version": "1.0.39",
"version": "1.0.40",
"description": "MCP server for DeerDawn — AI session memory that briefs every new AI session",

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

#!/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
};

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