@tickernelz/paperclip-pro-plugin-kubernetes 2026.925.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +180 -0
  3. package/dist/adapter-defaults.d.ts +29 -0
  4. package/dist/adapter-defaults.d.ts.map +1 -0
  5. package/dist/adapter-defaults.js +98 -0
  6. package/dist/adapter-defaults.js.map +1 -0
  7. package/dist/adapter-registry.d.ts +64 -0
  8. package/dist/adapter-registry.d.ts.map +1 -0
  9. package/dist/adapter-registry.js +25 -0
  10. package/dist/adapter-registry.js.map +1 -0
  11. package/dist/cilium-network-policy.d.ts +12 -0
  12. package/dist/cilium-network-policy.d.ts.map +1 -0
  13. package/dist/cilium-network-policy.js +60 -0
  14. package/dist/cilium-network-policy.js.map +1 -0
  15. package/dist/file-sync.d.ts +73 -0
  16. package/dist/file-sync.d.ts.map +1 -0
  17. package/dist/file-sync.js +752 -0
  18. package/dist/file-sync.js.map +1 -0
  19. package/dist/image-allowlist.d.ts +18 -0
  20. package/dist/image-allowlist.d.ts.map +1 -0
  21. package/dist/image-allowlist.js +39 -0
  22. package/dist/image-allowlist.js.map +1 -0
  23. package/dist/index.d.ts +3 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +3 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/job-orchestrator.d.ts +24 -0
  28. package/dist/job-orchestrator.d.ts.map +1 -0
  29. package/dist/job-orchestrator.js +90 -0
  30. package/dist/job-orchestrator.js.map +1 -0
  31. package/dist/kube-client.d.ts +15 -0
  32. package/dist/kube-client.d.ts.map +1 -0
  33. package/dist/kube-client.js +23 -0
  34. package/dist/kube-client.js.map +1 -0
  35. package/dist/lease-lifecycle.d.ts +62 -0
  36. package/dist/lease-lifecycle.d.ts.map +1 -0
  37. package/dist/lease-lifecycle.js +155 -0
  38. package/dist/lease-lifecycle.js.map +1 -0
  39. package/dist/manifest.d.ts +4 -0
  40. package/dist/manifest.d.ts.map +1 -0
  41. package/dist/manifest.js +108 -0
  42. package/dist/manifest.js.map +1 -0
  43. package/dist/network-policy.d.ts +22 -0
  44. package/dist/network-policy.d.ts.map +1 -0
  45. package/dist/network-policy.js +116 -0
  46. package/dist/network-policy.js.map +1 -0
  47. package/dist/plugin.d.ts +3 -0
  48. package/dist/plugin.d.ts.map +1 -0
  49. package/dist/plugin.js +820 -0
  50. package/dist/plugin.js.map +1 -0
  51. package/dist/pod-exec.d.ts +67 -0
  52. package/dist/pod-exec.d.ts.map +1 -0
  53. package/dist/pod-exec.js +401 -0
  54. package/dist/pod-exec.js.map +1 -0
  55. package/dist/pod-spec-builder.d.ts +25 -0
  56. package/dist/pod-spec-builder.d.ts.map +1 -0
  57. package/dist/pod-spec-builder.js +82 -0
  58. package/dist/pod-spec-builder.js.map +1 -0
  59. package/dist/sandbox-cr-builder.d.ts +40 -0
  60. package/dist/sandbox-cr-builder.d.ts.map +1 -0
  61. package/dist/sandbox-cr-builder.js +123 -0
  62. package/dist/sandbox-cr-builder.js.map +1 -0
  63. package/dist/sandbox-cr-orchestrator.d.ts +62 -0
  64. package/dist/sandbox-cr-orchestrator.d.ts.map +1 -0
  65. package/dist/sandbox-cr-orchestrator.js +248 -0
  66. package/dist/sandbox-cr-orchestrator.js.map +1 -0
  67. package/dist/sandbox-orchestrator.d.ts +39 -0
  68. package/dist/sandbox-orchestrator.d.ts.map +1 -0
  69. package/dist/sandbox-orchestrator.js +2 -0
  70. package/dist/sandbox-orchestrator.js.map +1 -0
  71. package/dist/scoped-network-egress.d.ts +19 -0
  72. package/dist/scoped-network-egress.d.ts.map +1 -0
  73. package/dist/scoped-network-egress.js +86 -0
  74. package/dist/scoped-network-egress.js.map +1 -0
  75. package/dist/secret-manager.d.ts +14 -0
  76. package/dist/secret-manager.d.ts.map +1 -0
  77. package/dist/secret-manager.js +39 -0
  78. package/dist/secret-manager.js.map +1 -0
  79. package/dist/tenant-orchestrator.d.ts +35 -0
  80. package/dist/tenant-orchestrator.d.ts.map +1 -0
  81. package/dist/tenant-orchestrator.js +263 -0
  82. package/dist/tenant-orchestrator.js.map +1 -0
  83. package/dist/types.d.ts +279 -0
  84. package/dist/types.d.ts.map +1 -0
  85. package/dist/types.js +65 -0
  86. package/dist/types.js.map +1 -0
  87. package/dist/upload-interceptor.d.ts +44 -0
  88. package/dist/upload-interceptor.d.ts.map +1 -0
  89. package/dist/upload-interceptor.js +114 -0
  90. package/dist/upload-interceptor.js.map +1 -0
  91. package/dist/utils.d.ts +11 -0
  92. package/dist/utils.d.ts.map +1 -0
  93. package/dist/utils.js +51 -0
  94. package/dist/utils.js.map +1 -0
  95. package/dist/worker.d.ts +3 -0
  96. package/dist/worker.d.ts.map +1 -0
  97. package/dist/worker.js +5 -0
  98. package/dist/worker.js.map +1 -0
  99. package/manifests/operator-prerequisites.yaml +22 -0
  100. package/package.json +47 -0
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Paperclip AI
4
+ Copyright (c) 2026 Zhafron Adani Kautsar (paperclip-pro fork modifications)
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,180 @@
1
+ # @tickernelz/paperclip-pro-plugin-kubernetes (alpha)
2
+
3
+ First-party Paperclip sandbox-provider plugin for Kubernetes.
4
+
5
+ **Alpha:** the default backend (`sandbox-cr`) is built on `kubernetes-sigs/agent-sandbox` v1alpha1 — expect breaking changes as that CRD evolves toward Beta. A stable fallback backend (`job`, using `batch/v1` Job) is available for clusters without agent-sandbox installed, but it does NOT support multi-command exec (paperclip-server's adapter-install pattern requires sandbox-cr).
6
+
7
+ ## Prerequisites
8
+
9
+ ### For `sandbox-cr` backend (default, recommended)
10
+
11
+ 1. A Kubernetes cluster running k8s 1.27+
12
+ 2. [`kubernetes-sigs/agent-sandbox`](https://github.com/kubernetes-sigs/agent-sandbox) controller installed in the cluster (alpha — installs the `sandboxes.agents.x-k8s.io/v1alpha1` CRD and controller)
13
+ 3. Paperclip-server running with access to the cluster (in-cluster via `inCluster: true` or external via `kubeconfig`)
14
+
15
+ ### For `job` backend (stable fallback)
16
+
17
+ 1. A Kubernetes cluster running k8s 1.27+
18
+ 2. Paperclip-server with cluster access — no additional controllers or CRDs required
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ paperclip-pro plugin install @tickernelz/paperclip-pro-plugin-kubernetes
24
+ ```
25
+
26
+ Or, for local development:
27
+
28
+ ```bash
29
+ paperclip-pro plugin install --local /path/to/paperclip/packages/plugins/sandbox-providers/kubernetes
30
+ ```
31
+
32
+ ## Backends
33
+
34
+ The plugin supports two backend modes, selected via the `backend` config field:
35
+
36
+ | Backend | Default | Stability | Multi-command exec | Requires |
37
+ |---|---|---|---|---|
38
+ | `sandbox-cr` | Yes | Alpha | Yes | `kubernetes-sigs/agent-sandbox` controller |
39
+ | `job` | No | Stable | No | Nothing beyond k8s 1.27+ |
40
+
41
+ **`sandbox-cr` (default):** Creates a `Sandbox` CR (`agents.x-k8s.io/v1alpha1`) whose controller provisions a long-lived pod running `sleep infinity`. paperclip-server execs individual commands into the running pod — this is the multi-command adapter-install pattern. When you `releaseLease`, the Sandbox CR is deleted and the controller tears down the pod.
42
+
43
+ **`job` (stable fallback):** Creates a `batch/v1` Job. The container entrypoint runs once and exits — no multi-command exec possible. Use this when you cannot install agent-sandbox, or when you need strictly stable Kubernetes APIs. Note: paperclip-server's adapter-install pattern will not work in job mode.
44
+
45
+ ### Migrating from `job` to `sandbox-cr`
46
+
47
+ 1. Install the agent-sandbox controller: `kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/latest/download/install.yaml`
48
+ 2. Update your environment config to set `backend: "sandbox-cr"` (or remove `backend` since `sandbox-cr` is the default)
49
+ 3. New leases will use the Sandbox CR backend. Existing leases created with `job` mode continue to use job semantics until they are released.
50
+
51
+ ## Configuration
52
+
53
+ Create a `sandbox` environment with `driver: kubernetes`. One of these auth fields is required:
54
+
55
+ - `inCluster: true` — use the in-pod ServiceAccount credentials (when paperclip-server runs inside the same cluster).
56
+ - `kubeconfig: <YAML>` — inline kubeconfig (stored as a company secret).
57
+ - `kubeconfigSecretRef: <secret-uuid>` — reference to an existing Paperclip secret.
58
+
59
+ Common optional fields:
60
+
61
+ | Field | Default | Purpose |
62
+ |---|---|---|
63
+ | `backend` | `"sandbox-cr"` | `sandbox-cr` (alpha, requires agent-sandbox controller) or `job` (stable, one-shot entrypoint). |
64
+ | `adapterType` | `"claude_local"` | One of the supported adapter types (claude_local, codex_local, gemini_local, cursor_local, opencode_local, pi_local). Determines runtime image + env keys + egress allow-list. |
65
+ | `namespacePrefix` | `"paperclip-"` | Prefix for the per-company tenant namespace. |
66
+ | `companySlug` | derived from companyId | Override the auto-derived company slug. |
67
+ | `imageRegistry` | (none) | Override the default registry for agent runtime images. |
68
+ | `imageAllowList` | `[]` | Glob patterns of allowed `target.imageOverride` values. Empty = no override permitted. |
69
+ | `imagePullSecrets` | `[]` | Names of pre-created Docker image pull secrets in the tenant namespace. |
70
+ | `egressAllowFqdns` | `[]` | Additional FQDNs (beyond adapter defaults like `api.anthropic.com`). |
71
+ | `egressAllowCidrs` | `[]` | Additional CIDRs to allow egress to. |
72
+ | `egressMode` | `"standard"` | `standard` (NetworkPolicy + CIDRs) or `cilium` (CiliumNetworkPolicy + FQDN allow-list). |
73
+ | `runtimeClassName` | (none) | e.g. `kata-fc` for Firecracker-backed microVMs. Cluster must have the RuntimeClass installed. |
74
+ | `serviceAccountAnnotations` | `{}` | Annotations applied to per-tenant ServiceAccount (e.g. IRSA `eks.amazonaws.com/role-arn`). |
75
+ | `jobTtlSecondsAfterFinished` | `900` | Seconds after a Job completes before garbage-collection. |
76
+ | `podActivityDeadlineSec` | `3600` | Hard ceiling on a single run's wall-clock time. |
77
+
78
+ Full JSON Schema in `src/manifest.ts`.
79
+
80
+ ### Task-scoped egress grants
81
+
82
+ Keep provider-level egress defaults narrow, then grant only the destinations a task needs through its execution workspace settings:
83
+
84
+ ```json
85
+ {
86
+ "executionWorkspaceSettings": {
87
+ "networkEgress": {
88
+ "allowFqdns": ["github.com", "pypi.org"],
89
+ "allowCidrs": []
90
+ }
91
+ }
92
+ }
93
+ ```
94
+
95
+ The provider creates a workload-owned policy selected by the task run label, so the additional destinations do not become reachable from other concurrent agent pods. Cilium mode enforces FQDNs directly. Standard NetworkPolicy mode cannot express FQDNs, so an FQDN grant permits public IPv4 TCP 80/443 for that run while excluding private, loopback, link-local, CGNAT, and multicast ranges. Network failures that look policy-related include the grant path in stderr, and the sandbox exposes the effective policy through `PAPERCLIP_NETWORK_EGRESS_*` environment variables.
96
+
97
+ ## What gets created in your cluster
98
+
99
+ For each company that runs agents (created lazily on first dispatch):
100
+
101
+ ```
102
+ Namespace paperclip-{companySlug} (PSS: restricted enforce + audit)
103
+ ServiceAccount paperclip-tenant-sa
104
+ Role paperclip-tenant-role (only get pods/log)
105
+ RoleBinding paperclip-tenant-rb
106
+ ResourceQuota paperclip-quota (pods, requests/limits cpu+memory)
107
+ LimitRange paperclip-limits (container max/min/default/defaultRequest)
108
+ NetworkPolicy paperclip-deny-all (deny ingress + egress baseline)
109
+ NetworkPolicy paperclip-egress-allow (DNS + paperclip-server callback + user CIDRs)
110
+ OR CiliumNetworkPolicy paperclip-egress-fqdn if egressMode=cilium
111
+ ```
112
+
113
+ For each agent run (sandbox-cr backend):
114
+
115
+ ```
116
+ Sandbox CR pc-{ulid} (agents.x-k8s.io/v1alpha1; explicit delete on release)
117
+ Pod pc-{ulid}-{podSuffix} (managed by Sandbox controller; torn down on CR delete)
118
+ Secret pc-{ulid}-env (owned by Sandbox CR; cascade-deleted)
119
+ ```
120
+
121
+ For each agent run (job backend):
122
+
123
+ ```
124
+ Job pc-{ulid} (backoffLimit: 0, ttlSecondsAfterFinished from config)
125
+ Pod pc-{ulid}-{podSuffix} (owned by Job; cascade-deleted)
126
+ Secret pc-{ulid}-env (owned by Job; cascade-deleted)
127
+ ```
128
+
129
+ ## Security baseline
130
+
131
+ Every agent pod is:
132
+
133
+ - non-root (`runAsUser: 1000`, `runAsGroup: 1000`, `runAsNonRoot: true`)
134
+ - drops ALL Linux capabilities, `allowPrivilegeEscalation: false`
135
+ - `readOnlyRootFilesystem: true` with explicit `emptyDir` mounts for `/workspace`, `/home/paperclip`, `/home/paperclip/.cache`, `/tmp`
136
+ - `seccompProfile: RuntimeDefault`
137
+ - Tini as PID 1 (reaps zombies, forwards signals)
138
+ - `fsGroupChangePolicy: OnRootMismatch` (fast PVC startup; openclaw-operator lesson)
139
+ - `automountServiceAccountToken: true` (for the agent shim's paperclip-server callback)
140
+
141
+ Plus per-namespace `pod-security.kubernetes.io/enforce: restricted` and a deny-all NetworkPolicy baseline with explicit egress allow-list (DNS, paperclip-server, configured FQDNs/CIDRs).
142
+
143
+ The per-run Secret carrying the bootstrap token and adapter API keys has `ownerReferences` pointing at the owning Job, so a single `kubectl delete job …` cascades cleanly to the Pod and Secret.
144
+
145
+ ## Optional Kata-FC microVM isolation
146
+
147
+ For stronger isolation, install [Kata Containers](https://github.com/kata-containers/kata-containers) with the Firecracker hypervisor, then set `runtimeClassName: kata-fc` in the plugin config. Each agent pod will run inside a Firecracker microVM. Requires nested-virt-capable nodes (bare-metal or specific cloud instance types).
148
+
149
+ ## Roadmap
150
+
151
+ - **Phase A (done):** `sandbox-cr` backend — multi-command exec via agent-sandbox Sandbox CRD.
152
+ - **Phase B:** Warm pool support — pre-provisioned Sandbox CRs for sub-second cold starts. The `SandboxOrchestrator` interface reserves optional `pause?`/`resume?` extension slots.
153
+ - **Phase C:** Kata-FC + snapshots — `runtimeClassName: kata-fc` with VM snapshot for fast restore.
154
+ - **Phase D:** Contribute back to agent-sandbox upstream if their Beta model diverges from our needs. The `SandboxOrchestrator` interface (`src/sandbox-orchestrator.ts`) is the clean swap point — a new implementation can be added without touching `plugin.ts` business logic.
155
+
156
+ ## Lessons learned (from openclaw-operator)
157
+
158
+ This plugin adopts patterns from `openclaw-rocks/openclaw-operator`:
159
+
160
+ - Tini PID 1 (issue #471 — zombie helper processes)
161
+ - Read-only rootFS with explicit writable mounts (issue #456 — ~/.config not writable)
162
+ - Strategic merge on reconcile (issue #446 — preserve third-party annotations)
163
+ - Multi-storage-class testing (issue #448 — `local-path-provisioner` differences)
164
+ - Image version compat matrix (issue #462 — runtime deps cannot resolve after upgrade)
165
+
166
+ ## Development
167
+
168
+ ```bash
169
+ cd packages/plugins/sandbox-providers/kubernetes
170
+ pnpm install --ignore-workspace
171
+ pnpm test # unit tests only (fast)
172
+ pnpm typecheck
173
+ pnpm build
174
+ ```
175
+
176
+ To run the kind-cluster integration test (requires `kubectl --context kind-paperclip` and a pre-loaded alpine image; see `test/integration/end-to-end-run.test.ts`):
177
+
178
+ ```bash
179
+ RUN_K8S_INTEGRATION_TESTS=1 pnpm test test/integration/end-to-end-run.test.ts
180
+ ```
@@ -0,0 +1,29 @@
1
+ import type { AdapterRegistryEntry } from "./adapter-registry.js";
2
+ export interface AdapterDefaults {
3
+ runtimeImage: string;
4
+ envKeys: string[];
5
+ allowFqdns: string[];
6
+ probeCommand: string[];
7
+ /** Non-secret env injected as the base layer for the Job (process-env wins on top). */
8
+ defaultEnv?: Record<string, string>;
9
+ }
10
+ export declare const KNOWN_ADAPTER_TYPES: ReadonlySet<string>;
11
+ /**
12
+ * Resolve the runtime defaults for an adapter. When a `registry` is supplied it
13
+ * is authoritative (replace semantics): the type MUST be present and complete,
14
+ * else this throws. With no registry, falls back to the built-in REGISTRY.
15
+ */
16
+ export declare function getAdapterDefaults(adapterType: string, registry?: readonly AdapterRegistryEntry[]): AdapterDefaults;
17
+ /**
18
+ * Resolve the adapter type for a single run: prefer the run's adapter (the agent's,
19
+ * from the lease params) so one environment can serve mixed harnesses; fall back to
20
+ * the environment's configured default adapter when the run does not specify one.
21
+ */
22
+ export declare function resolveRunAdapterType(runAdapterType: string | null | undefined, configAdapterType: string): string;
23
+ /**
24
+ * Build the per-run env for the Job: the non-secret `defaultEnv` is the base
25
+ * and the process-env values (the secret API keys named by `envKeys`) override
26
+ * it. Pure for testability.
27
+ */
28
+ export declare function buildAdapterEnv(defaults: AdapterDefaults, processEnv?: NodeJS.ProcessEnv): Record<string, string>;
29
+ //# sourceMappingURL=adapter-defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter-defaults.d.ts","sourceRoot":"","sources":["../src/adapter-defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAElE,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,uFAAuF;IACvF,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC;AAyCD,eAAO,MAAM,mBAAmB,EAAE,WAAW,CAAC,MAAM,CAAkC,CAAC;AAqBvF;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,SAAS,oBAAoB,EAAE,GACzC,eAAe,CAajB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,cAAc,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACzC,iBAAiB,EAAE,MAAM,GACxB,MAAM,CAGR;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,eAAe,EACzB,UAAU,GAAE,MAAM,CAAC,UAAwB,GAC1C,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOxB"}
@@ -0,0 +1,98 @@
1
+ const REGISTRY = {
2
+ claude_local: {
3
+ runtimeImage: "ghcr.io/paperclipai/agent-runtime-claude:v1",
4
+ envKeys: ["ANTHROPIC_API_KEY"],
5
+ allowFqdns: ["api.anthropic.com"],
6
+ probeCommand: ["claude", "--version"],
7
+ },
8
+ codex_local: {
9
+ runtimeImage: "ghcr.io/paperclipai/agent-runtime-codex:v1",
10
+ envKeys: ["OPENAI_API_KEY"],
11
+ allowFqdns: ["api.openai.com"],
12
+ probeCommand: ["codex", "--version"],
13
+ },
14
+ gemini_local: {
15
+ runtimeImage: "ghcr.io/paperclipai/agent-runtime-gemini:v1",
16
+ envKeys: ["GOOGLE_API_KEY", "GEMINI_API_KEY"],
17
+ allowFqdns: ["generativelanguage.googleapis.com"],
18
+ probeCommand: ["gemini", "--version"],
19
+ },
20
+ cursor_local: {
21
+ runtimeImage: "ghcr.io/paperclipai/agent-runtime-cursor:v1",
22
+ envKeys: ["ANTHROPIC_API_KEY", "OPENAI_API_KEY"],
23
+ allowFqdns: ["api.anthropic.com", "api.openai.com"],
24
+ probeCommand: ["cursor-agent", "--version"],
25
+ },
26
+ opencode_local: {
27
+ runtimeImage: "ghcr.io/paperclipai/agent-runtime-opencode:v1",
28
+ envKeys: ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "OPENROUTER_API_KEY"],
29
+ allowFqdns: ["api.anthropic.com", "api.openai.com", "openrouter.ai"],
30
+ probeCommand: ["opencode", "--version"],
31
+ },
32
+ pi_local: {
33
+ runtimeImage: "ghcr.io/paperclipai/agent-runtime-pi:v1",
34
+ envKeys: ["ANTHROPIC_API_KEY"],
35
+ allowFqdns: ["api.anthropic.com"],
36
+ probeCommand: ["pi", "--version"],
37
+ },
38
+ };
39
+ export const KNOWN_ADAPTER_TYPES = new Set(Object.keys(REGISTRY));
40
+ function fromRegistryEntry(entry) {
41
+ // Only runtimeImage is strictly required. The array fields are optional and
42
+ // default to []: the operator emits them with `omitempty`, so a genuinely
43
+ // empty allowFqdns/envKeys/probeCommand arrives as undefined, which is valid
44
+ // (no extra egress / no forwarded secrets / no probe), NOT an error.
45
+ if (!entry.runtimeImage) {
46
+ throw new Error(`Adapter "${entry.adapterType}" is missing required runtime field: runtimeImage`);
47
+ }
48
+ return {
49
+ runtimeImage: entry.runtimeImage,
50
+ envKeys: entry.envKeys ?? [],
51
+ allowFqdns: entry.allowFqdns ?? [],
52
+ probeCommand: entry.probeCommand ?? [],
53
+ defaultEnv: entry.defaultEnv,
54
+ };
55
+ }
56
+ /**
57
+ * Resolve the runtime defaults for an adapter. When a `registry` is supplied it
58
+ * is authoritative (replace semantics): the type MUST be present and complete,
59
+ * else this throws. With no registry, falls back to the built-in REGISTRY.
60
+ */
61
+ export function getAdapterDefaults(adapterType, registry) {
62
+ if (registry && registry.length > 0) {
63
+ const entry = registry.find((e) => e.adapterType === adapterType);
64
+ if (!entry) {
65
+ throw new Error(`Adapter "${adapterType}" is not in the configured adapter registry`);
66
+ }
67
+ return fromRegistryEntry(entry);
68
+ }
69
+ const defaults = REGISTRY[adapterType];
70
+ if (!defaults) {
71
+ throw new Error(`Unknown adapter type: ${adapterType}`);
72
+ }
73
+ return defaults;
74
+ }
75
+ /**
76
+ * Resolve the adapter type for a single run: prefer the run's adapter (the agent's,
77
+ * from the lease params) so one environment can serve mixed harnesses; fall back to
78
+ * the environment's configured default adapter when the run does not specify one.
79
+ */
80
+ export function resolveRunAdapterType(runAdapterType, configAdapterType) {
81
+ const trimmed = typeof runAdapterType === "string" ? runAdapterType.trim() : "";
82
+ return trimmed.length > 0 ? trimmed : configAdapterType;
83
+ }
84
+ /**
85
+ * Build the per-run env for the Job: the non-secret `defaultEnv` is the base
86
+ * and the process-env values (the secret API keys named by `envKeys`) override
87
+ * it. Pure for testability.
88
+ */
89
+ export function buildAdapterEnv(defaults, processEnv = process.env) {
90
+ const out = { ...(defaults.defaultEnv ?? {}) };
91
+ for (const k of defaults.envKeys) {
92
+ const v = processEnv[k];
93
+ if (v)
94
+ out[k] = v;
95
+ }
96
+ return out;
97
+ }
98
+ //# sourceMappingURL=adapter-defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter-defaults.js","sourceRoot":"","sources":["../src/adapter-defaults.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * One declarative agent-harness ("adapter") entry. Governs picker availability
4
+ * and, for sandboxed (Kubernetes) runs, the runtime wiring.
5
+ *
6
+ * NOTE: this shape is intentionally duplicated across the package boundary. It
7
+ * MUST stay structurally in sync with:
8
+ * - server `@tickernelz/paperclip-pro-shared` `adapterRegistryEntrySchema` (the parser side)
9
+ * - operator `AdapterRegistryEntry` Go struct (PAPERCLIP_ADAPTERS emitter)
10
+ * The duplication is deliberate: this plugin is standalone-installable and must
11
+ * not pull in heavy workspace packages at runtime.
12
+ */
13
+ export declare const adapterRegistryEntrySchema: z.ZodObject<{
14
+ adapterType: z.ZodString;
15
+ enabled: z.ZodDefault<z.ZodBoolean>;
16
+ runtimeImage: z.ZodOptional<z.ZodString>;
17
+ envKeys: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
18
+ allowFqdns: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
19
+ probeCommand: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
20
+ defaultEnv: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
21
+ }, "strict", z.ZodTypeAny, {
22
+ adapterType: string;
23
+ enabled: boolean;
24
+ runtimeImage?: string | undefined;
25
+ envKeys?: string[] | undefined;
26
+ allowFqdns?: string[] | undefined;
27
+ probeCommand?: string[] | undefined;
28
+ defaultEnv?: Record<string, string> | undefined;
29
+ }, {
30
+ adapterType: string;
31
+ enabled?: boolean | undefined;
32
+ runtimeImage?: string | undefined;
33
+ envKeys?: string[] | undefined;
34
+ allowFqdns?: string[] | undefined;
35
+ probeCommand?: string[] | undefined;
36
+ defaultEnv?: Record<string, string> | undefined;
37
+ }>;
38
+ export declare const adapterRegistrySchema: z.ZodArray<z.ZodObject<{
39
+ adapterType: z.ZodString;
40
+ enabled: z.ZodDefault<z.ZodBoolean>;
41
+ runtimeImage: z.ZodOptional<z.ZodString>;
42
+ envKeys: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
43
+ allowFqdns: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
44
+ probeCommand: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
45
+ defaultEnv: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
46
+ }, "strict", z.ZodTypeAny, {
47
+ adapterType: string;
48
+ enabled: boolean;
49
+ runtimeImage?: string | undefined;
50
+ envKeys?: string[] | undefined;
51
+ allowFqdns?: string[] | undefined;
52
+ probeCommand?: string[] | undefined;
53
+ defaultEnv?: Record<string, string> | undefined;
54
+ }, {
55
+ adapterType: string;
56
+ enabled?: boolean | undefined;
57
+ runtimeImage?: string | undefined;
58
+ envKeys?: string[] | undefined;
59
+ allowFqdns?: string[] | undefined;
60
+ probeCommand?: string[] | undefined;
61
+ defaultEnv?: Record<string, string> | undefined;
62
+ }>, "many">;
63
+ export type AdapterRegistryEntry = z.infer<typeof adapterRegistryEntrySchema>;
64
+ //# sourceMappingURL=adapter-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter-registry.d.ts","sourceRoot":"","sources":["../src/adapter-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;EAU5B,CAAC;AAEZ,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;WAAsC,CAAC;AAEzE,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * One declarative agent-harness ("adapter") entry. Governs picker availability
4
+ * and, for sandboxed (Kubernetes) runs, the runtime wiring.
5
+ *
6
+ * NOTE: this shape is intentionally duplicated across the package boundary. It
7
+ * MUST stay structurally in sync with:
8
+ * - server `@tickernelz/paperclip-pro-shared` `adapterRegistryEntrySchema` (the parser side)
9
+ * - operator `AdapterRegistryEntry` Go struct (PAPERCLIP_ADAPTERS emitter)
10
+ * The duplication is deliberate: this plugin is standalone-installable and must
11
+ * not pull in heavy workspace packages at runtime.
12
+ */
13
+ export const adapterRegistryEntrySchema = z
14
+ .object({
15
+ adapterType: z.string().min(1),
16
+ enabled: z.boolean().default(true),
17
+ runtimeImage: z.string().optional(),
18
+ envKeys: z.array(z.string()).optional(),
19
+ allowFqdns: z.array(z.string()).optional(),
20
+ probeCommand: z.array(z.string()).optional(),
21
+ defaultEnv: z.record(z.string()).optional(),
22
+ })
23
+ .strict();
24
+ export const adapterRegistrySchema = z.array(adapterRegistryEntrySchema);
25
+ //# sourceMappingURL=adapter-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter-registry.js","sourceRoot":"","sources":["../src/adapter-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC;KACxC,MAAM,CAAC;IACN,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9B,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IAClC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACnC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACvC,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC1C,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC5C,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;CAC5C,CAAC;KACD,MAAM,EAAE,CAAC;AAEZ,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC"}
@@ -0,0 +1,12 @@
1
+ export interface BuildCiliumNetworkPolicyInput {
2
+ namespace: string;
3
+ paperclipServerNamespace: string;
4
+ egressAllowFqdns: string[];
5
+ egressAllowCidrs: string[];
6
+ name?: string;
7
+ endpointSelector?: Record<string, string>;
8
+ includeBaseRules?: boolean;
9
+ ownerReferences?: Record<string, unknown>[];
10
+ }
11
+ export declare function buildCiliumNetworkPolicyManifest(input: BuildCiliumNetworkPolicyInput): Record<string, unknown>;
12
+ //# sourceMappingURL=cilium-network-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cilium-network-policy.d.ts","sourceRoot":"","sources":["../src/cilium-network-policy.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,6BAA6B;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,wBAAwB,EAAE,MAAM,CAAC;IACjC,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;CAC7C;AAMD,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,6BAA6B,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAyD9G"}
@@ -0,0 +1,60 @@
1
+ // Design note: no ingress rules are defined here. Paperclip-server does NOT
2
+ // push to agent pods — agents make outbound (egress) callbacks to
3
+ // paperclip-server on port 3100. If server→agent push is ever needed, add a
4
+ // targeted ingress rule scoped to the paperclip-server endpoint selector.
5
+ export function buildCiliumNetworkPolicyManifest(input) {
6
+ const egress = [];
7
+ if (input.includeBaseRules !== false)
8
+ egress.push({
9
+ toEndpoints: [
10
+ { matchLabels: { "k8s:io.kubernetes.pod.namespace": "kube-system", "k8s-app": "kube-dns" } },
11
+ ],
12
+ toPorts: [
13
+ {
14
+ ports: [
15
+ { port: "53", protocol: "UDP" },
16
+ { port: "53", protocol: "TCP" },
17
+ ],
18
+ rules: { dns: [{ matchPattern: "*" }] },
19
+ },
20
+ ],
21
+ });
22
+ if (input.egressAllowFqdns.length > 0) {
23
+ egress.push({
24
+ toFQDNs: input.egressAllowFqdns.map((fqdn) => ({ matchName: fqdn })),
25
+ toPorts: [{ ports: [{ port: "443", protocol: "TCP" }] }],
26
+ });
27
+ }
28
+ if (input.includeBaseRules !== false)
29
+ egress.push({
30
+ toEndpoints: [
31
+ {
32
+ matchLabels: {
33
+ "k8s:io.kubernetes.pod.namespace": input.paperclipServerNamespace,
34
+ app: "paperclip-server",
35
+ },
36
+ },
37
+ ],
38
+ toPorts: [{ ports: [{ port: "3100", protocol: "TCP" }] }],
39
+ });
40
+ if (input.egressAllowCidrs.length > 0) {
41
+ egress.push({
42
+ toCIDRSet: input.egressAllowCidrs.map((cidr) => ({ cidr })),
43
+ });
44
+ }
45
+ return {
46
+ apiVersion: "cilium.io/v2",
47
+ kind: "CiliumNetworkPolicy",
48
+ metadata: {
49
+ name: input.name ?? "paperclip-egress-fqdn",
50
+ namespace: input.namespace,
51
+ labels: { "paperclip.io/managed-by": "paperclip-k8s-plugin" },
52
+ ...(input.ownerReferences ? { ownerReferences: input.ownerReferences } : {}),
53
+ },
54
+ spec: {
55
+ endpointSelector: { matchLabels: input.endpointSelector ?? { "paperclip.io/role": "agent" } },
56
+ egress,
57
+ },
58
+ };
59
+ }
60
+ //# sourceMappingURL=cilium-network-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cilium-network-policy.js","sourceRoot":"","sources":["../src/cilium-network-policy.ts"],"names":[],"mappings":"AAWA,4EAA4E;AAC5E,kEAAkE;AAClE,4EAA4E;AAC5E,0EAA0E;AAC1E,MAAM,UAAU,gCAAgC,CAAC,KAAoC;IACnF,MAAM,MAAM,GAA8B,EAAE,CAAC;IAE7C,IAAI,KAAK,CAAC,gBAAgB,KAAK,KAAK;QAAE,MAAM,CAAC,IAAI,CAAC;YAChD,WAAW,EAAE;gBACX,EAAE,WAAW,EAAE,EAAE,iCAAiC,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE;aAC7F;YACD,OAAO,EAAE;gBACP;oBACE,KAAK,EAAE;wBACL,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE;wBAC/B,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE;qBAChC;oBACD,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC,EAAE;iBACxC;aACF;SACF,CAAC,CAAC;IAEH,IAAI,KAAK,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC;YACV,OAAO,EAAE,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACpE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;SACzD,CAAC,CAAC;IACL,CAAC;IAED,IAAI,KAAK,CAAC,gBAAgB,KAAK,KAAK;QAAE,MAAM,CAAC,IAAI,CAAC;YAChD,WAAW,EAAE;gBACX;oBACE,WAAW,EAAE;wBACX,iCAAiC,EAAE,KAAK,CAAC,wBAAwB;wBACjE,GAAG,EAAE,kBAAkB;qBACxB;iBACF;aACF;YACD,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;SAC1D,CAAC,CAAC;IAEH,IAAI,KAAK,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC;YACV,SAAS,EAAE,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;SAC5D,CAAC,CAAC;IACL,CAAC;IAED,OAAO;QACL,UAAU,EAAE,cAAc;QAC1B,IAAI,EAAE,qBAAqB;QAC3B,QAAQ,EAAE;YACR,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,uBAAuB;YAC3C,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,MAAM,EAAE,EAAE,yBAAyB,EAAE,sBAAsB,EAAE;YAC7D,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC7E;QACD,IAAI,EAAE;YACJ,gBAAgB,EAAE,EAAE,WAAW,EAAE,KAAK,CAAC,gBAAgB,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,EAAE;YAC7F,MAAM;SACP;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,73 @@
1
+ import type { Readable, Writable } from "node:stream";
2
+ import type { PluginEnvironmentSyncResult, PluginSyncOperation } from "@tickernelz/paperclip-pro-plugin-sdk";
3
+ /**
4
+ * Injected streaming pod-exec primitive. `command` is always
5
+ * `["/bin/sh", "-c", <script>]`; `io.stdin` (when present) is streamed to the
6
+ * command's stdin over the exec data channel, and `io.stdout` (when present)
7
+ * receives the command's stdout. Returns the command's exit code plus its
8
+ * captured (bounded) stderr. `plugin.ts` binds this to
9
+ * `execInPodStreaming(kc, namespace, podName, "agent", …)`.
10
+ */
11
+ export type PodStreamExec = (command: string[], io: {
12
+ stdin?: Readable;
13
+ stdout?: Writable;
14
+ timeoutMs?: number;
15
+ maxStderrBytes?: number;
16
+ }) => Promise<{
17
+ exitCode: number;
18
+ stderr: string;
19
+ }>;
20
+ /**
21
+ * Host-side complete-mediation guard applied as defense-in-depth below the
22
+ * orchestrator's own confinement. Every sandbox-side path (the sync target for
23
+ * inbound, the sync source for outbound) MUST be an absolute path that
24
+ * canonicalizes lexically inside the workspace remote dir; absolute escapes and
25
+ * `..` traversal are rejected fail-closed before any bytes move. Sandbox paths
26
+ * are POSIX.
27
+ */
28
+ export declare function assertConfinedSandboxPath(remoteDir: string, candidate: string, label: string): void;
29
+ /**
30
+ * Parse one `tar -tvf` verbose listing line into its leading type flag and the
31
+ * trailing name-and-link-target field. The listing dialect depends on which tar
32
+ * the host ships: GNU/busybox emit
33
+ * `<perms> <owner>/<group> <size> <date> <time> <rest>`, while bsdtar
34
+ * (libarchive — the system tar on macOS) emits the ls-style
35
+ * `<perms> <links> <user> <group> <size> <Mon> <day> <time|year> <rest>`.
36
+ * The second field disambiguates: GNU always slash-joins owner/group, bsdtar
37
+ * puts a pure-digit link count there, so no line satisfies both shapes — the
38
+ * slash requirement is load-bearing, since a bsdtar line with numeric uid/gid
39
+ * would otherwise match the GNU shape shifted, hiding traversal in `<rest>`.
40
+ * Entries whose size column is not a plain byte count (e.g. a device node's
41
+ * `major,minor`) match neither shape. Returns null when nothing matches so
42
+ * callers can fail closed.
43
+ */
44
+ export declare function parseTarVerboseListingLine(line: string): {
45
+ typeFlag: string;
46
+ rest: string;
47
+ } | null;
48
+ /**
49
+ * Split a verbose-listing link field (`<name><delimiter><target>`) exactly
50
+ * once. The sandbox controls both halves, so a field with zero or multiple
51
+ * delimiter occurrences is unresolvable: a link name that itself contains the
52
+ * delimiter shifts the split point, and taking the first (or last) occurrence
53
+ * would let a crafted name or target hide an escaping link target from the
54
+ * confinement check. Returns null so callers fail closed.
55
+ */
56
+ export declare function splitLinkEntryOnce(field: string, delimiter: string): {
57
+ name: string;
58
+ target: string;
59
+ } | null;
60
+ export declare function performSyncIn(input: {
61
+ exec: PodStreamExec;
62
+ operations: PluginSyncOperation[];
63
+ remoteDir: string;
64
+ timeoutMs: number;
65
+ }): Promise<PluginEnvironmentSyncResult>;
66
+ export declare function performSyncOut(input: {
67
+ exec: PodStreamExec;
68
+ operations: PluginSyncOperation[];
69
+ remoteDir: string;
70
+ timeoutMs: number;
71
+ maxOutputBytes?: number;
72
+ }): Promise<PluginEnvironmentSyncResult>;
73
+ //# sourceMappingURL=file-sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-sync.d.ts","sourceRoot":"","sources":["../src/file-sync.ts"],"names":[],"mappings":"AAkDA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAItD,OAAO,KAAK,EACV,2BAA2B,EAG3B,mBAAmB,EACpB,MAAM,sCAAsC,CAAC;AAI9C;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,CAC1B,OAAO,EAAE,MAAM,EAAE,EACjB,EAAE,EAAE;IACF,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,KACE,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAoCnD;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAenG;AA2LD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAMlG;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAK5G;AA6TD,wBAAsB,aAAa,CAAC,KAAK,EAAE;IACzC,IAAI,EAAE,aAAa,CAAC;IACpB,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,2BAA2B,CAAC,CA0CvC;AAsJD,wBAAsB,cAAc,CAAC,KAAK,EAAE;IAC1C,IAAI,EAAE,aAAa,CAAC;IACpB,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAmCvC"}