@kalphq/cli 0.0.0-dev-20260512233019 → 0.0.0-dev-20260513001643

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.
@@ -1,7 +1,10 @@
1
1
  import { DurableObject } from "cloudflare:workers";
2
2
  import { Hono } from "hono";
3
3
  import { deleteCookie, getSignedCookie, setSignedCookie } from "hono/cookie";
4
+ import { createRemoteJWKSet, jwtVerify } from "jose";
4
5
  import agentsSnapshot from "./agents.snapshot.json";
6
+ import identityConfig from "./identity.config.json";
7
+ import mapIdentity from "./identity.map.mjs";
5
8
 
6
9
  const SESSION_COOKIE_NAME = "kalp_studio_session";
7
10
  const SESSION_MAX_AGE_SECONDS = 60 * 60 * 8;
@@ -10,6 +13,71 @@ const CORS_HEADERS = {
10
13
  "Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS",
11
14
  "Access-Control-Allow-Headers": "Content-Type, Authorization",
12
15
  };
16
+ const JWKS_RESOLVER_CACHE = new Map();
17
+
18
+ function extractBearerToken(authorization) {
19
+ if (!authorization) return null;
20
+ const value = authorization.trim();
21
+ if (!value) return null;
22
+ const match = value.match(/^Bearer\s+(.+)$/i);
23
+ if (!match || !match[1]) return null;
24
+ const token = match[1].trim();
25
+ return token || null;
26
+ }
27
+
28
+ function normalizeHeaderRecord(request) {
29
+ const headers = {};
30
+ for (const [key, value] of request.headers.entries()) {
31
+ headers[key.toLowerCase()] = value;
32
+ }
33
+ return headers;
34
+ }
35
+
36
+ function toMappedIdentity(payload, request) {
37
+ const rawHeaders = normalizeHeaderRecord(request);
38
+ try {
39
+ const mapped = mapIdentity(payload, rawHeaders);
40
+ if (
41
+ mapped &&
42
+ typeof mapped === "object" &&
43
+ typeof mapped.userId === "string" &&
44
+ mapped.userId.length > 0
45
+ ) {
46
+ return {
47
+ userId: mapped.userId,
48
+ email: mapped.email,
49
+ name: mapped.name,
50
+ claims:
51
+ mapped.claims && typeof mapped.claims === "object" ? mapped.claims : {},
52
+ };
53
+ }
54
+ } catch {}
55
+
56
+ const sub =
57
+ payload && typeof payload === "object" && typeof payload.sub === "string"
58
+ ? payload.sub
59
+ : "anonymous";
60
+ return { userId: sub, claims: {} };
61
+ }
62
+
63
+ function resolveSymmetricSecret(env, secretEnvKey) {
64
+ const key = secretEnvKey && secretEnvKey.trim() ? secretEnvKey.trim() : "JWT_SIGNING_SECRET";
65
+ const selected = env[key];
66
+ if (typeof selected === "string" && selected.trim()) return selected.trim();
67
+ if (key !== "JWT_SIGNING_SECRET") {
68
+ const fallback = env.JWT_SIGNING_SECRET;
69
+ if (typeof fallback === "string" && fallback.trim()) return fallback.trim();
70
+ }
71
+ return null;
72
+ }
73
+
74
+ function getJwksResolver(jwksUrl) {
75
+ const cached = JWKS_RESOLVER_CACHE.get(jwksUrl);
76
+ if (cached) return cached;
77
+ const resolver = createRemoteJWKSet(new URL(jwksUrl));
78
+ JWKS_RESOLVER_CACHE.set(jwksUrl, resolver);
79
+ return resolver;
80
+ }
13
81
 
