fix(plugin): address kubernetes greptile follow-up

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Dotta
2026-05-13 11:26:24 -05:00
committed by Chris Farhood
parent a98c5cdfa9
commit a6c2e0392b
7 changed files with 97 additions and 12 deletions
@@ -43,6 +43,13 @@ const manifest: PaperclipPluginManifestV1 = {
maxLength: 20,
description: "Prefix for the per-company tenant namespace (default: paperclip-).",
},
paperclipServerNamespace: {
type: "string",
maxLength: 63,
pattern: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$",
description:
"Namespace where paperclip-server pods run. Used by generated egress policies so agent pods can call back to the server (default: paperclip).",
},
companySlug: {
type: "string",
maxLength: 43,
@@ -38,11 +38,6 @@ import {
paperclipLabels,
} from "./utils.js";
// The namespace paperclip-server itself runs in. Used when building
// NetworkPolicy manifests so the tenant namespace allows inbound traffic
// from the server pod.
const PAPERCLIP_SERVER_NAMESPACE = "paperclip";
// Name of the ServiceAccount created inside each tenant namespace by ensureTenant.
const TENANT_SERVICE_ACCOUNT = "paperclip-tenant-sa";
@@ -80,7 +75,7 @@ export function extractAdapterEnvFromProcess(
const missing: string[] = [];
for (const k of envKeys) {
const v = process.env[k];
if (v) {
if (v !== undefined) {
out[k] = v;
} else {
missing.push(k);
@@ -94,6 +89,20 @@ export function extractAdapterEnvFromProcess(
return out;
}
function shellQuoteArg(arg: string): string {
return "'" + arg.replace(/'/g, "'\\''") + "'";
}
export function buildSandboxExecShellCommand(
params: Pick<PluginEnvironmentExecuteParams, "args" | "command">,
): string {
if (typeof params.command === "string" && params.command.trim().length > 0) {
return params.command;
}
return params.args?.map(shellQuoteArg).join(" ") ?? "";
}
function generateBootstrapToken(): string {
// TODO: paperclip-server's actual callback auth scheme is separate and is
// out of M4b scope. This per-run random token is stored in the per-run
@@ -217,7 +226,7 @@ const plugin = definePlugin({
await ensureTenant(clients, {
namespace,
companyId: params.companyId,
paperclipServerNamespace: PAPERCLIP_SERVER_NAMESPACE,
paperclipServerNamespace: config.paperclipServerNamespace,
serviceAccountAnnotations: config.serviceAccountAnnotations,
egressMode: config.egressMode,
egressAllowFqdns: [...adapterDefaults.allowFqdns, ...config.egressAllowFqdns],
@@ -481,10 +490,7 @@ const plugin = definePlugin({
// Build the command to exec. If params.command is provided use it;
// otherwise wrap in a login shell so profile scripts run.
const rawCommand =
typeof params.command === "string" && params.command.trim().length > 0
? params.command
: params.args?.join(" ") ?? "";
const rawCommand = buildSandboxExecShellCommand(params);
const execCommand = rawCommand.length > 0
? ["/bin/sh", "-lc", rawCommand]
@@ -29,6 +29,12 @@ export const kubernetesProviderConfigSchema = z
kubeconfig: z.string().optional(),
namespacePrefix: z.string().regex(/^[a-z0-9-]{1,20}$/).default("paperclip-"),
paperclipServerNamespace: z
.string()
.min(1)
.max(63)
.regex(/^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/)
.default("paperclip"),
companySlug: z.string().regex(/^[a-z0-9-]{1,43}$/).optional(),
imageRegistry: z.string().url().optional(),