Merge remote-tracking branch 'public-gh/master'

* public-gh/master:
  Address PR feedback: keep testEnvironment non-destructive, warn on swallowed errors
  Apply suggestion from @greptile-apps[bot]
  Fix opencode-local adapter: parser, UI, CLI, and environment tests
  Rename Invoke button to Run Heartbeat for clarity
  fixing overhanging recommended text in onboarding
  Add Contributing guide
  feat(pi-local): fix bugs, add RPC mode, improve cost tracking and output handling
  fix(sidebar-badges): include approvals in inbox badge count
  feat: add Pi adapter support to constants and onboarding UI
  Adding support for pi-local
  ci: clarify fail-fast lockfile refresh behavior
  ci: remove unnecessary full-history checkout
  ci: fix pnpm lockfile policy checks
  ci: split workflows and move pnpm lockfile ownership to GitHub Actions
  Add License
  fix: use root option in sendFile to avoid dotfile 500 on SPA refresh

# Conflicts:
#	cli/src/adapters/registry.ts
#	pnpm-lock.yaml
#	server/src/adapters/registry.ts
#	ui/package.json
#	ui/src/adapters/registry.ts
This commit is contained in:
Dotta
2026-03-07 15:18:02 -06:00
44 changed files with 2350 additions and 995 deletions

View File

@@ -35,6 +35,7 @@
"@paperclipai/adapter-codex-local": "workspace:*",
"@paperclipai/adapter-cursor-local": "workspace:*",
"@paperclipai/adapter-opencode-local": "workspace:*",
"@paperclipai/adapter-pi-local": "workspace:*",
"@paperclipai/adapter-openclaw": "workspace:*",
"@paperclipai/adapter-openclaw-gateway": "workspace:*",
"@paperclipai/adapter-utils": "workspace:*",

View File

@@ -5,7 +5,7 @@ import path from "node:path";
import { testEnvironment } from "@paperclipai/adapter-opencode-local/server";
describe("opencode_local environment diagnostics", () => {
it("creates a missing working directory when cwd is absolute", async () => {
it("reports a missing working directory as an error when cwd is absolute", async () => {
const cwd = path.join(
os.tmpdir(),
`paperclip-opencode-local-cwd-${Date.now()}-${Math.random().toString(16).slice(2)}`,
@@ -23,11 +23,9 @@ describe("opencode_local environment diagnostics", () => {
},
});
expect(result.checks.some((check) => check.code === "opencode_cwd_valid")).toBe(true);
expect(result.checks.some((check) => check.level === "error")).toBe(false);
const stats = await fs.stat(cwd);
expect(stats.isDirectory()).toBe(true);
await fs.rm(path.dirname(cwd), { recursive: true, force: true });
expect(result.checks.some((check) => check.code === "opencode_cwd_invalid")).toBe(true);
expect(result.checks.some((check) => check.level === "error")).toBe(true);
expect(result.status).toBe("fail");
});
it("treats an empty OPENAI_API_KEY override as missing", async () => {

View File

@@ -45,6 +45,15 @@ import {
} from "@paperclipai/adapter-openclaw-gateway";
import { listCodexModels } from "./codex-models.js";
import { listCursorModels } from "./cursor-models.js";
import {
execute as piExecute,
testEnvironment as piTestEnvironment,
sessionCodec as piSessionCodec,
listPiModels,
} from "@paperclipai/adapter-pi-local/server";
import {
agentConfigurationDoc as piAgentConfigurationDoc,
} from "@paperclipai/adapter-pi-local";
import { processAdapter } from "./process/index.js";
import { httpAdapter } from "./http/index.js";
@@ -110,11 +119,23 @@ const openCodeLocalAdapter: ServerAdapterModule = {
agentConfigurationDoc: openCodeAgentConfigurationDoc,
};
const piLocalAdapter: ServerAdapterModule = {
type: "pi_local",
execute: piExecute,
testEnvironment: piTestEnvironment,
sessionCodec: piSessionCodec,
models: [],
listModels: listPiModels,
supportsLocalAgentJwt: true,
agentConfigurationDoc: piAgentConfigurationDoc,
};
const adaptersByType = new Map<string, ServerAdapterModule>(
[
claudeLocalAdapter,
codexLocalAdapter,
openCodeLocalAdapter,
piLocalAdapter,
cursorLocalAdapter,
openclawAdapter,
openclawGatewayAdapter,

View File

@@ -136,7 +136,7 @@ export async function createApp(
if (uiDist) {
app.use(express.static(uiDist));
app.get(/.*/, (_req, res) => {
res.sendFile(path.join(uiDist, "index.html"));
res.sendFile("index.html", { root: uiDist });
});
} else {
console.warn("[paperclip] UI dist not found; running in API-only mode");

View File

@@ -45,7 +45,7 @@ export function sidebarBadgeRoutes(db: Db) {
const alertsCount =
(summary.agents.error > 0 && !hasFailedRuns ? 1 : 0) +
(summary.costs.monthBudgetCents > 0 && summary.costs.monthUtilizationPercent >= 80 ? 1 : 0);
badges.inbox = badges.failedRuns + alertsCount + staleIssueCount + joinRequestCount;
badges.inbox = badges.failedRuns + alertsCount + staleIssueCount + joinRequestCount + badges.approvals;
res.json(badges);
});