mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-17 11:20:37 +09:00
Merge remote-tracking branch 'public-gh/master' into paperclip-company-import-export
* public-gh/master: fix: address greptile follow-up feedback docs: clarify quickstart npx usage Add guarded dev restart handling Fix PAP-576 settings toggles and transcript default Add username log censor setting fix: use standard toggle component for permission controls # Conflicts: # server/src/routes/agents.ts # ui/src/pages/AgentDetail.tsx
This commit is contained in:
commit
5140d7b0c4
44 changed files with 11673 additions and 208 deletions
|
|
@ -48,6 +48,7 @@ import { findServerAdapter, listAdapterModels } from "../adapters/index.js";
|
|||
import { redactEventPayload } from "../redaction.js";
|
||||
import { redactCurrentUserValue } from "../log-redaction.js";
|
||||
import { renderOrgChartSvg, renderOrgChartPng, type OrgNode, type OrgChartStyle, ORG_CHART_STYLES } from "./org-chart-svg.js";
|
||||
import { instanceSettingsService } from "../services/instance-settings.js";
|
||||
import { runClaudeLogin } from "@paperclipai/adapter-claude-local/server";
|
||||
import {
|
||||
DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX,
|
||||
|
|
@ -84,8 +85,15 @@ export function agentRoutes(db: Db) {
|
|||
const instructions = agentInstructionsService();
|
||||
const companySkills = companySkillService(db);
|
||||
const workspaceOperations = workspaceOperationService(db);
|
||||
const instanceSettings = instanceSettingsService(db);
|
||||
const strictSecretsMode = process.env.PAPERCLIP_SECRETS_STRICT_MODE === "true";
|
||||
|
||||
async function getCurrentUserRedactionOptions() {
|
||||
return {
|
||||
enabled: (await instanceSettings.getGeneral()).censorUsernameInLogs,
|
||||
};
|
||||
}
|
||||
|
||||
function canCreateAgents(agent: { role: string; permissions: Record<string, unknown> | null | undefined }) {
|
||||
if (!agent.permissions || typeof agent.permissions !== "object") return false;
|
||||
return Boolean((agent.permissions as Record<string, unknown>).canCreateAgents);
|
||||
|
|
@ -2084,7 +2092,7 @@ export function agentRoutes(db: Db) {
|
|||
return;
|
||||
}
|
||||
assertCompanyAccess(req, run.companyId);
|
||||
res.json(redactCurrentUserValue(run));
|
||||
res.json(redactCurrentUserValue(run, await getCurrentUserRedactionOptions()));
|
||||
});
|
||||
|
||||
router.post("/heartbeat-runs/:runId/cancel", async (req, res) => {
|
||||
|
|
@ -2119,11 +2127,12 @@ export function agentRoutes(db: Db) {
|
|||
const afterSeq = Number(req.query.afterSeq ?? 0);
|
||||
const limit = Number(req.query.limit ?? 200);
|
||||
const events = await heartbeat.listEvents(runId, Number.isFinite(afterSeq) ? afterSeq : 0, Number.isFinite(limit) ? limit : 200);
|
||||
const currentUserRedactionOptions = await getCurrentUserRedactionOptions();
|
||||
const redactedEvents = events.map((event) =>
|
||||
redactCurrentUserValue({
|
||||
...event,
|
||||
payload: redactEventPayload(event.payload),
|
||||
}),
|
||||
}, currentUserRedactionOptions),
|
||||
);
|
||||
res.json(redactedEvents);
|
||||
});
|
||||
|
|
@ -2159,7 +2168,7 @@ export function agentRoutes(db: Db) {
|
|||
const context = asRecord(run.contextSnapshot);
|
||||
const executionWorkspaceId = asNonEmptyString(context?.executionWorkspaceId);
|
||||
const operations = await workspaceOperations.listForRun(runId, executionWorkspaceId);
|
||||
res.json(redactCurrentUserValue(operations));
|
||||
res.json(redactCurrentUserValue(operations, await getCurrentUserRedactionOptions()));
|
||||
});
|
||||
|
||||
router.get("/workspace-operations/:operationId/log", async (req, res) => {
|
||||
|
|
@ -2255,7 +2264,7 @@ export function agentRoutes(db: Db) {
|
|||
}
|
||||
|
||||
res.json({
|
||||
...redactCurrentUserValue(run),
|
||||
...redactCurrentUserValue(run, await getCurrentUserRedactionOptions()),
|
||||
agentId: agent.id,
|
||||
agentName: agent.name,
|
||||
adapterType: agent.adapterType,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import { Router } from "express";
|
||||
import type { Db } from "@paperclipai/db";
|
||||
import { and, count, eq, gt, isNull, sql } from "drizzle-orm";
|
||||
import { instanceUserRoles, invites } from "@paperclipai/db";
|
||||
import { and, count, eq, gt, inArray, isNull, sql } from "drizzle-orm";
|
||||
import { heartbeatRuns, instanceUserRoles, invites } from "@paperclipai/db";
|
||||
import type { DeploymentExposure, DeploymentMode } from "@paperclipai/shared";
|
||||
import { readPersistedDevServerStatus, toDevServerHealthStatus } from "../dev-server-status.js";
|
||||
import { instanceSettingsService } from "../services/instance-settings.js";
|
||||
import { serverVersion } from "../version.js";
|
||||
|
||||
export function healthRoutes(
|
||||
|
|
@ -55,6 +57,23 @@ export function healthRoutes(
|
|||
}
|
||||
}
|
||||
|
||||
const persistedDevServerStatus = readPersistedDevServerStatus();
|
||||
let devServer: ReturnType<typeof toDevServerHealthStatus> | undefined;
|
||||
if (persistedDevServerStatus) {
|
||||
const instanceSettings = instanceSettingsService(db);
|
||||
const experimentalSettings = await instanceSettings.getExperimental();
|
||||
const activeRunCount = await db
|
||||
.select({ count: count() })
|
||||
.from(heartbeatRuns)
|
||||
.where(inArray(heartbeatRuns.status, ["queued", "running"]))
|
||||
.then((rows) => Number(rows[0]?.count ?? 0));
|
||||
|
||||
devServer = toDevServerHealthStatus(persistedDevServerStatus, {
|
||||
autoRestartEnabled: experimentalSettings.autoRestartDevServerWhenIdle ?? false,
|
||||
activeRunCount,
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
status: "ok",
|
||||
version: serverVersion,
|
||||
|
|
@ -66,6 +85,7 @@ export function healthRoutes(
|
|||
features: {
|
||||
companyDeletionEnabled: opts.companyDeletionEnabled,
|
||||
},
|
||||
...(devServer ? { devServer } : {}),
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Router, type Request } from "express";
|
||||
import type { Db } from "@paperclipai/db";
|
||||
import { patchInstanceExperimentalSettingsSchema } from "@paperclipai/shared";
|
||||
import { patchInstanceExperimentalSettingsSchema, patchInstanceGeneralSettingsSchema } from "@paperclipai/shared";
|
||||
import { forbidden } from "../errors.js";
|
||||
import { validate } from "../middleware/validate.js";
|
||||
import { instanceSettingsService, logActivity } from "../services/index.js";
|
||||
|
|
@ -20,6 +20,41 @@ export function instanceSettingsRoutes(db: Db) {
|
|||
const router = Router();
|
||||
const svc = instanceSettingsService(db);
|
||||
|
||||
router.get("/instance/settings/general", async (req, res) => {
|
||||
assertCanManageInstanceSettings(req);
|
||||
res.json(await svc.getGeneral());
|
||||
});
|
||||
|
||||
router.patch(
|
||||
"/instance/settings/general",
|
||||
validate(patchInstanceGeneralSettingsSchema),
|
||||
async (req, res) => {
|
||||
assertCanManageInstanceSettings(req);
|
||||
const updated = await svc.updateGeneral(req.body);
|
||||
const actor = getActorInfo(req);
|
||||
const companyIds = await svc.listCompanyIds();
|
||||
await Promise.all(
|
||||
companyIds.map((companyId) =>
|
||||
logActivity(db, {
|
||||
companyId,
|
||||
actorType: actor.actorType,
|
||||
actorId: actor.actorId,
|
||||
agentId: actor.agentId,
|
||||
runId: actor.runId,
|
||||
action: "instance.settings.general_updated",
|
||||
entityType: "instance_settings",
|
||||
entityId: updated.id,
|
||||
details: {
|
||||
general: updated.general,
|
||||
changedKeys: Object.keys(req.body).sort(),
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
res.json(updated.general);
|
||||
},
|
||||
);
|
||||
|
||||
router.get("/instance/settings/experimental", async (req, res) => {
|
||||
assertCanManageInstanceSettings(req);
|
||||
res.json(await svc.getExperimental());
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue