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

@papi-ai/server

Package Overview
Dependencies
Maintainers
1
Versions
107
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@papi-ai/server - npm Package Compare versions

Comparing version
0.7.85
to
0.7.98
+168
dist/statusline.js
// src/cli/statusline.ts
import { createHash } from "crypto";
import { existsSync, readFileSync, writeFileSync } from "fs";
import { homedir, tmpdir } from "os";
import { join } from "path";
import { pathToFileURL } from "url";
var CACHE_TTL_MS = 3e4;
var FETCH_TIMEOUT_MS = 3e3;
function renderPeekLine(c) {
const parts = [`PAPI c${c.cycle ?? "?"}`];
parts.push(`build ${c.inFlight}`);
parts.push(`review ${c.inReview}`);
if (c.ownerActions !== null) parts.push(`you ${c.ownerActions}`);
return parts.join(" | ");
}
function cacheFilePath(endpoint, projectId) {
const hash = createHash("sha256").update(`${endpoint}|${projectId ?? ""}`).digest("hex").slice(0, 16);
return join(tmpdir(), `papi-statusline-${hash}.json`);
}
function readClientCache(path, now = Date.now()) {
try {
const hit = JSON.parse(readFileSync(path, "utf-8"));
if (!hit || typeof hit.expires !== "number" || hit.expires <= now) return void 0;
return hit.payload;
} catch {
return void 0;
}
}
function writeClientCache(path, payload, now = Date.now()) {
try {
writeFileSync(path, JSON.stringify({ expires: now + CACHE_TTL_MS, payload }));
} catch {
}
}
function configFromMcpJson(body) {
if (!body || typeof body !== "object") return void 0;
const servers = body.mcpServers;
if (!servers || typeof servers !== "object") return void 0;
for (const entry of Object.values(servers)) {
if (!entry || typeof entry !== "object") continue;
const s = entry;
if (typeof s.url !== "string" || !/papi/i.test(s.url)) continue;
const auth = s.headers?.["Authorization"];
const authString = typeof auth === "string" ? auth : "";
const bearer = authString.startsWith("Bearer ") ? authString.slice(7).trim() : "";
const projectHeader = s.headers?.["x-papi-project-id"];
if (!bearer) continue;
return {
endpoint: new URL(s.url).origin,
bearer,
projectId: typeof projectHeader === "string" && projectHeader.length > 0 ? projectHeader : void 0
};
}
return void 0;
}
function mergeStatusLine(body, command) {
const obj = body && typeof body === "object" ? body : {};
if (obj["statusLine"] !== void 0) return null;
return { ...obj, statusLine: { type: "command", command } };
}
function resolveConfig(argv) {
const flag = (name) => {
const i = argv.indexOf(name);
return i >= 0 && argv[i + 1] ? argv[i + 1] : void 0;
};
const endpoint = flag("--endpoint") ?? process.env["PAPI_ENDPOINT"];
const bearer = flag("--bearer") ?? process.env["PAPI_BEARER"];
const projectId = flag("--project") ?? process.env["PAPI_PROJECT_ID"];
if (endpoint && bearer) return { endpoint: new URL(endpoint).origin, bearer, projectId };
const mcpJsonPath = join(process.cwd(), ".mcp.json");
if (existsSync(mcpJsonPath)) {
try {
return configFromMcpJson(JSON.parse(readFileSync(mcpJsonPath, "utf-8")));
} catch {
return void 0;
}
}
return void 0;
}
async function fetchPeek(config) {
const response = await fetch(`${config.endpoint}/peek`, {
headers: {
Authorization: `Bearer ${config.bearer}`,
...config.projectId ? { "x-papi-project-id": config.projectId } : {}
},
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
});
if (!response.ok) throw new Error(`peek returned ${response.status}`);
const body = await response.json();
return {
cycle: typeof body.cycle === "number" ? body.cycle : null,
inFlight: typeof body.inFlight === "number" ? body.inFlight : 0,
inReview: typeof body.inReview === "number" ? body.inReview : 0,
ownerActions: typeof body.ownerActions === "number" ? body.ownerActions : null
};
}
var RENDER_COMMAND = "npx -y @papi-ai/cli papi-statusline";
async function install() {
const settingsPath = join(homedir(), ".claude", "settings.json");
let body = {};
if (existsSync(settingsPath)) {
try {
body = JSON.parse(readFileSync(settingsPath, "utf-8"));
} catch (err) {
console.error(`papi-statusline: could not parse ${settingsPath}: ${err.message}`);
return 1;
}
}
const merged = mergeStatusLine(body, RENDER_COMMAND);
if (merged === null) {
console.log(
`papi-statusline: ${settingsPath} already has a statusLine entry \u2014 left untouched.
To switch it to PAPI manually, set:
"statusLine": { "type": "command", "command": "${RENDER_COMMAND}" }
Any other MCP client with a custom status-bar command can run the same command.`
);
return 0;
}
writeFileSync(settingsPath, `${JSON.stringify(merged, null, 2)}
`);
console.log(`papi-statusline: installed into ${settingsPath}. Restart your AI client to see it.`);
return 0;
}
async function runStatusline(argv, stdin) {
if (argv.includes("--install")) return install();
void stdin;
const config = resolveConfig(argv);
if (!config) return 0;
const cachePath = cacheFilePath(config.endpoint, config.projectId);
const cached = readClientCache(cachePath);
if (cached) {
process.stdout.write(`${renderPeekLine(cached)}
`);
return 0;
}
try {
const payload = await fetchPeek(config);
writeClientCache(cachePath, payload);
process.stdout.write(`${renderPeekLine(payload)}
`);
} catch {
}
return 0;
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
const chunks = [];
process.stdin.on("data", (chunk) => chunks.push(chunk));
process.stdin.on("end", () => {
void runStatusline(process.argv.slice(2), Buffer.concat(chunks).toString("utf-8")).then((code) => {
process.exitCode = code;
});
});
setTimeout(() => {
void runStatusline(process.argv.slice(2), Buffer.concat(chunks).toString("utf-8")).then((code) => {
process.exitCode = code;
});
}, 250);
}
export {
cacheFilePath,
configFromMcpJson,
mergeStatusLine,
readClientCache,
renderPeekLine,
runStatusline,
writeClientCache
};
+5
-6
{
"name": "@papi-ai/server",
"version": "0.7.85",
"description": "PAPI MCP server — AI-powered sprint planning, build execution, and strategy review for software projects",
"version": "0.7.98",
"description": "PAPI MCP server \u2014 AI-powered sprint planning, build execution, and strategy review for software projects",
"license": "Elastic-2.0",

@@ -52,13 +52,12 @@ "mcpName": "io.github.getpapi/papi",

"dependencies": {
"@anthropic-ai/sdk": "^0.82.0",
"@anthropic-ai/sdk": "^0.91.1",
"@modelcontextprotocol/sdk": "^1.27.1",
"@papi-ai/adapter-pg": "^0.2.16",
"@papi-ai/shared": "^0.2.5",
"@papi-ai/skills": "^0.1.4",
"js-yaml": "^4.1.0"
"@papi-ai/skills": "^0.1.4"
},
"devDependencies": {
"@papi-ai/adapter-md": "^0.2.0",
"@types/js-yaml": "^4.0.9",
"@types/node": "^22.0.0",
"ajv": "^8.20.0",
"tsup": "^8.0.0",

@@ -65,0 +64,0 @@ "typescript": "^5.5.0",

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

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

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