8bd5042b5d
Replaces K8s log API streaming (which was dropping every ~3 seconds at production scale) with filesystem tailing via tee to a pod log file on the shared PVC. Core changes: - Add tee to claudeInvocation to write pod log file - Add mkdir -p to init container to create log directory - Add assertSafePathComponent and buildPodLogPath helper - Add tailPodLogFile function with adaptive 250ms/1s polling - Replace k8s log streaming with tailPodLogFile in Promise.allSettled - Delete log-dedup.ts (RTK output truncation no longer needed) - Update config-schema.ts and index.ts to remove RTK references - Clean up log file in cleanupJob when retainJobs=false Note: 14 tests in execute.test.ts test the obsolete k8s log streaming approach and need to be rewritten or deleted (streamPodLogsOnce tests). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
139 lines
4.7 KiB
TypeScript
139 lines
4.7 KiB
TypeScript
// NOTE: These types must match what Paperclip's SchemaConfigFields component
|
|
// expects. Paperclip's server at GET /api/adapters/:type/config-schema
|
|
// calls adapter.getConfigSchema() and the UI reads the JSON — types are only
|
|
// used at build time here. The Paperclip types in @paperclipai/adapter-utils
|
|
// may lag behind; these locals are the source of truth for this adapter.
|
|
|
|
interface ConfigFieldOption {
|
|
label: string;
|
|
value: string;
|
|
group?: string;
|
|
}
|
|
|
|
type ConfigFieldSchema =
|
|
| { type: "text"; key: string; label: string; hint?: string; default?: unknown; meta?: Record<string, unknown> }
|
|
| { type: "number"; key: string; label: string; hint?: string; default?: unknown; meta?: Record<string, unknown> }
|
|
| { type: "toggle"; key: string; label: string; hint?: string; default?: unknown; meta?: Record<string, unknown> }
|
|
| { type: "select"; key: string; label: string; hint?: string; options: ConfigFieldOption[]; default?: unknown; meta?: Record<string, unknown> }
|
|
| { type: "textarea"; key: string; label: string; hint?: string; default?: unknown; meta?: Record<string, unknown> }
|
|
| { type: "combobox"; key: string; label: string; hint?: string; options?: ConfigFieldOption[]; default?: unknown; meta?: Record<string, unknown> };
|
|
|
|
interface AdapterConfigSchema {
|
|
fields: ConfigFieldSchema[];
|
|
}
|
|
|
|
export function getConfigSchema(): AdapterConfigSchema {
|
|
// model, effort, instructionsFilePath, timeoutSec, graceSec are provided
|
|
// by the platform UI and must not be duplicated here.
|
|
const fields: ConfigFieldSchema[] = [
|
|
// Core Claude fields
|
|
{
|
|
type: "number",
|
|
key: "maxTurnsPerRun",
|
|
label: "Max Turns Per Run",
|
|
hint: "Maximum number of agentic turns (tool calls) per heartbeat run. 0 means unlimited.",
|
|
default: 1000,
|
|
},
|
|
// Kubernetes
|
|
{
|
|
type: "text",
|
|
key: "serviceAccountName",
|
|
label: "Service Account",
|
|
hint: "Service Account name for Job pods. Defaults to the cluster default.",
|
|
},
|
|
{
|
|
type: "text",
|
|
key: "namespace",
|
|
label: "Namespace",
|
|
hint: "Kubernetes namespace for Jobs. Defaults to the Deployment namespace.",
|
|
},
|
|
{
|
|
type: "text",
|
|
key: "image",
|
|
label: "Container Image",
|
|
hint: "Override the container image used for Job pods. Defaults to the running Deployment image.",
|
|
},
|
|
{
|
|
type: "select",
|
|
key: "imagePullPolicy",
|
|
label: "Image Pull Policy",
|
|
hint: "Image pull policy for the container image.",
|
|
options: [
|
|
{ value: "IfNotPresent", label: "IfNotPresent" },
|
|
{ value: "Always", label: "Always" },
|
|
{ value: "Never", label: "Never" },
|
|
],
|
|
},
|
|
{
|
|
type: "text",
|
|
key: "kubeconfig",
|
|
label: "Kubeconfig Path",
|
|
hint: "Absolute path to a kubeconfig file on disk. Defaults to in-cluster service account auth.",
|
|
},
|
|
{
|
|
type: "number",
|
|
key: "ttlSecondsAfterFinished",
|
|
label: "TTL Seconds After Finished",
|
|
hint: "Auto-cleanup delay for completed Jobs in seconds. Default: 300.",
|
|
},
|
|
{
|
|
type: "toggle",
|
|
key: "retainJobs",
|
|
label: "Retain Jobs",
|
|
hint: "Skip cleanup of completed Jobs for debugging purposes.",
|
|
},
|
|
{
|
|
type: "toggle",
|
|
key: "reattachOrphanedJobs",
|
|
label: "Reattach to Orphaned Jobs",
|
|
hint: "If a prior K8s Job for the same agent/task/session is still running (e.g. Paperclip restarted mid-run), attach to it and stream its output instead of blocking the new run. When false, any non-terminal orphan blocks the new run. Default: on.",
|
|
default: true,
|
|
},
|
|
// Resource Limits
|
|
{
|
|
type: "text",
|
|
key: "resources.requests.cpu",
|
|
label: "CPU Request",
|
|
hint: "CPU request for Job pods (e.g. 100m, 0.5, 1).",
|
|
},
|
|
{
|
|
type: "text",
|
|
key: "resources.requests.memory",
|
|
label: "Memory Request",
|
|
hint: "Memory request for Job pods (e.g. 128Mi, 512Mi, 1Gi).",
|
|
},
|
|
{
|
|
type: "text",
|
|
key: "resources.limits.cpu",
|
|
label: "CPU Limit",
|
|
hint: "CPU limit for Job pods (e.g. 100m, 0.5, 1).",
|
|
},
|
|
{
|
|
type: "text",
|
|
key: "resources.limits.memory",
|
|
label: "Memory Limit",
|
|
hint: "Memory limit for Job pods (e.g. 128Mi, 512Mi, 1Gi).",
|
|
},
|
|
// Scheduling
|
|
{
|
|
type: "textarea",
|
|
key: "nodeSelector",
|
|
label: "Node Selector",
|
|
hint: "Node selector for Job pods. One key=value per line (e.g. disktype=ssd).",
|
|
},
|
|
{
|
|
type: "textarea",
|
|
key: "tolerations",
|
|
label: "Tolerations",
|
|
hint: "Tolerations for Job pods as JSON array.",
|
|
},
|
|
{
|
|
type: "textarea",
|
|
key: "labels",
|
|
label: "Labels",
|
|
hint: "Extra labels added to Job metadata. One key=value per line.",
|
|
},
|
|
];
|
|
|
|
return { fields };
|
|
} |