Fix getConfigSchema to use flat fields array with correct hint keys
The Paperclip AdapterConfigSchema type expects a flat fields array, not nested sections. Also maps description -> hint per the schema type. Defines types locally since @paperclipai/adapter-utils@0.3.1 on npm does not yet export AdapterConfigSchema/ConfigFieldSchema (those exist in the monorepo but aren't released to npm yet). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@farhoodliquor/paperclip-adapter-claude-k8s",
|
"name": "@farhoodliquor/paperclip-adapter-claude-k8s",
|
||||||
"version": "0.1.8",
|
"version": "0.1.9",
|
||||||
"description": "Paperclip adapter plugin that runs Claude Code agents as Kubernetes Jobs",
|
"description": "Paperclip adapter plugin that runs Claude Code agents as Kubernetes Jobs",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
+109
-166
@@ -1,173 +1,116 @@
|
|||||||
export interface AdapterConfigSchema {
|
// NOTE: These types must match what Paperclip's SchemaConfigFields component
|
||||||
sections?: AdapterConfigSection[];
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdapterConfigSection {
|
type ConfigFieldSchema =
|
||||||
title: string;
|
| { 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[];
|
fields: ConfigFieldSchema[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ConfigFieldSchema =
|
|
||||||
| TextFieldSchema
|
|
||||||
| NumberFieldSchema
|
|
||||||
| ToggleFieldSchema
|
|
||||||
| SelectFieldSchema
|
|
||||||
| TextareaFieldSchema;
|
|
||||||
|
|
||||||
export interface TextFieldSchema {
|
|
||||||
type: "text";
|
|
||||||
key: string;
|
|
||||||
label: string;
|
|
||||||
description?: string;
|
|
||||||
placeholder?: string;
|
|
||||||
helpLink?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NumberFieldSchema {
|
|
||||||
type: "number";
|
|
||||||
key: string;
|
|
||||||
label: string;
|
|
||||||
description?: string;
|
|
||||||
placeholder?: string;
|
|
||||||
helpLink?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ToggleFieldSchema {
|
|
||||||
type: "toggle";
|
|
||||||
key: string;
|
|
||||||
label: string;
|
|
||||||
description?: string;
|
|
||||||
helpLink?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SelectFieldSchema {
|
|
||||||
type: "select";
|
|
||||||
key: string;
|
|
||||||
label: string;
|
|
||||||
description?: string;
|
|
||||||
options: { value: string; label: string }[];
|
|
||||||
helpLink?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TextareaFieldSchema {
|
|
||||||
type: "textarea";
|
|
||||||
key: string;
|
|
||||||
label: string;
|
|
||||||
description?: string;
|
|
||||||
placeholder?: string;
|
|
||||||
helpLink?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getConfigSchema(): AdapterConfigSchema {
|
export function getConfigSchema(): AdapterConfigSchema {
|
||||||
return {
|
const fields: ConfigFieldSchema[] = [
|
||||||
sections: [
|
// Kubernetes
|
||||||
{
|
{
|
||||||
title: "Kubernetes",
|
type: "text",
|
||||||
fields: [
|
key: "namespace",
|
||||||
{
|
label: "Namespace",
|
||||||
type: "text",
|
hint: "Kubernetes namespace for Jobs. Defaults to the Deployment namespace.",
|
||||||
key: "namespace",
|
},
|
||||||
label: "Namespace",
|
{
|
||||||
description: "Kubernetes namespace for Jobs. Defaults to the Deployment namespace.",
|
type: "text",
|
||||||
},
|
key: "image",
|
||||||
{
|
label: "Container Image",
|
||||||
type: "text",
|
hint: "Override the container image used for Job pods. Defaults to the running Deployment image.",
|
||||||
key: "image",
|
},
|
||||||
label: "Container Image",
|
{
|
||||||
description: "Override the container image used for Job pods. Defaults to the running Deployment image.",
|
type: "select",
|
||||||
placeholder: "registry/image:tag",
|
key: "imagePullPolicy",
|
||||||
},
|
label: "Image Pull Policy",
|
||||||
{
|
hint: "Image pull policy for the container image.",
|
||||||
type: "select",
|
options: [
|
||||||
key: "imagePullPolicy",
|
{ value: "IfNotPresent", label: "IfNotPresent" },
|
||||||
label: "Image Pull Policy",
|
{ value: "Always", label: "Always" },
|
||||||
description: "Image pull policy for the container image.",
|
{ value: "Never", label: "Never" },
|
||||||
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: "text",
|
},
|
||||||
key: "kubeconfig",
|
{
|
||||||
label: "Kubeconfig Path",
|
type: "number",
|
||||||
description: "Absolute path to a kubeconfig file on disk. Defaults to in-cluster service account auth.",
|
key: "ttlSecondsAfterFinished",
|
||||||
placeholder: "/path/to/kubeconfig",
|
label: "TTL Seconds After Finished",
|
||||||
},
|
hint: "Auto-cleanup delay for completed Jobs in seconds. Default: 300.",
|
||||||
{
|
},
|
||||||
type: "number",
|
{
|
||||||
key: "ttlSecondsAfterFinished",
|
type: "toggle",
|
||||||
label: "TTL Seconds After Finished",
|
key: "retainJobs",
|
||||||
description: "Auto-cleanup delay for completed Jobs in seconds. Default: 300.",
|
label: "Retain Jobs",
|
||||||
},
|
hint: "Skip cleanup of completed Jobs for debugging purposes.",
|
||||||
{
|
},
|
||||||
type: "toggle",
|
// Resource Limits
|
||||||
key: "retainJobs",
|
{
|
||||||
label: "Retain Jobs",
|
type: "text",
|
||||||
description: "Skip cleanup of completed Jobs for debugging purposes.",
|
key: "resources.requests.cpu",
|
||||||
},
|
label: "CPU Request",
|
||||||
],
|
hint: "CPU request for Job pods (e.g. 100m, 0.5, 1).",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Resource Limits",
|
type: "text",
|
||||||
fields: [
|
key: "resources.requests.memory",
|
||||||
{
|
label: "Memory Request",
|
||||||
type: "text",
|
hint: "Memory request for Job pods (e.g. 128Mi, 512Mi, 1Gi).",
|
||||||
key: "resources.requests.cpu",
|
},
|
||||||
label: "CPU Request",
|
{
|
||||||
description: "CPU request for Job pods (e.g. 100m, 0.5, 1).",
|
type: "text",
|
||||||
placeholder: "100m",
|
key: "resources.limits.cpu",
|
||||||
},
|
label: "CPU Limit",
|
||||||
{
|
hint: "CPU limit for Job pods (e.g. 100m, 0.5, 1).",
|
||||||
type: "text",
|
},
|
||||||
key: "resources.requests.memory",
|
{
|
||||||
label: "Memory Request",
|
type: "text",
|
||||||
description: "Memory request for Job pods (e.g. 128Mi, 512Mi, 1Gi).",
|
key: "resources.limits.memory",
|
||||||
placeholder: "512Mi",
|
label: "Memory Limit",
|
||||||
},
|
hint: "Memory limit for Job pods (e.g. 128Mi, 512Mi, 1Gi).",
|
||||||
{
|
},
|
||||||
type: "text",
|
// Scheduling
|
||||||
key: "resources.limits.cpu",
|
{
|
||||||
label: "CPU Limit",
|
type: "textarea",
|
||||||
description: "CPU limit for Job pods (e.g. 100m, 0.5, 1).",
|
key: "nodeSelector",
|
||||||
placeholder: "1000m",
|
label: "Node Selector",
|
||||||
},
|
hint: "Node selector for Job pods. One key=value per line (e.g. disktype=ssd).",
|
||||||
{
|
},
|
||||||
type: "text",
|
{
|
||||||
key: "resources.limits.memory",
|
type: "textarea",
|
||||||
label: "Memory Limit",
|
key: "tolerations",
|
||||||
description: "Memory limit for Job pods (e.g. 128Mi, 512Mi, 1Gi).",
|
label: "Tolerations",
|
||||||
placeholder: "1Gi",
|
hint: "Tolerations for Job pods as JSON array.",
|
||||||
},
|
},
|
||||||
],
|
{
|
||||||
},
|
type: "textarea",
|
||||||
{
|
key: "labels",
|
||||||
title: "Scheduling",
|
label: "Labels",
|
||||||
fields: [
|
hint: "Extra labels added to Job metadata. One key=value per line.",
|
||||||
{
|
},
|
||||||
type: "textarea",
|
];
|
||||||
key: "nodeSelector",
|
|
||||||
label: "Node Selector",
|
return { fields };
|
||||||
description: "Node selector for Job pods. One key=value per line (e.g. disktype=ssd).",
|
|
||||||
placeholder: "disktype=ssd\ngpu=true",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "textarea",
|
|
||||||
key: "tolerations",
|
|
||||||
label: "Tolerations",
|
|
||||||
description: "Tolerations for Job pods as JSON array.",
|
|
||||||
placeholder: '[{"key":"node-type","operator":"Equal","value":"gpu","effect":"NoSchedule"}]',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "textarea",
|
|
||||||
key: "labels",
|
|
||||||
label: "Labels",
|
|
||||||
description: "Extra labels added to Job metadata. One key=value per line.",
|
|
||||||
placeholder: "team=ai\nenv=prod",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user