14
82
  function withCors(response) {
15
83
  if (response.status === 101) return response;
@@ -92,6 +160,78 @@ async function requireSession(c, next) {
92
160
  return next();
93
161
  }
94
162
 
163
+ async function verifyGatewayAuth(c) {
164
+ const enforce = identityConfig?.enforceGlobalAuth !== false;
165
+ if (!enforce) {
166
+ return { userId: "anonymous", claims: {}, providerId: "none" };
167
+ }
168
+
169
+ const token = extractBearerToken(c.req.header("Authorization"));
170
+
171
+ const serviceKey = c.env.KALP_SERVICE_KEY?.trim();
172
+ if (serviceKey && token && token === serviceKey) {
173
+ return {
174
+ userId: "service-admin",
175
+ providerId: "service-key",
176
+ claims: { role: "service_admin", service: true },
177
+ };
178
+ }
179
+
180
+ const strategy = identityConfig?.strategy;
181
+ if (!strategy) return null;
182
+
183
+ try {
184
+ if (strategy.type === "jwks") {
185
+ if (!token) return null;
186
+ if (!strategy.jwksUrl) return null;
187
+ const resolver = getJwksResolver(strategy.jwksUrl);
188
+ const options = {};
189
+ if (strategy.issuer) options.issuer = strategy.issuer;
190
+ if (strategy.audience) options.audience = strategy.audience;
191
+ const { payload } = await jwtVerify(token, resolver, options);
192
+ return {
193
+ ...toMappedIdentity(payload, c.req.raw),
194
+ providerId: identityConfig.identityId ?? "identity",
195
+ };
196
+ }
197
+
198
+ if (strategy.type === "symmetric") {
199
+ if (!token) return null;
200
+ const secret = resolveSymmetricSecret(c.env, strategy.secretEnvKey);
201
+ if (!secret) return null;
202
+ const encoded = new TextEncoder().encode(secret);
203
+ const { payload } = await jwtVerify(token, encoded, {
204
+ algorithms: ["HS256"],
205
+ });
206
+ return {
207
+ ...toMappedIdentity(payload, c.req.raw),
208
+ providerId: identityConfig.identityId ?? "identity",
209
+ };
210
+ }
211
+
212
+ if (strategy.type === "apiKey") {
213
+ const headerName = (strategy.headerName || "x-api-key").toLowerCase();
214
+ const envKey = strategy.envKey || "KALP_API_KEY";
215
+ const expected =
216
+ typeof c.env[envKey] === "string" ? c.env[envKey].trim() : "";
217
+ if (!expected) return null;
218
+ const provided =
219
+ c.req.header(headerName)?.trim() ??
220
+ (headerName === "authorization" ? token : null);
221
+ if (!provided || provided !== expected) return null;
222
+ return {
223
+ userId: "api-key-client",
224
+ providerId: identityConfig.identityId ?? "identity",
225
+ claims: { role: "api_key" },
226
+ };
227
+ }
228
+ } catch {
229
+ return null;
230
+ }
231
+
232
+ return null;
233
+ }
234
+
95
235
  function mapIndexToStudioAgents(entries) {
96
236
  return entries.map((entry) => ({
97
237
  name: entry.name,
@@ -124,6 +264,16 @@ async function readLatestManifest(env, agentName) {
124
264
  }
125
265
  }
126
266
 
267
+ async function resolveAgentAccess(env, agentName, routeKey) {
268
+ const manifest = await readLatestManifest(env, agentName);
269
+ const metadata = manifest?.metadata || {};
270
+ const routePublic = metadata.routesPublic
271
+ ? metadata.routesPublic[routeKey]
272
+ : undefined;
273
+ const agentPublic = metadata.public ?? false;
274
+ return { isPublic: routePublic !== undefined ? routePublic : agentPublic };
275
+ }
276
+
127
277
  export class AgentDurableObject extends DurableObject {
128
278
  constructor(ctx, env) {
129
279
  super(ctx, env);
@@ -216,9 +366,19 @@ function getAgentRouting(url) {
216
366
  };
217
367
  }
218
368
 
219
- async function forwardToAgentDO(request, env) {
369
+ async function forwardToAgentDO(c, request, env) {
220
370
  const url = new URL(request.url);
221
371
  const { agentName, passthroughPath } = getAgentRouting(url);
372
+ const routeKey = `${request.method.toUpperCase()}:${passthroughPath === "/" ? "/" : passthroughPath}`;
373
+ const access = await resolveAgentAccess(env, agentName, routeKey);
374
+ let identity = null;
375
+ if (!access.isPublic) {
376
+ identity = await verifyGatewayAuth(c);
377
+ if (!identity) {
378
+ return c.json({ ok: false, error: "Unauthorized" }, 401);
379
+ }
380
+ }
381
+
222
382
  const id = env.KALP_RUNTIME_CLOUDFLARE.idFromName(agentName);
223
383
  const stub = env.KALP_RUNTIME_CLOUDFLARE.get(id);
224
384
 
@@ -226,6 +386,12 @@ async function forwardToAgentDO(request, env) {
226
386
  proxiedUrl.pathname = passthroughPath === "/" ? "/" : passthroughPath;
227
387
  const headers = new Headers(request.headers);
228
388
  headers.set("x-kalp-agent-name", agentName);
389
+ headers.set("x-kalp-route-public", String(access.isPublic));
390
+ if (identity && identity.userId) {
391
+ headers.set("x-kalp-auth-user-id", identity.userId);
392
+ headers.set("x-kalp-auth-provider-id", identity.providerId ?? "identity");
393
+ headers.set("x-kalp-auth-claims", JSON.stringify(identity.claims ?? {}));
394
+ }
229
395
 
230
396
  const proxyRequest = new Request(proxiedUrl.toString(), {
231
397
  method: request.method,
@@ -376,11 +542,11 @@ app.get("/api/internal/events/:executionId", (c) => {
376
542
  });
377
543
 
378
544
  app.all("/a/:agentName/*", async (c) => {
379
- return forwardToAgentDO(c.req.raw, c.env);
545
+ return forwardToAgentDO(c, c.req.raw, c.env);
380
546
  });
381
547
 
382
548
  app.all("/a/:agentName", async (c) => {
383
- return forwardToAgentDO(c.req.raw, c.env);
549
+ return forwardToAgentDO(c, c.req.raw, c.env);
384
550
  });
385
551
 
386
552
  app.get("/studio", (c) => c.redirect("/studio/", 308));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kalphq/cli",
3
- "version": "0.0.0-dev-20260512233019",
3
+ "version": "0.0.0-dev-20260513001643",
4
4
  "description": "Zero-config CLI for deploying Kalp agents",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -27,9 +27,9 @@
27
27
  "open": "^11.0.0",
28
28
  "picocolors": "1.1.1",
29
29
  "zod": "3.25.76",
30
- "@kalphq/compiler": "0.0.0-dev-20260512233019",
31
- "@kalphq/project": "0.0.0-dev-20260512233019",
32
- "@kalphq/sdk": "0.0.0-dev-20260512233019"
30
+ "@kalphq/compiler": "0.0.0-dev-20260513001643",
31
+ "@kalphq/sdk": "0.0.0-dev-20260513001643",
32
+ "@kalphq/project": "0.0.0-dev-20260513001643"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/json-stable-stringify": "1.2.0",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/deploy.ts","../src/utils/ai.ts"],"sourcesContent":["import { readFile, writeFile } from \"node:fs/promises\";\nimport { createHash } from \"node:crypto\";\nimport { execa } from \"execa\";\nimport { requireAuth } from \"@/utils/auth\";\nimport { ensureStudioSecrets } from \"@/utils/secret\";\nimport { readProjectState, writeProjectState } from \"@/utils/project-state\";\nimport { materializeRuntime } from \"@/utils/runtime\";\nimport {\n getRequiredSecretForProvider,\n readDotEnv,\n resolveProviderFromConfig,\n} from \"@/utils/ai\";\nimport { resolveProvider } from \"@/utils/providers\";\n\nfunction findWorkersUrl(output: string): string | null {\n const match = output.match(/https:\\/\\/[^\\s]+\\.workers\\.dev/);\n return match?.[0] ?? null;\n}\n\ninterface RuntimeWranglerConfig {\n name?: string;\n kv_namespaces?: Array<{ binding: string; id?: string }>;\n}\n\ninterface KvNamespaceInfo {\n id: string;\n title: string;\n}\n\nasync function readWranglerConfig(\n configPath: string,\n): Promise<RuntimeWranglerConfig> {\n const text = await readFile(configPath, \"utf-8\");\n return JSON.parse(text) as RuntimeWranglerConfig;\n}\n\nasync function writeWranglerConfig(\n configPath: string,\n config: RuntimeWranglerConfig,\n): Promise<void> {\n await writeFile(configPath, `${JSON.stringify(config, null, 2)}\\n`, \"utf-8\");\n}\n\nfunction deriveKvNamespaceTitle(workerName: string, binding: string): string {\n return `${workerName}-${binding.toLowerCase().replace(/_/g, \"-\")}`;\n}\n\nfunction parseKvListOutput(stdout: string): KvNamespaceInfo[] {\n const trimmed = stdout.trim();\n if (!trimmed) return [];\n\n try {\n const parsed = JSON.parse(trimmed);\n if (Array.isArray(parsed)) {\n return parsed\n .map((item) => ({\n id: String((item as { id?: string }).id ?? \"\"),\n title: String((item as { title?: string }).title ?? \"\"),\n }))\n .filter((item) => !!item.id && !!item.title);\n }\n } catch {\n // fallback to text parsing below\n }\n\n const matches = trimmed.match(/[a-f0-9]{32}\\s+[^\\r\\n]+/gi) ?? [];\n return matches\n .map((line) => {\n const [id, ...rest] = line.trim().split(/\\s+/g);\n return { id: id ?? \"\", title: rest.join(\" \") };\n })\n .filter((item) => !!item.id && !!item.title);\n}\n\nasync function listKvNamespaces(\n cwd: string,\n configPath: string,\n): Promise<KvNamespaceInfo[]> {\n const provider = resolveProvider();\n const namespaces = await provider.listNamespaces({ cwd, configPath });\n return namespaces.map((item) => ({\n id: item.id,\n title: item.title,\n }));\n}\n\nasync function ensureKvNamespaceBindingId(\n cwd: string,\n configPath: string,\n): Promise<string | null> {\n const config = await readWranglerConfig(configPath);\n const binding = config.kv_namespaces?.find(\n (item) => item.binding === \"KALP_MANIFESTS\",\n );\n\n if (!binding || !config.name) return null;\n if (binding.id) return binding.id;\n\n const expectedTitle = deriveKvNamespaceTitle(config.name, binding.binding);\n const namespaces = await listKvNamespaces(cwd, configPath);\n const existing = namespaces.find((item) => item.title === expectedTitle);\n if (!existing) return null;\n\n binding.id = existing.id;\n await writeWranglerConfig(configPath, config);\n return existing.id;\n}\n\nfunction isNamespaceAlreadyExistsError(output: string): boolean {\n return output.includes(\"[code: 10014]\") && output.includes(\"already exists\");\n}\n\nasync function resolveWorkerUrl(\n configPath: string,\n deployOutput: string,\n): Promise<string> {\n const fromOutput = findWorkersUrl(deployOutput);\n if (fromOutput) return fromOutput;\n\n const configText = await readFile(configPath, \"utf-8\").catch(\n () => null as string | null,\n );\n const workerName = configText?.match(/\"name\"\\s*:\\s*\"([^\"]+)\"/)?.[1];\n\n if (workerName) {\n return `https://${workerName}.workers.dev`;\n }\n\n throw new Error(\"Could not resolve worker URL from wrangler deploy output.\");\n}\n\nexport async function runInitialDeploy(cwd: string): Promise<{\n workerUrl: string;\n customDomains: string[];\n accountId: string;\n studioAdminUser: string;\n studioPassword: string;\n credentialsChanged: boolean;\n}> {\n const auth = await requireAuth();\n const aiProvider = await resolveProviderFromConfig(cwd);\n const requiredProviderSecret = getRequiredSecretForProvider(aiProvider);\n const envMap = await readDotEnv(cwd);\n const providerSecretValue = envMap[requiredProviderSecret]?.trim();\n if (!providerSecretValue) {\n throw new Error(\n `Missing required secret ${requiredProviderSecret} for provider \"${aiProvider}\". Add it to .env before deploy.`,\n );\n }\n\n const secrets = await ensureStudioSecrets(cwd);\n const runtimeProvider = resolveProvider();\n const runtime = await materializeRuntime(cwd);\n let secretSyncFailed = false;\n const secretEntries = [\n [\"KALP_SECRET_KEY\", secrets.key],\n [\"KALP_STUDIO_PASSWORD\", secrets.studioPassword],\n [\"KALP_STUDIO_ADMIN_USER\", secrets.studioAdminUser],\n [requiredProviderSecret, providerSecretValue],\n ] as const;\n\n for (const [name, value] of secretEntries) {\n try {\n await runtimeProvider.putSecret({\n cwd,\n configPath: runtime.wranglerConfigPath,\n name,\n value,\n });\n } catch {\n secretSyncFailed = true;\n break;\n }\n }\n\n await ensureKvNamespaceBindingId(cwd, runtime.wranglerConfigPath).catch(\n () => null,\n );\n\n const deployArgs = secretSyncFailed\n ? [\n \"wrangler\",\n \"deploy\",\n \"--config\",\n runtime.wranglerConfigPath,\n \"--secrets-file\",\n \".env\",\n ]\n : [\"wrangler\", \"deploy\", \"--config\", runtime.wranglerConfigPath];\n\n let deploy = await runtimeProvider\n .deployRuntime({\n cwd,\n configPath: runtime.wranglerConfigPath,\n useSecretsFile: secretSyncFailed,\n })\n .catch((error) => error);\n if (deploy instanceof Error) {\n const combined = deploy.message;\n if (isNamespaceAlreadyExistsError(combined)) {\n await ensureKvNamespaceBindingId(cwd, runtime.wranglerConfigPath);\n deploy = await runtimeProvider.deployRuntime({\n cwd,\n configPath: runtime.wranglerConfigPath,\n useSecretsFile: secretSyncFailed,\n });\n } else {\n throw deploy;\n }\n }\n\n const workerUrl = deploy.workerUrl;\n const customDomains = deploy.customDomains ?? [];\n\n const existingState = await readProjectState(cwd);\n\n const credentialsFingerprint = createHash(\"sha256\")\n .update(`${secrets.studioAdminUser}:${secrets.studioPassword}`)\n .digest(\"hex\");\n const credentialsChanged =\n existingState?.studioCredentialsFingerprint !== credentialsFingerprint;\n\n await writeProjectState(cwd, {\n workerUrl,\n deployedAt: new Date().toISOString(),\n accountId: auth.accountId,\n studioCredentialsFingerprint: credentialsFingerprint,\n agents: existingState?.agents ?? {},\n });\n\n return {\n workerUrl,\n customDomains,\n accountId: auth.accountId,\n studioAdminUser: secrets.studioAdminUser,\n studioPassword: secrets.studioPassword,\n credentialsChanged,\n };\n}\n","import { access, readFile } from \"node:fs/promises\";\nimport { constants } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { createJiti } from \"jiti\";\n\nexport type AIProvider = \"openai\" | \"anthropic\" | \"openrouter\" | \"custom\";\n\nconst PROVIDER_SECRET_MAP: Record<AIProvider, string> = {\n openai: \"OPENAI_API_KEY\",\n anthropic: \"ANTHROPIC_API_KEY\",\n openrouter: \"OPENROUTER_API_KEY\",\n custom: \"CUSTOM_AI_API_KEY\",\n};\n\nfunction parseEnv(content: string): Record<string, string> {\n const env: Record<string, string> = {};\n for (const raw of content.split(/\\r?\\n/g)) {\n const line = raw.trim();\n if (!line || line.startsWith(\"#\")) continue;\n const idx = line.indexOf(\"=\");\n if (idx <= 0) continue;\n env[line.slice(0, idx).trim()] = line.slice(idx + 1);\n }\n return env;\n}\n\nexport async function resolveProviderFromConfig(cwd: string): Promise<AIProvider> {\n const configPath = join(cwd, \"kalp.config.ts\");\n await access(configPath, constants.F_OK);\n const jiti = createJiti(cwd, { interopDefault: true });\n const config = (await jiti.import(configPath)) as\n | { default?: { ai?: { provider?: AIProvider } }; ai?: { provider?: AIProvider } }\n | undefined;\n const provider = config?.default?.ai?.provider ?? config?.ai?.provider ?? \"openai\";\n return provider;\n}\n\nexport async function readDotEnv(cwd: string): Promise<Record<string, string>> {\n const envPath = join(cwd, \".env\");\n const content = await readFile(envPath, \"utf-8\").catch(() => \"\");\n return parseEnv(content);\n}\n\nexport function getRequiredSecretForProvider(provider: AIProvider): string {\n return PROVIDER_SECRET_MAP[provider];\n}\n"],"mappings":";;;;;;;;;;;;;;;AAAA,SAAS,YAAAA,WAAU,iBAAiB;AACpC,SAAS,kBAAkB;;;ACD3B,SAAS,QAAQ,gBAAgB;AACjC,SAAS,iBAAiB;AAC1B,SAAS,YAAY;AACrB,SAAS,kBAAkB;AAI3B,IAAM,sBAAkD;AAAA,EACtD,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,QAAQ;AACV;AAEA,SAAS,SAAS,SAAyC;AACzD,QAAM,MAA8B,CAAC;AACrC,aAAW,OAAO,QAAQ,MAAM,QAAQ,GAAG;AACzC,UAAM,OAAO,IAAI,KAAK;AACtB,QAAI,CAAC,QAAQ,KAAK,WAAW,GAAG,EAAG;AACnC,UAAM,MAAM,KAAK,QAAQ,GAAG;AAC5B,QAAI,OAAO,EAAG;AACd,QAAI,KAAK,MAAM,GAAG,GAAG,EAAE,KAAK,CAAC,IAAI,KAAK,MAAM,MAAM,CAAC;AAAA,EACrD;AACA,SAAO;AACT;AAEA,eAAsB,0BAA0B,KAAkC;AAChF,QAAM,aAAa,KAAK,KAAK,gBAAgB;AAC7C,QAAM,OAAO,YAAY,UAAU,IAAI;AACvC,QAAM,OAAO,WAAW,KAAK,EAAE,gBAAgB,KAAK,CAAC;AACrD,QAAM,SAAU,MAAM,KAAK,OAAO,UAAU;AAG5C,QAAM,WAAW,QAAQ,SAAS,IAAI,YAAY,QAAQ,IAAI,YAAY;AAC1E,SAAO;AACT;AAEA,eAAsB,WAAW,KAA8C;AAC7E,QAAM,UAAU,KAAK,KAAK,MAAM;AAChC,QAAM,UAAU,MAAM,SAAS,SAAS,OAAO,EAAE,MAAM,MAAM,EAAE;AAC/D,SAAO,SAAS,OAAO;AACzB;AAEO,SAAS,6BAA6B,UAA8B;AACzE,SAAO,oBAAoB,QAAQ;AACrC;;;ADhBA,eAAe,mBACb,YACgC;AAChC,QAAM,OAAO,MAAMC,UAAS,YAAY,OAAO;AAC/C,SAAO,KAAK,MAAM,IAAI;AACxB;AAEA,eAAe,oBACb,YACA,QACe;AACf,QAAM,UAAU,YAAY,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,GAAM,OAAO;AAC7E;AAEA,SAAS,uBAAuB,YAAoB,SAAyB;AAC3E,SAAO,GAAG,UAAU,IAAI,QAAQ,YAAY,EAAE,QAAQ,MAAM,GAAG,CAAC;AAClE;AA6BA,eAAe,iBACb,KACA,YAC4B;AAC5B,QAAM,WAAW,gBAAgB;AACjC,QAAM,aAAa,MAAM,SAAS,eAAe,EAAE,KAAK,WAAW,CAAC;AACpE,SAAO,WAAW,IAAI,CAAC,UAAU;AAAA,IAC/B,IAAI,KAAK;AAAA,IACT,OAAO,KAAK;AAAA,EACd,EAAE;AACJ;AAEA,eAAe,2BACb,KACA,YACwB;AACxB,QAAM,SAAS,MAAM,mBAAmB,UAAU;AAClD,QAAM,UAAU,OAAO,eAAe;AAAA,IACpC,CAAC,SAAS,KAAK,YAAY;AAAA,EAC7B;AAEA,MAAI,CAAC,WAAW,CAAC,OAAO,KAAM,QAAO;AACrC,MAAI,QAAQ,GAAI,QAAO,QAAQ;AAE/B,QAAM,gBAAgB,uBAAuB,OAAO,MAAM,QAAQ,OAAO;AACzE,QAAM,aAAa,MAAM,iBAAiB,KAAK,UAAU;AACzD,QAAM,WAAW,WAAW,KAAK,CAAC,SAAS,KAAK,UAAU,aAAa;AACvE,MAAI,CAAC,SAAU,QAAO;AAEtB,UAAQ,KAAK,SAAS;AACtB,QAAM,oBAAoB,YAAY,MAAM;AAC5C,SAAO,SAAS;AAClB;AAEA,SAAS,8BAA8B,QAAyB;AAC9D,SAAO,OAAO,SAAS,eAAe,KAAK,OAAO,SAAS,gBAAgB;AAC7E;AAqBA,eAAsB,iBAAiB,KAOpC;AACD,QAAM,OAAO,MAAM,YAAY;AAC/B,QAAM,aAAa,MAAM,0BAA0B,GAAG;AACtD,QAAM,yBAAyB,6BAA6B,UAAU;AACtE,QAAM,SAAS,MAAM,WAAW,GAAG;AACnC,QAAM,sBAAsB,OAAO,sBAAsB,GAAG,KAAK;AACjE,MAAI,CAAC,qBAAqB;AACxB,UAAM,IAAI;AAAA,MACR,2BAA2B,sBAAsB,kBAAkB,UAAU;AAAA,IAC/E;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,oBAAoB,GAAG;AAC7C,QAAM,kBAAkB,gBAAgB;AACxC,QAAM,UAAU,MAAM,mBAAmB,GAAG;AAC5C,MAAI,mBAAmB;AACvB,QAAM,gBAAgB;AAAA,IACpB,CAAC,mBAAmB,QAAQ,GAAG;AAAA,IAC/B,CAAC,wBAAwB,QAAQ,cAAc;AAAA,IAC/C,CAAC,0BAA0B,QAAQ,eAAe;AAAA,IAClD,CAAC,wBAAwB,mBAAmB;AAAA,EAC9C;AAEA,aAAW,CAAC,MAAM,KAAK,KAAK,eAAe;AACzC,QAAI;AACF,YAAM,gBAAgB,UAAU;AAAA,QAC9B;AAAA,QACA,YAAY,QAAQ;AAAA,QACpB;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,QAAQ;AACN,yBAAmB;AACnB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,2BAA2B,KAAK,QAAQ,kBAAkB,EAAE;AAAA,IAChE,MAAM;AAAA,EACR;AAEA,QAAM,aAAa,mBACf;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,EACF,IACA,CAAC,YAAY,UAAU,YAAY,QAAQ,kBAAkB;AAEjE,MAAI,SAAS,MAAM,gBAChB,cAAc;AAAA,IACb;AAAA,IACA,YAAY,QAAQ;AAAA,IACpB,gBAAgB;AAAA,EAClB,CAAC,EACA,MAAM,CAAC,UAAU,KAAK;AACzB,MAAI,kBAAkB,OAAO;AAC3B,UAAM,WAAW,OAAO;AACxB,QAAI,8BAA8B,QAAQ,GAAG;AAC3C,YAAM,2BAA2B,KAAK,QAAQ,kBAAkB;AAChE,eAAS,MAAM,gBAAgB,cAAc;AAAA,QAC3C;AAAA,QACA,YAAY,QAAQ;AAAA,QACpB,gBAAgB;AAAA,MAClB,CAAC;AAAA,IACH,OAAO;AACL,YAAM;AAAA,IACR;AAAA,EACF;AAEA,QAAM,YAAY,OAAO;AACzB,QAAM,gBAAgB,OAAO,iBAAiB,CAAC;AAE/C,QAAM,gBAAgB,MAAM,iBAAiB,GAAG;AAEhD,QAAM,yBAAyB,WAAW,QAAQ,EAC/C,OAAO,GAAG,QAAQ,eAAe,IAAI,QAAQ,cAAc,EAAE,EAC7D,OAAO,KAAK;AACf,QAAM,qBACJ,eAAe,iCAAiC;AAElD,QAAM,kBAAkB,KAAK;AAAA,IAC3B;AAAA,IACA,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,IACnC,WAAW,KAAK;AAAA,IAChB,8BAA8B;AAAA,IAC9B,QAAQ,eAAe,UAAU,CAAC;AAAA,EACpC,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,WAAW,KAAK;AAAA,IAChB,iBAAiB,QAAQ;AAAA,IACzB,gBAAgB,QAAQ;AAAA,IACxB;AAAA,EACF;AACF;","names":["readFile","readFile"]}