pi-feats 0.1.1

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 (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +508 -0
  3. package/extensions/README.md +27 -0
  4. package/extensions/api-server/PLAN.md +70 -0
  5. package/extensions/api-server/README.md +103 -0
  6. package/extensions/api-server/application-log-store.ts +21 -0
  7. package/extensions/api-server/application-runtime.ts +212 -0
  8. package/extensions/api-server/application-store.ts +30 -0
  9. package/extensions/api-server/index.ts +52 -0
  10. package/extensions/api-server/profile-store.ts +367 -0
  11. package/extensions/api-server/server.ts +863 -0
  12. package/extensions/cli-resources.ts +564 -0
  13. package/extensions/guardrails/index.ts +178 -0
  14. package/extensions/lib/application-handler-templates.ts +63 -0
  15. package/extensions/lib/profile-env.ts +61 -0
  16. package/extensions/lib/profile-sandbox.ts +197 -0
  17. package/extensions/lib/remote-hosts.ts +392 -0
  18. package/extensions/pi-console-webui/app/[section]/page.tsx +4 -0
  19. package/extensions/pi-console-webui/app/api/admin/config/[target]/route.ts +5 -0
  20. package/extensions/pi-console-webui/app/api/admin/services/[service]/restart/route.ts +5 -0
  21. package/extensions/pi-console-webui/app/api/auth/login/route.ts +9 -0
  22. package/extensions/pi-console-webui/app/api/auth/logout/route.ts +3 -0
  23. package/extensions/pi-console-webui/app/api/message/app/[slug]/route.ts +11 -0
  24. package/extensions/pi-console-webui/app/api/pi/[...path]/route.ts +31 -0
  25. package/extensions/pi-console-webui/app/applications/[slug]/page.tsx +2 -0
  26. package/extensions/pi-console-webui/app/globals.css +41 -0
  27. package/extensions/pi-console-webui/app/icon.svg +1 -0
  28. package/extensions/pi-console-webui/app/layout.tsx +5 -0
  29. package/extensions/pi-console-webui/app/login/page.tsx +11 -0
  30. package/extensions/pi-console-webui/app/page.tsx +2 -0
  31. package/extensions/pi-console-webui/app/terminal/page.tsx +4 -0
  32. package/extensions/pi-console-webui/components/admin-config-form.tsx +16 -0
  33. package/extensions/pi-console-webui/components/application-handler-editor.tsx +39 -0
  34. package/extensions/pi-console-webui/components/application-logs.tsx +38 -0
  35. package/extensions/pi-console-webui/components/application-mappings.tsx +28 -0
  36. package/extensions/pi-console-webui/components/application-sessions.tsx +11 -0
  37. package/extensions/pi-console-webui/components/application-settings.tsx +60 -0
  38. package/extensions/pi-console-webui/components/application-workspace.tsx +14 -0
  39. package/extensions/pi-console-webui/components/applications.tsx +15 -0
  40. package/extensions/pi-console-webui/components/chat-workspace.tsx +42 -0
  41. package/extensions/pi-console-webui/components/console-page.tsx +23 -0
  42. package/extensions/pi-console-webui/components/console-state.tsx +30 -0
  43. package/extensions/pi-console-webui/components/console.tsx +115 -0
  44. package/extensions/pi-console-webui/components/guardrails-panel.tsx +78 -0
  45. package/extensions/pi-console-webui/components/package-resources.tsx +13 -0
  46. package/extensions/pi-console-webui/components/pulse-resources.tsx +41 -0
  47. package/extensions/pi-console-webui/components/skill-resources.tsx +35 -0
  48. package/extensions/pi-console-webui/components/skill-source-document-preview.tsx +7 -0
  49. package/extensions/pi-console-webui/components/skill-source-import.tsx +7 -0
  50. package/extensions/pi-console-webui/components/skill-sources.tsx +12 -0
  51. package/extensions/pi-console-webui/components/terminal-client.tsx +39 -0
  52. package/extensions/pi-console-webui/components/toast.tsx +18 -0
  53. package/extensions/pi-console-webui/components/ui/button.tsx +4 -0
  54. package/extensions/pi-console-webui/components/ui/card.tsx +4 -0
  55. package/extensions/pi-console-webui/components/ui/input.tsx +4 -0
  56. package/extensions/pi-console-webui/components/ui/switch.tsx +6 -0
  57. package/extensions/pi-console-webui/components/ui/tabs.tsx +11 -0
  58. package/extensions/pi-console-webui/components.json +8 -0
  59. package/extensions/pi-console-webui/index.ts +33 -0
  60. package/extensions/pi-console-webui/lib/admin-config.ts +22 -0
  61. package/extensions/pi-console-webui/lib/auth.ts +21 -0
  62. package/extensions/pi-console-webui/lib/config.ts +15 -0
  63. package/extensions/pi-console-webui/lib/pi-api.ts +9 -0
  64. package/extensions/pi-console-webui/lib/utils.ts +3 -0
  65. package/extensions/pi-console-webui/next-env.d.ts +6 -0
  66. package/extensions/pi-console-webui/next.config.js +5 -0
  67. package/extensions/pi-console-webui/postcss.config.js +1 -0
  68. package/extensions/pi-console-webui/tailwind.config.ts +2 -0
  69. package/extensions/pi-console-webui/tsconfig.json +41 -0
  70. package/extensions/profiles.ts +439 -0
  71. package/extensions/pulse/index.ts +62 -0
  72. package/extensions/pulse/store.ts +105 -0
  73. package/extensions/sequential-workflow.ts +270 -0
  74. package/extensions/skill-sources/index.ts +4 -0
  75. package/extensions/skill-sources/store.ts +118 -0
  76. package/package.json +89 -0
  77. package/scripts/install-nono.sh +34 -0
@@ -0,0 +1,18 @@
1
+ "use client";
2
+ import { createContext, useContext, useState } from "react";
3
+ import { CheckCircle2, CircleAlert, X } from "lucide-react";
4
+
5
+ type ToastType = "success" | "error" | "info";
6
+ type ToastItem = { id: number; message: string; type: ToastType };
7
+ const ToastContext = createContext<{ toast: (message: string, type?: ToastType) => void }>({ toast: () => {} });
8
+
9
+ export function ToastProvider({ children }: { children: React.ReactNode }) {
10
+ const [items, setItems] = useState<ToastItem[]>([]);
11
+ const toast = (message: string, type: ToastType = "success") => {
12
+ const id = Date.now() + Math.random();
13
+ setItems((current) => [...current, { id, message, type }]);
14
+ window.setTimeout(() => setItems((current) => current.filter((item) => item.id !== id)), 4500);
15
+ };
16
+ return <ToastContext.Provider value={{ toast }}>{children}<div className="fixed right-5 top-5 z-[100] flex w-full max-w-sm flex-col gap-2" aria-live="polite">{items.map((item) => <div key={item.id} className={`flex items-center gap-3 rounded-xl border px-4 py-3 text-sm shadow-lg ${item.type === "error" ? "border-red-200 bg-red-50 text-red-800" : item.type === "info" ? "border-blue-200 bg-blue-50 text-blue-800" : "border-emerald-200 bg-emerald-50 text-emerald-800"}`}><span>{item.type === "error" ? <CircleAlert size={18}/> : <CheckCircle2 size={18}/>}</span><p className="flex-1">{item.message}</p><button className="rounded p-1 hover:bg-black/5" title="Dismiss notification" onClick={() => setItems((current) => current.filter((entry) => entry.id !== item.id))}><X size={15}/></button></div>)}</div></ToastContext.Provider>;
17
+ }
18
+ export const useToast = () => useContext(ToastContext);
@@ -0,0 +1,4 @@
1
+ import { type ButtonHTMLAttributes, forwardRef } from "react";
2
+ import { cn } from "@/lib/utils";
3
+ export const Button = forwardRef<HTMLButtonElement, ButtonHTMLAttributes<HTMLButtonElement>>(({ className, ...props }, ref) => <button ref={ref} className={cn("rounded-md bg-[#406889] px-3 py-2 text-sm font-medium text-white hover:bg-[#365a78] disabled:opacity-50", className)} {...props} />);
4
+ Button.displayName = "Button";
@@ -0,0 +1,4 @@
1
+ import { type HTMLAttributes } from "react";
2
+ import { cn } from "@/lib/utils";
3
+ export function Card({ className, ...props }: HTMLAttributes<HTMLDivElement>) { return <section className={cn("rounded-xl border border-zinc-200 bg-white p-5 shadow-sm", className)} {...props} />; }
4
+ export function CardTitle({ className, ...props }: HTMLAttributes<HTMLHeadingElement>) { return <h2 className={cn("text-lg font-semibold text-zinc-900", className)} {...props} />; }
@@ -0,0 +1,4 @@
1
+ import { type InputHTMLAttributes, forwardRef } from "react";
2
+ import { cn } from "@/lib/utils";
3
+ export const Input = forwardRef<HTMLInputElement, InputHTMLAttributes<HTMLInputElement>>(({ className, ...props }, ref) => <input ref={ref} className={cn("w-full rounded-md border border-zinc-700 bg-zinc-900 px-3 py-2 text-sm outline-none focus:border-blue-500", className)} {...props} />);
4
+ Input.displayName = "Input";
@@ -0,0 +1,6 @@
1
+ "use client";
2
+ import * as SwitchPrimitive from "@radix-ui/react-switch";
3
+ import { type ComponentPropsWithoutRef, forwardRef } from "react";
4
+ import { cn } from "@/lib/utils";
5
+ export const Switch = forwardRef<HTMLButtonElement, ComponentPropsWithoutRef<typeof SwitchPrimitive.Root>>(({ className, ...props }, ref) => <SwitchPrimitive.Root ref={ref} className={cn("h-6 w-11 rounded-full bg-zinc-300 data-[state=checked]:bg-[#406889]", className)} {...props}><SwitchPrimitive.Thumb className="block size-5 translate-x-0.5 rounded-full bg-white shadow transition-transform data-[state=checked]:translate-x-5" /></SwitchPrimitive.Root>);
6
+ Switch.displayName = "Switch";
@@ -0,0 +1,11 @@
1
+ "use client";
2
+ import * as TabsPrimitive from "@radix-ui/react-tabs";
3
+ import { type ComponentPropsWithoutRef, forwardRef } from "react";
4
+ import { cn } from "@/lib/utils";
5
+ export const Tabs = TabsPrimitive.Root;
6
+ export const TabsList = forwardRef<HTMLDivElement, ComponentPropsWithoutRef<typeof TabsPrimitive.List>>(({ className, ...props }, ref) => <TabsPrimitive.List ref={ref} className={cn("flex flex-wrap gap-1 rounded-lg bg-zinc-100 p-1", className)} {...props} />);
7
+ TabsList.displayName = "TabsList";
8
+ export const TabsTrigger = forwardRef<HTMLButtonElement, ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>>(({ className, ...props }, ref) => <TabsPrimitive.Trigger ref={ref} className={cn("rounded-md px-3 py-2 text-sm font-medium text-zinc-600 data-[state=active]:bg-white data-[state=active]:text-zinc-950 data-[state=active]:shadow-sm", className)} {...props} />);
9
+ TabsTrigger.displayName = "TabsTrigger";
10
+ export const TabsContent = forwardRef<HTMLDivElement, ComponentPropsWithoutRef<typeof TabsPrimitive.Content>>(({ className, ...props }, ref) => <TabsPrimitive.Content ref={ref} className={cn("mt-5 outline-none", className)} {...props} />);
11
+ TabsContent.displayName = "TabsContent";
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "new-york",
4
+ "rsc": true,
5
+ "tsx": true,
6
+ "tailwind": { "config": "tailwind.config.ts", "css": "app/globals.css", "baseColor": "zinc", "cssVariables": false },
7
+ "aliases": { "components": "@/components", "utils": "@/lib/utils", "ui": "@/components/ui", "lib": "@/lib" }
8
+ }
@@ -0,0 +1,33 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { randomBytes } from "node:crypto";
3
+ import { existsSync, openSync } from "node:fs";
4
+ import { mkdir, readFile, unlink, writeFile } from "node:fs/promises";
5
+ import { homedir } from "node:os";
6
+ import { join } from "node:path";
7
+ import { spawn } from "node:child_process";
8
+
9
+ type ConsoleSettings = { host?: string; port?: number; username?: string; password?: string; secret?: string };
10
+ type LegacySettings = Record<string, unknown> & { console?: ConsoleSettings };
11
+ type State = { pid: number; host: string; port: number; startedAt: string };
12
+ const root = __dirname;
13
+ // Package dependencies are installed at the package root. Keep the nested
14
+ // location as a fallback for direct development use.
15
+ const dependencyRoot = () => existsSync(join(root, "node_modules", "next")) ? root : join(root, "..", "..");
16
+ const nextBinary = () => join(dependencyRoot(), "node_modules", "next", "dist", "bin", "next");
17
+ const agentDir = () => process.env.PI_CODING_AGENT_DIR ?? join(homedir(), ".pi", "agent");
18
+ const configPath = () => join(agentDir(), "pi-console.webui.json");
19
+ const legacySettingsPath = () => join(agentDir(), "settings.json");
20
+ const statePath = () => join(agentDir(), "pi-console-webui.state.json");
21
+ const logPath = () => join(agentDir(), "pi-console-webui.log");
22
+ async function readJson<T>(path: string): Promise<T | undefined> { try { return JSON.parse(await readFile(path, "utf8")) as T; } catch (error) { if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined; throw error; } }
23
+ async function writeJson(path: string, value: unknown) { await mkdir(agentDir(), { recursive: true }); await writeFile(path, `${JSON.stringify(value, null, 2)}\n`); }
24
+ async function settings(): Promise<ConsoleSettings> { const current = await readJson<ConsoleSettings>(configPath()); if (current) return current; const legacy = await readJson<LegacySettings>(legacySettingsPath()); const console = legacy?.console ?? {}; await writeJson(configPath(), console); if (legacy?.console) { const { console: _console, ...remaining } = legacy; await writeJson(legacySettingsPath(), remaining); } return console; }
25
+ async function config(): Promise<Required<ConsoleSettings>> { const current = await settings(); const host = typeof current.host === "string" && current.host ? current.host : "127.0.0.1"; const port = Number.isInteger(current.port) && current.port! > 0 && current.port! <= 65535 ? current.port : 3030; const username = typeof current.username === "string" && current.username ? current.username : "admin"; const password = typeof current.password === "string" && current.password ? current.password : "change-me"; const secret = typeof current.secret === "string" && current.secret ? current.secret : randomBytes(32).toString("base64url"); const resolved = { ...current, host, port, username, password, secret }; await writeJson(configPath(), resolved); return resolved; }
26
+ async function state(): Promise<State | undefined> { return readJson<State>(statePath()); }
27
+ function alive(pid: number): boolean { try { process.kill(pid, 0); return true; } catch { return false; } }
28
+ const buildMarker = () => join(root, ".next", "BUILD_ID");
29
+ async function build(next: string) { const log = openSync(logPath(), "a"); const child = spawn(process.execPath, [next, "build"], { cwd: root, stdio: ["ignore", log, log] }); const code = await new Promise<number>((resolveBuild) => child.once("exit", (value) => resolveBuild(value ?? 1))); if (code !== 0) throw new Error(`Next build failed; see ${logPath()}`); }
30
+ async function start(rebuild = false) { const cfg = await config(), current = await state(), next = nextBinary(); if (current && alive(current.pid)) return console.log(`Pi Console is already running at http://${current.host}:${current.port}`); if (!existsSync(next)) throw new Error(`Dependencies are missing. Reinstall the Pi package or run npm install in ${dependencyRoot()}.`); if (rebuild || !existsSync(buildMarker())) await build(next); const log = openSync(logPath(), "a"); const child = spawn(process.execPath, [next, "start", "--hostname", cfg.host, "--port", String(cfg.port)], { cwd: root, detached: true, stdio: ["ignore", log, log], env: process.env }); child.unref(); await writeJson(statePath(), { pid: child.pid!, host: cfg.host, port: cfg.port, startedAt: new Date().toISOString() }); console.log(`Pi Console started at http://${cfg.host}:${cfg.port}`); console.log(`Console credentials are configured in ${configPath()}.`); }
31
+ async function stop() { const current = await state(); if (!current || !alive(current.pid)) { await unlink(statePath()).catch(() => {}); return console.log("Pi Console is not running."); } try { process.kill(-current.pid, "SIGTERM"); } catch { process.kill(current.pid, "SIGTERM"); } await unlink(statePath()).catch(() => {}); console.log("Pi Console stopped."); }
32
+ async function status() { const current = await state(); console.log(current && alive(current.pid) ? `Pi Console: running at http://${current.host}:${current.port}` : "Pi Console: stopped"); }
33
+ export default async function (_pi: ExtensionAPI) { const [command, action] = process.argv.slice(2); if (command !== "console") return; if (action === "start") await start(); else if (action === "stop") await stop(); else if (action === "restart") { await stop(); await start(); } else if (action === "rebuild") { await stop(); await start(true); } else if (action === "status") await status(); else { console.error("Usage: pi console start | stop | restart | rebuild | status"); process.exitCode = 1; } process.exit(); }
@@ -0,0 +1,22 @@
1
+ import { readFile, rename, writeFile } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+
5
+ export type AdminTarget = "api-server" | "pi-console-webui";
6
+ // The API token must never reach the browser. Console credentials are intentionally editable in the administrative UI.
7
+ const secrets: Record<AdminTarget, string[]> = { "api-server": ["apiToken"], "pi-console-webui": [] };
8
+ const file = (target: AdminTarget) => join(process.env.PI_CODING_AGENT_DIR ?? join(homedir(), ".pi", "agent"), target === "api-server" ? "api-server.json" : "pi-console.webui.json");
9
+ const valid = (target: string): target is AdminTarget => target === "api-server" || target === "pi-console-webui";
10
+ export const isAdminTarget = valid;
11
+ async function raw(target: AdminTarget): Promise<Record<string, unknown>> { return JSON.parse(await readFile(file(target), "utf8")) as Record<string, unknown>; }
12
+ export async function readAdminConfig(target: AdminTarget) { const config = await raw(target); return Object.fromEntries(Object.entries(config).map(([key, value]) => [key, secrets[target].includes(key) ? "<redacted>" : value])); }
13
+ export async function writeAdminConfig(target: AdminTarget, value: unknown) {
14
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Configuration must be a JSON object.");
15
+ const current = await raw(target), next = { ...current, ...(value as Record<string, unknown>) };
16
+ for (const key of secrets[target]) if (next[key] === "<redacted>" || next[key] === undefined) next[key] = current[key];
17
+ if (typeof next.host !== "string" || !next.host) throw new Error("host must be a non-empty string.");
18
+ if (!Number.isInteger(next.port) || (next.port as number) < 1 || (next.port as number) > 65535) throw new Error("port must be an integer between 1 and 65535.");
19
+ const path = file(target), temporary = `${path}.tmp-${process.pid}`;
20
+ await writeFile(temporary, `${JSON.stringify(next, null, 2)}\n`, "utf8"); await rename(temporary, path);
21
+ return readAdminConfig(target);
22
+ }
@@ -0,0 +1,21 @@
1
+ import { createHmac, timingSafeEqual } from "node:crypto";
2
+ import { cookies } from "next/headers";
3
+ import { getConfig } from "./config";
4
+
5
+ const COOKIE = "pi_console_auth";
6
+ const token = (secret: string) => createHmac("sha256", secret).update("pi-console").digest("base64url");
7
+ export async function authenticated(): Promise<boolean> {
8
+ const config = await getConfig();
9
+ const value = (await cookies()).get(COOKIE)?.value;
10
+ if (!value) return false;
11
+ const expected = token(config.secret);
12
+ const a = Buffer.from(value), b = Buffer.from(expected);
13
+ return a.length === b.length && timingSafeEqual(a, b);
14
+ }
15
+ export async function loginValid(username: string, password: string): Promise<boolean> {
16
+ const config = await getConfig();
17
+ const a = Buffer.from(`${username}:${password}`), b = Buffer.from(`${config.username}:${config.password}`);
18
+ return a.length === b.length && timingSafeEqual(a, b);
19
+ }
20
+ export async function authToken(): Promise<string> { return token((await getConfig()).secret); }
21
+ export { COOKIE };
@@ -0,0 +1,15 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+
5
+ export type ConsoleConfig = { username: string; password: string; apiUrl: string; apiToken: string; secret: string };
6
+ type ApiConfig = { host?: string; port?: number; apiToken?: string };
7
+ type WebUiConfig = { username?: string; password?: string; secret?: string };
8
+ async function readJson<T>(path: string): Promise<T> { try { return JSON.parse(await readFile(path, "utf8")) as T; } catch (error) { if ((error as NodeJS.ErrnoException).code === "ENOENT") return {} as T; throw error; } }
9
+ export async function getConfig(): Promise<ConsoleConfig> {
10
+ const agentDir = process.env.PI_CODING_AGENT_DIR ?? join(homedir(), ".pi", "agent");
11
+ const [api, console] = await Promise.all([readJson<ApiConfig>(join(agentDir, "api-server.json")), readJson<WebUiConfig>(join(agentDir, "pi-console.webui.json"))]);
12
+ const host = api.host === "0.0.0.0" || !api.host ? "127.0.0.1" : api.host;
13
+ const apiUrl = process.env.PI_API_PROXY_TARGET || `http://${host}:${api.port ?? 8767}`;
14
+ return { username: console.username ?? "admin", password: console.password ?? "change-me", apiUrl, apiToken: api.apiToken ?? "", secret: console.secret ?? `${console.username ?? "admin"}:${console.password ?? "change-me"}` };
15
+ }
@@ -0,0 +1,9 @@
1
+ import { getConfig } from "@/lib/config";
2
+
3
+ export async function piGet<T>(path: string): Promise<T> {
4
+ const config = await getConfig();
5
+ if (!config.apiToken) throw new Error("Pi API token is not configured");
6
+ const response = await fetch(`${config.apiUrl}/api/${path}`, { headers: { authorization: `Bearer ${config.apiToken}` }, cache: "no-store" });
7
+ if (!response.ok) { const body = await response.json().catch(() => ({})); throw new Error(body.error?.message ?? body.error ?? "Pi API request failed"); }
8
+ return response.json() as Promise<T>;
9
+ }
@@ -0,0 +1,3 @@
1
+ import { clsx, type ClassValue } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+ export const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs));
@@ -0,0 +1,6 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+ /// <reference path="./.next/types/routes.d.ts" />
4
+
5
+ // NOTE: This file should not be edited
6
+ // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
@@ -0,0 +1,5 @@
1
+ const { join } = require("node:path");
2
+
3
+ module.exports = {
4
+ outputFileTracingRoot: join(__dirname, "../.."),
5
+ };
@@ -0,0 +1 @@
1
+ module.exports = { plugins: { tailwindcss: {}, autoprefixer: {} } };
@@ -0,0 +1,2 @@
1
+ import type { Config } from "tailwindcss";
2
+ export default { content: ["./app/**/*.{ts,tsx}", "./components/**/*.{ts,tsx}"], theme: { extend: {} }, plugins: [] } satisfies Config;
@@ -0,0 +1,41 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": [
5
+ "dom",
6
+ "dom.iterable",
7
+ "es2022"
8
+ ],
9
+ "strict": true,
10
+ "noEmit": true,
11
+ "module": "esnext",
12
+ "moduleResolution": "bundler",
13
+ "jsx": "preserve",
14
+ "plugins": [
15
+ {
16
+ "name": "next"
17
+ }
18
+ ],
19
+ "paths": {
20
+ "@/*": [
21
+ "./*"
22
+ ]
23
+ },
24
+ "allowJs": true,
25
+ "skipLibCheck": true,
26
+ "incremental": true,
27
+ "esModuleInterop": true,
28
+ "resolveJsonModule": true,
29
+ "isolatedModules": true
30
+ },
31
+ "include": [
32
+ "next-env.d.ts",
33
+ "**/*.ts",
34
+ "**/*.tsx",
35
+ ".next/types/**/*.ts"
36
+ ],
37
+ "exclude": [
38
+ "node_modules",
39
+ "index.ts"
40
+ ]
41
+ }