@jiggai/kitchen 0.3.9 → 0.3.10
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.
- package/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +2 -2
- package/.next/server/app/_global-error.html +2 -2
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +3 -3
- package/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/agents/[agentId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/channels/page_client-reference-manifest.js +1 -1
- package/.next/server/app/channels.html +2 -2
- package/.next/server/app/channels.rsc +4 -4
- package/.next/server/app/channels.segments/_full.segment.rsc +4 -4
- package/.next/server/app/channels.segments/_head.segment.rsc +1 -1
- package/.next/server/app/channels.segments/_index.segment.rsc +3 -3
- package/.next/server/app/channels.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/channels.segments/channels/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/channels.segments/channels.segment.rsc +1 -1
- package/.next/server/app/cron-jobs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/goals/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/goals/new/page_client-reference-manifest.js +1 -1
- package/.next/server/app/goals/new.html +2 -2
- package/.next/server/app/goals/new.rsc +4 -4
- package/.next/server/app/goals/new.segments/_full.segment.rsc +4 -4
- package/.next/server/app/goals/new.segments/_head.segment.rsc +1 -1
- package/.next/server/app/goals/new.segments/_index.segment.rsc +3 -3
- package/.next/server/app/goals/new.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/goals/new.segments/goals/new/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/goals/new.segments/goals/new.segment.rsc +1 -1
- package/.next/server/app/goals/new.segments/goals.segment.rsc +1 -1
- package/.next/server/app/goals/page_client-reference-manifest.js +1 -1
- package/.next/server/app/goals.html +1 -1
- package/.next/server/app/goals.rsc +4 -4
- package/.next/server/app/goals.segments/_full.segment.rsc +4 -4
- package/.next/server/app/goals.segments/_head.segment.rsc +1 -1
- package/.next/server/app/goals.segments/_index.segment.rsc +3 -3
- package/.next/server/app/goals.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/goals.segments/goals/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/goals.segments/goals.segment.rsc +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/recipes/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/recipes/page_client-reference-manifest.js +1 -1
- package/.next/server/app/runs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings.html +1 -1
- package/.next/server/app/settings.rsc +4 -4
- package/.next/server/app/settings.segments/_full.segment.rsc +4 -4
- package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_index.segment.rsc +3 -3
- package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
- package/.next/server/app/teams/[teamId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/teams/[teamId]/runs/[workflowId]/[runId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/teams/[teamId]/runs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/teams/[teamId]/tickets/[ticket]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/teams/[teamId]/tickets/page_client-reference-manifest.js +1 -1
- package/.next/server/app/teams/[teamId]/workflows/[workflowId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/teams/[teamId]/workflows/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tickets/[ticket]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tickets/page_client-reference-manifest.js +1 -1
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_1fe98a49.js +1 -1
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_1fe98a49.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__450d6006._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__450d6006._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__471bc2e7._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__471bc2e7._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__49281b4c._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__49281b4c._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__9a7d79aa._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__9a7d79aa._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__b577b475._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__b577b475._.js.map +1 -1
- package/.next/server/chunks/ssr/src_59477309._.js +1 -1
- package/.next/server/chunks/ssr/src_59477309._.js.map +1 -1
- package/.next/server/chunks/ssr/src_app_teams_[teamId]_team-editor_index_tsx_e68e9cbc._.js +1 -1
- package/.next/server/chunks/ssr/src_app_teams_[teamId]_team-editor_index_tsx_e68e9cbc._.js.map +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +2 -2
- package/.next/static/chunks/5f77499ca309ab9d.js +10 -0
- package/.next/static/chunks/7495459fdf300bf9.js +1 -0
- package/.next/static/chunks/{b28737d6a10af30d.js → ecdc9465f0e4d954.js} +1 -1
- package/.next/static/chunks/{cfe32a0bce2eab2b.css → f5d0d403c0fecb7f.css} +1 -1
- package/README.md +6 -5
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/.next/static/chunks/635adeaf329f2446.js +0 -1
- package/.next/static/chunks/6427cd3eaffdd572.js +0 -10
- /package/.next/static/{AKnetF2w3rOYqZBVpzsLP → 0PWp2TzSmiSf_Pf9YHES-}/_buildManifest.js +0 -0
- /package/.next/static/{AKnetF2w3rOYqZBVpzsLP → 0PWp2TzSmiSf_Pf9YHES-}/_clientMiddlewareManifest.json +0 -0
- /package/.next/static/{AKnetF2w3rOYqZBVpzsLP → 0PWp2TzSmiSf_Pf9YHES-}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../node_modules/next/dist/client/app-dir/link.js/__nextjs-internal-proxy.cjs","../../../../node_modules/next/src/client/app-dir/link.tsx","../../../../node_modules/next/src/client/app-dir/link.react-server.tsx","../../../../src/lib/kitchen-api.ts","../../../../src/lib/openclaw.ts","../../../../src/lib/workflows/runs-storage.ts","../../../../src/lib/workflows/readdir.ts","../../../../src/lib/workflows/storage.ts","../../../../src/app/teams/%5BteamId%5D/runs/%5BworkflowId%5D/%5BrunId%5D/run-detail-client.tsx/__nextjs-internal-proxy.mjs","../../../../src/app/teams/%5BteamId%5D/runs/%5BworkflowId%5D/%5BrunId%5D/page.tsx"],"sourcesContent":["// This file is generated by next-core EcmascriptClientReferenceModule.\nconst { createClientModuleProxy } = require(\"react-server-dom-turbopack/server\");\n\n__turbopack_context__.n(createClientModuleProxy(\"[project]/node_modules/next/dist/client/app-dir/link.js\"));\n","'use client'\n\nimport React, { createContext, useContext, useOptimistic, useRef } from 'react'\nimport type { UrlObject } from 'url'\nimport { formatUrl } from '../../shared/lib/router/utils/format-url'\nimport { AppRouterContext } from '../../shared/lib/app-router-context.shared-runtime'\nimport { useMergedRef } from '../use-merged-ref'\nimport { isAbsoluteUrl } from '../../shared/lib/utils'\nimport { addBasePath } from '../add-base-path'\nimport { warnOnce } from '../../shared/lib/utils/warn-once'\nimport type { PENDING_LINK_STATUS } from '../components/links'\nimport {\n IDLE_LINK_STATUS,\n mountLinkInstance,\n onNavigationIntent,\n unmountLinkForCurrentNavigation,\n unmountPrefetchableInstance,\n type LinkInstance,\n} from '../components/links'\nimport { isLocalURL } from '../../shared/lib/router/utils/is-local-url'\nimport {\n FetchStrategy,\n type PrefetchTaskFetchStrategy,\n} from '../components/segment-cache/types'\nimport { errorOnce } from '../../shared/lib/utils/error-once'\n\ntype Url = string | UrlObject\ntype RequiredKeys<T> = {\n [K in keyof T]-?: {} extends Pick<T, K> ? never : K\n}[keyof T]\ntype OptionalKeys<T> = {\n [K in keyof T]-?: {} extends Pick<T, K> ? K : never\n}[keyof T]\n\ntype OnNavigateEventHandler = (event: { preventDefault: () => void }) => void\n\ntype InternalLinkProps = {\n /**\n * **Required**. The path or URL to navigate to. It can also be an object (similar to `URL`).\n *\n * @example\n * ```tsx\n * // Navigate to /dashboard:\n * <Link href=\"/dashboard\">Dashboard</Link>\n *\n * // Navigate to /about?name=test:\n * <Link href={{ pathname: '/about', query: { name: 'test' } }}>\n * About\n * </Link>\n * ```\n *\n * @remarks\n * - For external URLs, use a fully qualified URL such as `https://...`.\n * - In the App Router, dynamic routes must not include bracketed segments in `href`.\n */\n href: Url\n\n /**\n * @deprecated v10.0.0: `href` props pointing to a dynamic route are\n * automatically resolved and no longer require the `as` prop.\n */\n as?: Url\n\n /**\n * Replace the current `history` state instead of adding a new URL into the stack.\n *\n * @defaultValue `false`\n *\n * @example\n * ```tsx\n * <Link href=\"/about\" replace>\n * About (replaces the history state)\n * </Link>\n * ```\n */\n replace?: boolean\n\n /**\n * Whether to override the default scroll behavior. If `true`, Next.js attempts to maintain\n * the scroll position if the newly navigated page is still visible. If not, it scrolls to the top.\n *\n * If `false`, Next.js will not modify the scroll behavior at all.\n *\n * @defaultValue `true`\n *\n * @example\n * ```tsx\n * <Link href=\"/dashboard\" scroll={false}>\n * No auto scroll\n * </Link>\n * ```\n */\n scroll?: boolean\n\n /**\n * Update the path of the current page without rerunning data fetching methods\n * like `getStaticProps`, `getServerSideProps`, or `getInitialProps`.\n *\n * @remarks\n * `shallow` only applies to the Pages Router. For the App Router, see the\n * [following documentation](https://nextjs.org/docs/app/building-your-application/routing/linking-and-navigating#using-the-native-history-api).\n *\n * @defaultValue `false`\n *\n * @example\n * ```tsx\n * <Link href=\"/blog\" shallow>\n * Shallow navigation\n * </Link>\n * ```\n */\n shallow?: boolean\n\n /**\n * Forces `Link` to pass its `href` to the child component. Useful if the child is a custom\n * component that wraps an `<a>` tag, or if you're using certain styling libraries.\n *\n * @defaultValue `false`\n *\n * @example\n * ```tsx\n * <Link href=\"/dashboard\" passHref legacyBehavior>\n * <MyStyledAnchor>Dashboard</MyStyledAnchor>\n * </Link>\n * ```\n */\n passHref?: boolean\n\n /**\n * Prefetch the page in the background.\n * Any `<Link />` that is in the viewport (initially or through scroll) will be prefetched.\n * Prefetch can be disabled by passing `prefetch={false}`.\n *\n * @remarks\n * Prefetching is only enabled in production.\n *\n * - In the **App Router**:\n * - `\"auto\"`, `null`, `undefined` (default): Prefetch behavior depends on static vs dynamic routes:\n * - Static routes: fully prefetched\n * - Dynamic routes: partial prefetch to the nearest segment with a `loading.js`\n * - `true`: Always prefetch the full route and data.\n * - `false`: Disable prefetching on both viewport and hover.\n * - In the **Pages Router**:\n * - `true` (default): Prefetches the route and data in the background on viewport or hover.\n * - `false`: Prefetch only on hover, not on viewport.\n *\n * @defaultValue `true` (Pages Router) or `null` (App Router)\n *\n * @example\n * ```tsx\n * <Link href=\"/dashboard\" prefetch={false}>\n * Dashboard\n * </Link>\n * ```\n */\n prefetch?: boolean | 'auto' | null\n\n /**\n * (unstable) Switch to a full prefetch on hover. Effectively the same as\n * updating the prefetch prop to `true` in a mouse event.\n */\n unstable_dynamicOnHover?: boolean\n\n /**\n * The active locale is automatically prepended in the Pages Router. `locale` allows for providing\n * a different locale, or can be set to `false` to opt out of automatic locale behavior.\n *\n * @remarks\n * Note: locale only applies in the Pages Router and is ignored in the App Router.\n *\n * @example\n * ```tsx\n * // Use the 'fr' locale:\n * <Link href=\"/about\" locale=\"fr\">\n * About (French)\n * </Link>\n *\n * // Disable locale prefix:\n * <Link href=\"/about\" locale={false}>\n * About (no locale prefix)\n * </Link>\n * ```\n */\n locale?: string | false\n\n /**\n * Enable legacy link behavior.\n *\n * @deprecated This will be removed in a future version\n * @defaultValue `false`\n * @see https://github.com/vercel/next.js/commit/489e65ed98544e69b0afd7e0cfc3f9f6c2b803b7\n */\n legacyBehavior?: boolean\n\n /**\n * Optional event handler for when the mouse pointer is moved onto the `<Link>`.\n */\n onMouseEnter?: React.MouseEventHandler<HTMLAnchorElement>\n\n /**\n * Optional event handler for when the `<Link>` is touched.\n */\n onTouchStart?: React.TouchEventHandler<HTMLAnchorElement>\n\n /**\n * Optional event handler for when the `<Link>` is clicked.\n */\n onClick?: React.MouseEventHandler<HTMLAnchorElement>\n\n /**\n * Optional event handler for when the `<Link>` is navigated.\n */\n onNavigate?: OnNavigateEventHandler\n}\n\n// TODO-APP: Include the full set of Anchor props\n// adding this to the publicly exported type currently breaks existing apps\n\n// `RouteInferType` is a stub here to avoid breaking `typedRoutes` when the type\n// isn't generated yet. It will be replaced when type generation runs.\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport type LinkProps<RouteInferType = any> = InternalLinkProps\ntype LinkPropsRequired = RequiredKeys<LinkProps>\ntype LinkPropsOptional = OptionalKeys<Omit<InternalLinkProps, 'locale'>>\n\nfunction isModifiedEvent(event: React.MouseEvent): boolean {\n const eventTarget = event.currentTarget as HTMLAnchorElement | SVGAElement\n const target = eventTarget.getAttribute('target')\n return (\n (target && target !== '_self') ||\n event.metaKey ||\n event.ctrlKey ||\n event.shiftKey ||\n event.altKey || // triggers resource download\n (event.nativeEvent && event.nativeEvent.which === 2)\n )\n}\n\nfunction linkClicked(\n e: React.MouseEvent,\n href: string,\n as: string,\n linkInstanceRef: React.RefObject<LinkInstance | null>,\n replace?: boolean,\n scroll?: boolean,\n onNavigate?: OnNavigateEventHandler\n): void {\n if (typeof window !== 'undefined') {\n const { nodeName } = e.currentTarget\n\n // anchors inside an svg have a lowercase nodeName\n const isAnchorNodeName = nodeName.toUpperCase() === 'A'\n if (\n (isAnchorNodeName && isModifiedEvent(e)) ||\n e.currentTarget.hasAttribute('download')\n ) {\n // ignore click for browser’s default behavior\n return\n }\n\n if (!isLocalURL(href)) {\n if (replace) {\n // browser default behavior does not replace the history state\n // so we need to do it manually\n e.preventDefault()\n location.replace(href)\n }\n\n // ignore click for browser’s default behavior\n return\n }\n\n e.preventDefault()\n\n if (onNavigate) {\n let isDefaultPrevented = false\n\n onNavigate({\n preventDefault: () => {\n isDefaultPrevented = true\n },\n })\n\n if (isDefaultPrevented) {\n return\n }\n }\n\n const { dispatchNavigateAction } =\n require('../components/app-router-instance') as typeof import('../components/app-router-instance')\n\n React.startTransition(() => {\n dispatchNavigateAction(\n as || href,\n replace ? 'replace' : 'push',\n scroll ?? true,\n linkInstanceRef.current\n )\n })\n }\n}\n\nfunction formatStringOrUrl(urlObjOrString: UrlObject | string): string {\n if (typeof urlObjOrString === 'string') {\n return urlObjOrString\n }\n\n return formatUrl(urlObjOrString)\n}\n\n/**\n * A React component that extends the HTML `<a>` element to provide\n * [prefetching](https://nextjs.org/docs/app/building-your-application/routing/linking-and-navigating#2-prefetching)\n * and client-side navigation. This is the primary way to navigate between routes in Next.js.\n *\n * @remarks\n * - Prefetching is only enabled in production.\n *\n * @see https://nextjs.org/docs/app/api-reference/components/link\n */\nexport default function LinkComponent(\n props: LinkProps & {\n children: React.ReactNode\n ref: React.Ref<HTMLAnchorElement>\n }\n) {\n const [linkStatus, setOptimisticLinkStatus] = useOptimistic(IDLE_LINK_STATUS)\n\n let children: React.ReactNode\n\n const linkInstanceRef = useRef<LinkInstance | null>(null)\n\n const {\n href: hrefProp,\n as: asProp,\n children: childrenProp,\n prefetch: prefetchProp = null,\n passHref,\n replace,\n shallow,\n scroll,\n onClick,\n onMouseEnter: onMouseEnterProp,\n onTouchStart: onTouchStartProp,\n legacyBehavior = false,\n onNavigate,\n ref: forwardedRef,\n unstable_dynamicOnHover,\n ...restProps\n } = props\n\n children = childrenProp\n\n if (\n legacyBehavior &&\n (typeof children === 'string' || typeof children === 'number')\n ) {\n children = <a>{children}</a>\n }\n\n const router = React.useContext(AppRouterContext)\n\n const prefetchEnabled = prefetchProp !== false\n\n const fetchStrategy =\n prefetchProp !== false\n ? getFetchStrategyFromPrefetchProp(prefetchProp)\n : // TODO: it makes no sense to assign a fetchStrategy when prefetching is disabled.\n FetchStrategy.PPR\n\n if (process.env.NODE_ENV !== 'production') {\n function createPropError(args: {\n key: string\n expected: string\n actual: string\n }) {\n return new Error(\n `Failed prop type: The prop \\`${args.key}\\` expects a ${args.expected} in \\`<Link>\\`, but got \\`${args.actual}\\` instead.` +\n (typeof window !== 'undefined'\n ? \"\\nOpen your browser's console to view the Component stack trace.\"\n : '')\n )\n }\n\n // TypeScript trick for type-guarding:\n const requiredPropsGuard: Record<LinkPropsRequired, true> = {\n href: true,\n } as const\n const requiredProps: LinkPropsRequired[] = Object.keys(\n requiredPropsGuard\n ) as LinkPropsRequired[]\n requiredProps.forEach((key: LinkPropsRequired) => {\n if (key === 'href') {\n if (\n props[key] == null ||\n (typeof props[key] !== 'string' && typeof props[key] !== 'object')\n ) {\n throw createPropError({\n key,\n expected: '`string` or `object`',\n actual: props[key] === null ? 'null' : typeof props[key],\n })\n }\n } else {\n // TypeScript trick for type-guarding:\n const _: never = key\n }\n })\n\n // TypeScript trick for type-guarding:\n const optionalPropsGuard: Record<LinkPropsOptional, true> = {\n as: true,\n replace: true,\n scroll: true,\n shallow: true,\n passHref: true,\n prefetch: true,\n unstable_dynamicOnHover: true,\n onClick: true,\n onMouseEnter: true,\n onTouchStart: true,\n legacyBehavior: true,\n onNavigate: true,\n } as const\n const optionalProps: LinkPropsOptional[] = Object.keys(\n optionalPropsGuard\n ) as LinkPropsOptional[]\n optionalProps.forEach((key: LinkPropsOptional) => {\n const valType = typeof props[key]\n\n if (key === 'as') {\n if (props[key] && valType !== 'string' && valType !== 'object') {\n throw createPropError({\n key,\n expected: '`string` or `object`',\n actual: valType,\n })\n }\n } else if (\n key === 'onClick' ||\n key === 'onMouseEnter' ||\n key === 'onTouchStart' ||\n key === 'onNavigate'\n ) {\n if (props[key] && valType !== 'function') {\n throw createPropError({\n key,\n expected: '`function`',\n actual: valType,\n })\n }\n } else if (\n key === 'replace' ||\n key === 'scroll' ||\n key === 'shallow' ||\n key === 'passHref' ||\n key === 'legacyBehavior' ||\n key === 'unstable_dynamicOnHover'\n ) {\n if (props[key] != null && valType !== 'boolean') {\n throw createPropError({\n key,\n expected: '`boolean`',\n actual: valType,\n })\n }\n } else if (key === 'prefetch') {\n if (\n props[key] != null &&\n valType !== 'boolean' &&\n props[key] !== 'auto'\n ) {\n throw createPropError({\n key,\n expected: '`boolean | \"auto\"`',\n actual: valType,\n })\n }\n } else {\n // TypeScript trick for type-guarding:\n const _: never = key\n }\n })\n }\n\n if (process.env.NODE_ENV !== 'production') {\n if (props.locale) {\n warnOnce(\n 'The `locale` prop is not supported in `next/link` while using the `app` router. Read more about app router internalization: https://nextjs.org/docs/app/building-your-application/routing/internationalization'\n )\n }\n if (!asProp) {\n let href: string | undefined\n if (typeof hrefProp === 'string') {\n href = hrefProp\n } else if (\n typeof hrefProp === 'object' &&\n typeof hrefProp.pathname === 'string'\n ) {\n href = hrefProp.pathname\n }\n\n if (href) {\n const hasDynamicSegment = href\n .split('/')\n .some((segment) => segment.startsWith('[') && segment.endsWith(']'))\n\n if (hasDynamicSegment) {\n throw new Error(\n `Dynamic href \\`${href}\\` found in <Link> while using the \\`/app\\` router, this is not supported. Read more: https://nextjs.org/docs/messages/app-dir-dynamic-href`\n )\n }\n }\n }\n }\n\n const { href, as } = React.useMemo(() => {\n const resolvedHref = formatStringOrUrl(hrefProp)\n return {\n href: resolvedHref,\n as: asProp ? formatStringOrUrl(asProp) : resolvedHref,\n }\n }, [hrefProp, asProp])\n\n // This will return the first child, if multiple are provided it will throw an error\n let child: any\n if (legacyBehavior) {\n if ((children as any)?.$$typeof === Symbol.for('react.lazy')) {\n throw new Error(\n `\\`<Link legacyBehavior>\\` received a direct child that is either a Server Component, or JSX that was loaded with React.lazy(). This is not supported. Either remove legacyBehavior, or make the direct child a Client Component that renders the Link's \\`<a>\\` tag.`\n )\n }\n\n if (process.env.NODE_ENV === 'development') {\n if (onClick) {\n console.warn(\n `\"onClick\" was passed to <Link> with \\`href\\` of \\`${hrefProp}\\` but \"legacyBehavior\" was set. The legacy behavior requires onClick be set on the child of next/link`\n )\n }\n if (onMouseEnterProp) {\n console.warn(\n `\"onMouseEnter\" was passed to <Link> with \\`href\\` of \\`${hrefProp}\\` but \"legacyBehavior\" was set. The legacy behavior requires onMouseEnter be set on the child of next/link`\n )\n }\n try {\n child = React.Children.only(children)\n } catch (err) {\n if (!children) {\n throw new Error(\n `No children were passed to <Link> with \\`href\\` of \\`${hrefProp}\\` but one child is required https://nextjs.org/docs/messages/link-no-children`\n )\n }\n throw new Error(\n `Multiple children were passed to <Link> with \\`href\\` of \\`${hrefProp}\\` but only one child is supported https://nextjs.org/docs/messages/link-multiple-children` +\n (typeof window !== 'undefined'\n ? \" \\nOpen your browser's console to view the Component stack trace.\"\n : '')\n )\n }\n } else {\n child = React.Children.only(children)\n }\n } else {\n if (process.env.NODE_ENV === 'development') {\n if ((children as any)?.type === 'a') {\n throw new Error(\n 'Invalid <Link> with <a> child. Please remove <a> or use <Link legacyBehavior>.\\nLearn more: https://nextjs.org/docs/messages/invalid-new-link-with-extra-anchor'\n )\n }\n }\n }\n\n const childRef: any = legacyBehavior\n ? child && typeof child === 'object' && child.ref\n : forwardedRef\n\n // Use a callback ref to attach an IntersectionObserver to the anchor tag on\n // mount. In the future we will also use this to keep track of all the\n // currently mounted <Link> instances, e.g. so we can re-prefetch them after\n // a revalidation or refresh.\n const observeLinkVisibilityOnMount = React.useCallback(\n (element: HTMLAnchorElement | SVGAElement) => {\n if (router !== null) {\n linkInstanceRef.current = mountLinkInstance(\n element,\n href,\n router,\n fetchStrategy,\n prefetchEnabled,\n setOptimisticLinkStatus\n )\n }\n\n return () => {\n if (linkInstanceRef.current) {\n unmountLinkForCurrentNavigation(linkInstanceRef.current)\n linkInstanceRef.current = null\n }\n unmountPrefetchableInstance(element)\n }\n },\n [prefetchEnabled, href, router, fetchStrategy, setOptimisticLinkStatus]\n )\n\n const mergedRef = useMergedRef(observeLinkVisibilityOnMount, childRef)\n\n const childProps: {\n onTouchStart?: React.TouchEventHandler<HTMLAnchorElement>\n onMouseEnter: React.MouseEventHandler<HTMLAnchorElement>\n onClick: React.MouseEventHandler<HTMLAnchorElement>\n href?: string\n ref?: any\n } = {\n ref: mergedRef,\n onClick(e) {\n if (process.env.NODE_ENV !== 'production') {\n if (!e) {\n throw new Error(\n `Component rendered inside next/link has to pass click event to \"onClick\" prop.`\n )\n }\n }\n\n if (!legacyBehavior && typeof onClick === 'function') {\n onClick(e)\n }\n\n if (\n legacyBehavior &&\n child.props &&\n typeof child.props.onClick === 'function'\n ) {\n child.props.onClick(e)\n }\n\n if (!router) {\n return\n }\n if (e.defaultPrevented) {\n return\n }\n linkClicked(e, href, as, linkInstanceRef, replace, scroll, onNavigate)\n },\n onMouseEnter(e) {\n if (!legacyBehavior && typeof onMouseEnterProp === 'function') {\n onMouseEnterProp(e)\n }\n\n if (\n legacyBehavior &&\n child.props &&\n typeof child.props.onMouseEnter === 'function'\n ) {\n child.props.onMouseEnter(e)\n }\n\n if (!router) {\n return\n }\n if (!prefetchEnabled || process.env.NODE_ENV === 'development') {\n return\n }\n\n const upgradeToDynamicPrefetch = unstable_dynamicOnHover === true\n onNavigationIntent(\n e.currentTarget as HTMLAnchorElement | SVGAElement,\n upgradeToDynamicPrefetch\n )\n },\n onTouchStart: process.env.__NEXT_LINK_NO_TOUCH_START\n ? undefined\n : function onTouchStart(e) {\n if (!legacyBehavior && typeof onTouchStartProp === 'function') {\n onTouchStartProp(e)\n }\n\n if (\n legacyBehavior &&\n child.props &&\n typeof child.props.onTouchStart === 'function'\n ) {\n child.props.onTouchStart(e)\n }\n\n if (!router) {\n return\n }\n if (!prefetchEnabled) {\n return\n }\n\n const upgradeToDynamicPrefetch = unstable_dynamicOnHover === true\n onNavigationIntent(\n e.currentTarget as HTMLAnchorElement | SVGAElement,\n upgradeToDynamicPrefetch\n )\n },\n }\n\n // If the url is absolute, we can bypass the logic to prepend the basePath.\n if (isAbsoluteUrl(as)) {\n childProps.href = as\n } else if (\n !legacyBehavior ||\n passHref ||\n (child.type === 'a' && !('href' in child.props))\n ) {\n childProps.href = addBasePath(as)\n }\n\n let link: React.ReactNode\n\n if (legacyBehavior) {\n if (process.env.NODE_ENV === 'development') {\n errorOnce(\n '`legacyBehavior` is deprecated and will be removed in a future ' +\n 'release. A codemod is available to upgrade your components:\\n\\n' +\n 'npx @next/codemod@latest new-link .\\n\\n' +\n 'Learn more: https://nextjs.org/docs/app/building-your-application/upgrading/codemods#remove-a-tags-from-link-components'\n )\n }\n link = React.cloneElement(child, childProps)\n } else {\n link = (\n <a {...restProps} {...childProps}>\n {children}\n </a>\n )\n }\n\n return (\n <LinkStatusContext.Provider value={linkStatus}>\n {link}\n </LinkStatusContext.Provider>\n )\n}\n\nconst LinkStatusContext = createContext<\n typeof PENDING_LINK_STATUS | typeof IDLE_LINK_STATUS\n>(IDLE_LINK_STATUS)\n\nexport const useLinkStatus = () => {\n return useContext(LinkStatusContext)\n}\n\nfunction getFetchStrategyFromPrefetchProp(\n prefetchProp: Exclude<LinkProps['prefetch'], undefined | false>\n): PrefetchTaskFetchStrategy {\n if (process.env.__NEXT_CACHE_COMPONENTS) {\n if (prefetchProp === true) {\n return FetchStrategy.Full\n }\n\n // `null` or `\"auto\"`: this is the default \"auto\" mode, where we will prefetch partially if the link is in the viewport.\n // This will also include invalid prop values that don't match the types specified here.\n // (although those should've been filtered out by prop validation in dev)\n prefetchProp satisfies null | 'auto'\n return FetchStrategy.PPR\n } else {\n return prefetchProp === null || prefetchProp === 'auto'\n ? // We default to PPR, and we'll discover whether or not the route supports it with the initial prefetch.\n FetchStrategy.PPR\n : // In the old implementation without runtime prefetches, `prefetch={true}` forces all dynamic data to be prefetched.\n // To preserve backwards-compatibility, anything other than `false`, `null`, or `\"auto\"` results in a full prefetch.\n // (although invalid values should've been filtered out by prop validation in dev)\n FetchStrategy.Full\n }\n}\n","import type { ComponentProps } from 'react'\nimport ClientLinkComponent, { type LinkProps, useLinkStatus } from './link'\n\nexport default function LinkComponent(\n props: ComponentProps<typeof ClientLinkComponent>\n) {\n const isLegacyBehavior = props.legacyBehavior\n const childIsHostComponent =\n typeof props.children === 'string' ||\n typeof props.children === 'number' ||\n typeof (props.children as any)?.type === 'string'\n const childIsClientComponent =\n (props.children as any)?.type?.$$typeof ===\n Symbol.for('react.client.reference')\n\n if (isLegacyBehavior && !childIsHostComponent && !childIsClientComponent) {\n if ((props.children as any)?.type?.$$typeof === Symbol.for('react.lazy')) {\n console.error(\n `Using a Lazy Component as a direct child of \\`<Link legacyBehavior>\\` from a Server Component is not supported. If you need legacyBehavior, wrap your Lazy Component in a Client Component that renders the Link's \\`<a>\\` tag.`\n )\n } else {\n console.error(\n `Using a Server Component as a direct child of \\`<Link legacyBehavior>\\` is not supported. If you need legacyBehavior, wrap your Server Component in a Client Component that renders the Link's \\`<a>\\` tag.`\n )\n }\n }\n\n return <ClientLinkComponent {...props} />\n}\n\nexport { type LinkProps, useLinkStatus }\n","// NOTE: This file is compiled by Next.js.\n// Do not import `openclaw/plugin-sdk` here (it is provided by the gateway runtime, not as an npm dep).\n\ntype KitchenApi = {\n config: unknown;\n runtime: {\n system: { runCommandWithTimeout: (argv: string[], opts: { timeoutMs: number }) => Promise<{ stdout?: string; stderr?: string }> };\n };\n};\n\nexport function getKitchenApi(): KitchenApi {\n const api = (globalThis as unknown as { __clawkitchen_api?: KitchenApi }).__clawkitchen_api;\n if (!api) {\n throw new Error(\n \"ClawKitchen: OpenClaw plugin API not available. (This should only happen if Kitchen is started outside the gateway process.)\",\n );\n }\n return api;\n}\n","import { getKitchenApi } from \"@/lib/kitchen-api\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\nexport type OpenClawExecResult = {\n ok: boolean;\n exitCode: number;\n stdout: string;\n stderr: string;\n};\n\nfunction extractStdout(err: { stdout?: unknown }): string {\n if (typeof err.stdout === \"string\") return err.stdout;\n if (err.stdout && typeof err.stdout === \"object\" && \"toString\" in err.stdout) {\n return String((err.stdout as { toString: () => string }).toString());\n }\n return \"\";\n}\n\nfunction resolveExitCode(res: { exitCode?: unknown; code?: unknown; status?: unknown }): number {\n if (typeof res.exitCode === \"number\") return res.exitCode;\n if (typeof res.code === \"number\") return res.code;\n if (typeof res.status === \"number\") return res.status;\n return 0;\n}\n\nfunction extractStderr(err: { stderr?: unknown; message?: unknown }, fallback: unknown): string {\n if (typeof err.stderr === \"string\") return err.stderr;\n if (err.stderr && typeof err.stderr === \"object\" && \"toString\" in err.stderr) {\n return String((err.stderr as { toString: () => string }).toString());\n }\n if (typeof err.message === \"string\") return err.message;\n return String(fallback);\n}\n\nasync function runOpenClawLocal(args: string[]): Promise<OpenClawExecResult> {\n try {\n const res = await execFileAsync(\"openclaw\", args, {\n timeout: 120000,\n windowsHide: true,\n maxBuffer: 10 * 1024 * 1024,\n });\n\n return {\n ok: true,\n exitCode: 0,\n stdout: String(res.stdout ?? \"\"),\n stderr: String(res.stderr ?? \"\"),\n };\n } catch (e: unknown) {\n const err = e as { code?: unknown; stdout?: unknown; stderr?: unknown; message?: unknown };\n const exitCode = typeof err.code === \"number\" ? err.code : 1;\n const stdout = extractStdout(err);\n const stderr = extractStderr(err, e);\n return { ok: false, exitCode, stdout, stderr };\n }\n}\n\nexport async function runOpenClaw(args: string[]): Promise<OpenClawExecResult> {\n // In some Kitchen runtime contexts, `api.runtime.system.runCommandWithTimeout`\n // is executed with a restricted allowlist that does not include the `cron` tool,\n // causing `openclaw cron ...` to fail with \"Tool not available: cron\".\n //\n // Cron routes need to work in the gateway-run Kitchen environment, so for cron\n // specifically we prefer a local exec (host OpenClaw).\n if (args[0] === \"cron\") return runOpenClawLocal(args);\n\n const api = getKitchenApi();\n try {\n const res = (await api.runtime.system.runCommandWithTimeout([\"openclaw\", ...args], { timeoutMs: 120000 })) as {\n stdout?: unknown;\n stderr?: unknown;\n exitCode?: unknown;\n code?: unknown;\n status?: unknown;\n };\n\n const stdout = String(res.stdout ?? \"\");\n const stderr = String(res.stderr ?? \"\");\n const exitCode = resolveExitCode(res);\n\n if (exitCode !== 0) return { ok: false, exitCode, stdout, stderr };\n return { ok: true, exitCode: 0, stdout, stderr };\n } catch (e: unknown) {\n const err = e as { code?: unknown; stdout?: unknown; stderr?: unknown; message?: unknown };\n const exitCode = typeof err.code === \"number\" ? err.code : 1;\n const stdout = extractStdout(err);\n const stderr = extractStderr(err, e);\n return { ok: false, exitCode, stdout, stderr };\n }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { getTeamWorkspaceDir } from \"@/lib/paths\";\nimport { readdirFiles } from \"@/lib/workflows/readdir\";\nimport { assertSafeWorkflowId } from \"@/lib/workflows/storage\";\nimport type { WorkflowRunFileV1, WorkflowRunNodeResultV1 } from \"@/lib/workflows/runs-types\";\n\nconst RUNS_DIR = path.join(\"shared-context\", \"workflow-runs\");\n\n/**\n * Back-compat:\n * - Legacy Kitchen layout: shared-context/workflow-runs/<workflowId>/*.run.json\n * - Runner layout (preferred): shared-context/workflow-runs/*.run.json\n */\nconst LEGACY_PER_WORKFLOW_LAYOUT = true;\n\nexport function assertSafeRunId(runId: string) {\n const id = String(runId ?? \"\").trim();\n if (!id) throw new Error(\"run id is required\");\n\n // Accept both Kitchen-style run ids (lowercase) and runner ids that include ISO timestamps (T/Z).\n // We still keep it conservative: only letters/numbers/dashes.\n if (!/^[A-Za-z0-9][A-Za-z0-9-]{0,120}$/.test(id)) {\n throw new Error(\"Invalid run id. Use letters, numbers, and dashes.\"\n );\n }\n return id;\n}\n\nexport async function getWorkflowRunsDir(teamId: string, workflowId: string) {\n // Validate workflowId (we no longer use it for the directory name).\n void assertSafeWorkflowId(workflowId);\n const teamDir = await getTeamWorkspaceDir(teamId);\n return path.join(teamDir, RUNS_DIR);\n}\n\nexport function workflowRunFileName(runId: string) {\n return `${runId}.run.json`;\n}\n\nexport async function listWorkflowRuns(teamId: string, workflowId: string) {\n const wfId = assertSafeWorkflowId(workflowId);\n const dir = await getWorkflowRunsDir(teamId, wfId);\n\n const runIds: string[] = [];\n\n // Preferred: runner directory-per-run layout: shared-context/workflow-runs/<runId>/run.json\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n const runDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();\n\n for (const runId of runDirs) {\n const p = path.join(dir, runId, \"run.json\");\n try {\n const raw = await fs.readFile(p, \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n const normalized = normalizeRunFile(teamId, wfId, parsed, runId);\n if (normalized.workflowId === wfId) runIds.push(runId);\n } catch {\n // ignore\n }\n }\n } catch {\n // ignore\n }\n\n // Legacy: flat *.run.json at root\n try {\n const { files } = await readdirFiles(dir, \".run.json\", true);\n for (const f of files) {\n const runId = f.replace(/\\.run\\.json$/i, \"\");\n try {\n const raw = await fs.readFile(path.join(dir, f), \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n const normalized = normalizeRunFile(teamId, wfId, parsed, runId);\n if (normalized.workflowId === wfId) runIds.push(runId);\n } catch {\n // ignore\n }\n }\n } catch {\n // ignore\n }\n\n // Back-compat: legacy per-workflow directory.\n if (LEGACY_PER_WORKFLOW_LAYOUT) {\n try {\n const legacyDir = path.join(dir, wfId);\n const { files } = await readdirFiles(legacyDir, \".run.json\", true);\n for (const f of files) runIds.push(f.replace(/\\.run\\.json$/i, \"\"));\n } catch {\n // ignore\n }\n }\n\n const uniq = Array.from(new Set(runIds)).sort().reverse();\n return { ok: true as const, dir, runIds: uniq };\n}\n\nexport async function readWorkflowRun(teamId: string, workflowId: string, runId: string) {\n const wfId = assertSafeWorkflowId(workflowId);\n const rId = assertSafeRunId(runId);\n const dir = await getWorkflowRunsDir(teamId, wfId);\n\n // Preferred (runner) layout: shared-context/workflow-runs/<runId>/run.json\n const pRunner = path.join(dir, rId, \"run.json\");\n try {\n const raw = await fs.readFile(pRunner, \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n const run = normalizeRunFile(teamId, wfId, parsed, rId);\n\n // Runner stores rich node outputs as files under:\n // workflow-runs/<runId>/node-outputs/*-<nodeId>.json\n // Normalize by hydrating node.output from those files when missing.\n if (Array.isArray(run.nodes) && run.nodes.length) {\n const outputsDir = path.join(dir, rId, \"node-outputs\");\n let files: string[] = [];\n try {\n files = await fs.readdir(outputsDir);\n } catch {\n files = [];\n }\n\n // Multiple output files may exist for the same nodeId when a workflow executes a node\n // more than once. Preserve ordering by assigning outputs sequentially per nodeId.\n //\n // NOTE: Runner output filenames are typically prefixed with a sortable counter (e.g. 001-<nodeId>.json).\n // We rely on lexicographic order of filenames within node-outputs.\n const byNodeId = new Map<string, string[]>();\n for (const f of files.slice().sort()) {\n const m = f.match(/-([^/]+)\\.json$/);\n if (!m) continue;\n const nodeId = m[1];\n if (!nodeId) continue;\n const existing = byNodeId.get(nodeId) ?? [];\n existing.push(path.join(outputsDir, f));\n byNodeId.set(nodeId, existing);\n }\n\n await Promise.all(\n run.nodes.map(async (n) => {\n if (!n || typeof n !== \"object\") return;\n // Only hydrate if output is truly missing.\n if (typeof n.output !== \"undefined\") return;\n\n const arr = byNodeId.get(n.nodeId);\n if (!arr || !arr.length) return;\n\n const pOut = arr.shift();\n if (!pOut) return;\n\n try {\n const outRaw = await fs.readFile(pOut, \"utf8\");\n n.output = JSON.parse(outRaw) as unknown;\n } catch {\n // ignore\n }\n })\n );\n }\n\n return { ok: true as const, path: pRunner, run };\n } catch (err: unknown) {\n // fallthrough\n if (!(err && typeof err === \"object\" && (err as { code?: unknown }).code === \"ENOENT\")) throw err;\n }\n\n // Legacy flat layout: shared-context/workflow-runs/<runId>.run.json\n const p0 = path.join(dir, workflowRunFileName(rId));\n try {\n const raw = await fs.readFile(p0, \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n return { ok: true as const, path: p0, run: normalizeRunFile(teamId, wfId, parsed, rId) };\n } catch (err: unknown) {\n if (LEGACY_PER_WORKFLOW_LAYOUT && err && typeof err === \"object\" && (err as { code?: unknown }).code === \"ENOENT\") {\n const legacy = path.join(dir, wfId, workflowRunFileName(rId));\n const raw = await fs.readFile(legacy, \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n return { ok: true as const, path: legacy, run: normalizeRunFile(teamId, wfId, parsed, rId) };\n }\n throw err;\n }\n}\n\nexport async function writeWorkflowRun(teamId: string, workflowId: string, run: WorkflowRunFileV1) {\n const wfId = assertSafeWorkflowId(workflowId);\n const rId = assertSafeRunId(run.id);\n const dir = await getWorkflowRunsDir(teamId, wfId);\n await fs.mkdir(dir, { recursive: true });\n\n // Default layout: write to shared-context/workflow-runs/<runId>.run.json\n //\n // IMPORTANT: that filename may already be occupied by a runner-owned run log (different schema).\n // In that case, keep the runner log intact and write the Kitchen run file to the legacy\n // per-workflow directory shared-context/workflow-runs/<workflowId>/<runId>.run.json.\n const p0 = path.join(dir, workflowRunFileName(rId));\n\n let p = p0;\n try {\n const raw = await fs.readFile(p0, \"utf8\");\n const existing = JSON.parse(raw) as unknown;\n const isRunnerLog =\n Boolean(existing) &&\n typeof existing === \"object\" &&\n !Array.isArray(existing) &&\n \"runId\" in (existing as Record<string, unknown>) &&\n !(\"schema\" in (existing as Record<string, unknown>));\n\n if (isRunnerLog && LEGACY_PER_WORKFLOW_LAYOUT) {\n const legacyDir = path.join(dir, wfId);\n await fs.mkdir(legacyDir, { recursive: true });\n p = path.join(legacyDir, workflowRunFileName(rId));\n }\n } catch {\n // ignore read/parse errors; we'll write to p0\n }\n\n const toWrite: WorkflowRunFileV1 = {\n ...run,\n schema: \"clawkitchen.workflow-run.v1\",\n id: rId,\n workflowId: wfId,\n teamId,\n };\n await fs.writeFile(p, JSON.stringify(toWrite, null, 2) + \"\\n\", \"utf8\");\n return { ok: true as const, path: p };\n}\n\nexport type WorkflowRunSummary = {\n workflowId: string;\n runId: string;\n status?: WorkflowRunFileV1[\"status\"];\n startedAt?: string;\n endedAt?: string;\n /** ISO timestamp derived from file mtime (best-effort) */\n updatedAt?: string;\n /** Absolute path to the run file on disk (server-side only) */\n path: string;\n};\n\nexport async function listAllWorkflowRuns(teamId: string): Promise<{ ok: true; dir: string; runs: WorkflowRunSummary[] }> {\n const teamDir = await getTeamWorkspaceDir(teamId);\n const dir = path.join(teamDir, RUNS_DIR);\n\n const runs: WorkflowRunSummary[] = [];\n\n // Preferred: runner directory-per-run layout:\n // shared-context/workflow-runs/<runId>/run.json\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n const runDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();\n\n for (const runId of runDirs) {\n const full = path.join(dir, runId, \"run.json\");\n try {\n const [raw, st] = await Promise.all([fs.readFile(full, \"utf8\"), fs.stat(full)]);\n const normalized = normalizeRunFile(teamId, \"(unknown)\", JSON.parse(raw) as unknown, runId);\n runs.push({\n workflowId: normalized.workflowId,\n runId: normalized.id,\n status: normalized.status,\n startedAt: normalized.startedAt,\n endedAt: normalized.endedAt,\n updatedAt: st.mtime ? new Date(st.mtime).toISOString() : undefined,\n path: full,\n });\n } catch {\n // ignore missing run.json/parse errors; keep going\n }\n }\n } catch (err: unknown) {\n if (!(err && typeof err === \"object\" && (err as { code?: unknown }).code === \"ENOENT\")) throw err;\n }\n\n // Legacy: flat run files at root: shared-context/workflow-runs/<runId>.run.json\n try {\n const entries = (await fs.readdir(dir)).filter((n) => n.endsWith(\".run.json\")).sort();\n for (const fileName of entries) {\n const runId = fileName.replace(/\\.run\\.json$/i, \"\");\n const full = path.join(dir, fileName);\n try {\n const [raw, st] = await Promise.all([fs.readFile(full, \"utf8\"), fs.stat(full)]);\n const normalized = normalizeRunFile(teamId, \"(unknown)\", JSON.parse(raw) as unknown, runId);\n runs.push({\n workflowId: normalized.workflowId,\n runId: normalized.id,\n status: normalized.status,\n startedAt: normalized.startedAt,\n endedAt: normalized.endedAt,\n updatedAt: st.mtime ? new Date(st.mtime).toISOString() : undefined,\n path: full,\n });\n } catch {\n runs.push({ workflowId: \"(unknown)\", runId, path: full });\n }\n }\n } catch (err: unknown) {\n if (!(err && typeof err === \"object\" && (err as { code?: unknown }).code === \"ENOENT\")) throw err;\n }\n\n // Back-compat: legacy per-workflow directory.\n if (LEGACY_PER_WORKFLOW_LAYOUT) {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n const wfDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();\n\n for (const workflowId of wfDirs) {\n const wfDir = path.join(dir, workflowId);\n let entries2: string[] = [];\n try {\n entries2 = (await fs.readdir(wfDir)).filter((n) => n.endsWith(\".run.json\")).sort();\n } catch (err: unknown) {\n if (err && typeof err === \"object\" && (err as { code?: unknown }).code === \"ENOENT\") continue;\n throw err;\n }\n\n for (const fileName of entries2) {\n const runId = fileName.replace(/\\.run\\.json$/i, \"\");\n const full = path.join(wfDir, fileName);\n try {\n const [raw, st] = await Promise.all([fs.readFile(full, \"utf8\"), fs.stat(full)]);\n const parsed = JSON.parse(raw) as Partial<WorkflowRunFileV1>;\n runs.push({\n workflowId,\n runId,\n status: parsed.status as WorkflowRunFileV1[\"status\"],\n startedAt: typeof parsed.startedAt === \"string\" ? parsed.startedAt : undefined,\n endedAt: typeof parsed.endedAt === \"string\" ? parsed.endedAt : undefined,\n updatedAt: st.mtime ? new Date(st.mtime).toISOString() : undefined,\n path: full,\n });\n } catch {\n runs.push({ workflowId, runId, path: full });\n }\n }\n }\n } catch (err: unknown) {\n if (!(err && typeof err === \"object\" && (err as { code?: unknown }).code === \"ENOENT\")) throw err;\n }\n }\n\n runs.sort((a, b) => String(b.updatedAt ?? b.startedAt ?? \"\").localeCompare(String(a.updatedAt ?? a.startedAt ?? \"\")));\n return { ok: true as const, dir, runs };\n}\n\nfunction normalizeRunFile(teamId: string, workflowId: string, parsed: unknown, runIdFallback: string): WorkflowRunFileV1 {\n // Kitchen schema\n if (parsed && typeof parsed === \"object\" && (parsed as { schema?: unknown }).schema === \"clawkitchen.workflow-run.v1\") {\n return parsed as WorkflowRunFileV1;\n }\n\n // Runner schema (RunLog from ClawRecipes). Convert to Kitchen's display schema.\n if (parsed && typeof parsed === \"object\" && \"runId\" in parsed && \"workflow\" in parsed) {\n const o = parsed as {\n runId?: unknown;\n createdAt?: unknown;\n updatedAt?: unknown;\n status?: unknown;\n workflow?: { id?: unknown; file?: unknown };\n nodeResults?: unknown;\n };\n\n const wfObj = o.workflow && typeof o.workflow === \"object\" ? o.workflow : {};\n const file = typeof wfObj.file === \"string\" ? wfObj.file : \"\";\n const inferredWfId = file ? path.basename(file).replace(/\\.workflow\\.json$/i, \"\") : \"\";\n const wfId = typeof wfObj.id === \"string\" && wfObj.id ? wfObj.id : inferredWfId || workflowId;\n\n const statusRaw = String(o.status ?? \"\");\n const status: WorkflowRunFileV1[\"status\"] =\n statusRaw === \"awaiting_approval\"\n ? \"waiting_for_approval\"\n : statusRaw === \"completed\"\n ? \"success\"\n : statusRaw === \"rejected\"\n ? \"error\"\n : statusRaw === \"error\"\n ? \"error\"\n : statusRaw === \"canceled\"\n ? \"canceled\"\n : \"running\";\n\n const nodes = Array.isArray(o.nodeResults)\n ? o.nodeResults\n .map((nr) => {\n if (!nr || typeof nr !== \"object\") return null;\n const r = nr as {\n nodeId?: unknown;\n status?: unknown;\n startedAt?: unknown;\n endedAt?: unknown;\n output?: unknown;\n error?: unknown;\n };\n const nodeId = typeof r.nodeId === \"string\" ? r.nodeId : \"\";\n if (!nodeId) return null;\n\n const st = String(r.status ?? \"pending\");\n const mapped =\n st === \"awaiting_approval\"\n ? \"waiting\"\n : st === \"completed\"\n ? \"success\"\n : st === \"error\"\n ? \"error\"\n : st;\n\n const allowed = [\"pending\", \"running\", \"waiting\", \"success\", \"error\", \"skipped\"] as const;\n const status = (allowed as readonly string[]).includes(mapped) ? (mapped as (typeof allowed)[number]) : \"pending\";\n\n const error =\n typeof r.error === \"string\"\n ? r.error\n : r.error && typeof r.error === \"object\" && \"message\" in r.error\n ? { message: String((r.error as { message?: unknown }).message ?? \"\") }\n : undefined;\n\n const startedAt = typeof r.startedAt === \"string\" ? r.startedAt : undefined;\n const endedAt = typeof r.endedAt === \"string\" ? r.endedAt : undefined;\n\n return {\n nodeId,\n status,\n ...(startedAt ? { startedAt } : {}),\n ...(endedAt ? { endedAt } : {}),\n ...(typeof r.output !== \"undefined\" ? { output: r.output } : {}),\n ...(typeof error !== \"undefined\" ? { error } : {}),\n } satisfies WorkflowRunNodeResultV1;\n })\n .filter((x): x is WorkflowRunNodeResultV1 => x !== null)\n : undefined;\n\n return {\n schema: \"clawkitchen.workflow-run.v1\",\n id: typeof o.runId === \"string\" ? o.runId : runIdFallback,\n workflowId: wfId,\n teamId,\n startedAt: typeof o.createdAt === \"string\" ? o.createdAt : new Date().toISOString(),\n endedAt:\n status === \"success\" || status === \"error\" || status === \"canceled\"\n ? typeof o.updatedAt === \"string\"\n ? o.updatedAt\n : undefined\n : undefined,\n status,\n summary: \"Executed by workflow runner\",\n nodes,\n };\n }\n\n // Unknown schema; keep a placeholder so UI can still render.\n return {\n schema: \"clawkitchen.workflow-run.v1\",\n id: runIdFallback,\n workflowId,\n teamId,\n startedAt: new Date().toISOString(),\n status: \"error\",\n summary: \"Malformed run file\",\n };\n}","import fs from \"node:fs/promises\";\n\n/** Reads dir with optional suffix filter and reverse sort. Returns { ok, dir, files } or { ok, dir, files: [] } on ENOENT. */\nexport async function readdirFiles(\n dir: string,\n suffix: string,\n reverse = false\n): Promise<{ ok: true; dir: string; files: string[] }> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n let files = entries\n .filter((e) => e.isFile() && e.name.endsWith(suffix))\n .map((e) => e.name)\n .sort();\n if (reverse) files = files.reverse();\n return { ok: true as const, dir, files };\n } catch (err: unknown) {\n if (err && typeof err === \"object\" && (err as { code?: unknown }).code === \"ENOENT\") {\n return { ok: true as const, dir, files: [] as string[] };\n }\n throw err;\n }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { getTeamWorkspaceDir } from \"@/lib/paths\";\nimport { readdirFiles } from \"@/lib/workflows/readdir\";\nimport type { WorkflowFileV1 } from \"@/lib/workflows/types\";\n\nconst WORKFLOWS_DIR = path.join(\"shared-context\", \"workflows\");\n\nexport function workflowFileName(workflowId: string) {\n return `${workflowId}.workflow.json`;\n}\n\nexport function assertSafeWorkflowId(workflowId: string) {\n const id = String(workflowId ?? \"\").trim();\n if (!id) throw new Error(\"workflow id is required\");\n if (!/^[a-z0-9][a-z0-9-]{0,62}$/.test(id)) {\n throw new Error(\n \"Invalid workflow id. Use lowercase letters, numbers, and dashes (max 63 chars), e.g. marketing-cadence-v1\"\n );\n }\n return id;\n}\n\nexport async function getTeamWorkflowsDir(teamId: string) {\n const teamDir = await getTeamWorkspaceDir(teamId);\n return path.join(teamDir, WORKFLOWS_DIR);\n}\n\nexport async function listWorkflows(teamId: string) {\n const dir = await getTeamWorkflowsDir(teamId);\n return readdirFiles(dir, \".workflow.json\");\n}\n\nexport async function readWorkflow(teamId: string, workflowId: string) {\n const id = assertSafeWorkflowId(workflowId);\n const dir = await getTeamWorkflowsDir(teamId);\n const p = path.join(dir, workflowFileName(id));\n const raw = await fs.readFile(p, \"utf8\");\n const parsed = JSON.parse(raw) as WorkflowFileV1;\n return { ok: true as const, path: p, workflow: parsed };\n}\n\nexport async function writeWorkflow(teamId: string, workflow: WorkflowFileV1) {\n const id = assertSafeWorkflowId(workflow.id);\n const dir = await getTeamWorkflowsDir(teamId);\n await fs.mkdir(dir, { recursive: true });\n const p = path.join(dir, workflowFileName(id));\n const toWrite: WorkflowFileV1 = {\n ...workflow,\n schema: \"clawkitchen.workflow.v1\",\n id,\n };\n await fs.writeFile(p, JSON.stringify(toWrite, null, 2) + \"\\n\", \"utf8\");\n return { ok: true as const, path: p };\n}\n\nexport async function deleteWorkflow(teamId: string, workflowId: string) {\n const id = assertSafeWorkflowId(workflowId);\n const dir = await getTeamWorkflowsDir(teamId);\n const p = path.join(dir, workflowFileName(id));\n try {\n await fs.unlink(p);\n } catch (err: unknown) {\n if (err && typeof err === \"object\" && (err as { code?: unknown }).code === \"ENOENT\") {\n return { ok: true as const, path: p, existed: false as const };\n }\n throw err;\n }\n return { ok: true as const, path: p, existed: true as const };\n}\n","// This file is generated by next-core EcmascriptClientReferenceModule.\nimport { registerClientReference } from \"react-server-dom-turbopack/server\";\nexport default registerClientReference(\n function() { throw new Error(\"Attempted to call the default export of [project]/src/app/teams/[teamId]/runs/[workflowId]/[runId]/run-detail-client.tsx from the server, but it's on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.\"); },\n \"[project]/src/app/teams/[teamId]/runs/[workflowId]/[runId]/run-detail-client.tsx\",\n \"default\",\n);\n","import Link from \"next/link\";\nimport { unstable_noStore as noStore } from \"next/cache\";\n\nimport { getTeamDisplayName } from \"@/lib/recipes\";\nimport { readWorkflowRun } from \"@/lib/workflows/runs-storage\";\nimport { readWorkflow } from \"@/lib/workflows/storage\";\nimport RunDetailClient from \"./run-detail-client\";\n\nexport const dynamic = \"force-dynamic\";\nexport const revalidate = 0;\n\nexport default async function TeamRunDetailPage({\n params,\n}: {\n params: Promise<{ teamId: string; workflowId: string; runId: string }>;\n}) {\n noStore();\n\n const { teamId, workflowId, runId } = await params;\n const name = await getTeamDisplayName(teamId);\n\n const [{ run }, workflowRes] = await Promise.all([\n readWorkflowRun(teamId, workflowId, runId),\n readWorkflow(teamId, workflowId).catch(() => null),\n ]);\n\n const wfName = workflowRes?.workflow?.name;\n\n return (\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div>\n <div className=\"text-xs text-[color:var(--ck-text-tertiary)]\">\n <Link href={`/runs?team=${encodeURIComponent(teamId)}`} className=\"hover:underline\">\n Runs\n </Link>\n <span className=\"mx-2\">/</span>\n <span className=\"font-mono\">{workflowId}</span>\n </div>\n <h1 className=\"mt-1 text-2xl font-semibold tracking-tight\">\n {wfName ? `${wfName} · ` : \"\"}\n <span className=\"font-mono\">{runId}</span>\n </h1>\n <div className=\"mt-1 text-sm text-[color:var(--ck-text-secondary)]\">\n {name || teamId} · <span className=\"font-mono\">{teamId}</span>\n </div>\n </div>\n\n <div className=\"flex items-center gap-3\">\n <Link\n href={`/teams/${encodeURIComponent(teamId)}/workflows/${encodeURIComponent(workflowId)}`}\n className=\"text-sm font-medium text-[color:var(--ck-text-secondary)] hover:underline\"\n >\n Workflow editor →\n </Link>\n </div>\n </div>\n\n <RunDetailClient run={run} />\n </div>\n );\n}\n"],"names":["LinkComponent","useLinkStatus","isModifiedEvent","event","eventTarget","currentTarget","target","getAttribute","metaKey","ctrlKey","shiftKey","altKey","nativeEvent","which","linkClicked","e","href","as","linkInstanceRef","replace","scroll","onNavigate","window","nodeName","isAnchorNodeName","toUpperCase","hasAttribute","isLocalURL","preventDefault","location","isDefaultPrevented","dispatchNavigateAction","require","React","startTransition","current","formatStringOrUrl","urlObjOrString","formatUrl","props","linkStatus","setOptimisticLinkStatus","useOptimistic","IDLE_LINK_STATUS","children","useRef","hrefProp","asProp","childrenProp","prefetch","prefetchProp","passHref","shallow","onClick","onMouseEnter","onMouseEnterProp","onTouchStart","onTouchStartProp","legacyBehavior","ref","forwardedRef","unstable_dynamicOnHover","restProps","a","router","useContext","AppRouterContext","prefetchEnabled","fetchStrategy","getFetchStrategyFromPrefetchProp","FetchStrategy","PPR","process","env","NODE_ENV","createPropError","args","Error","key","expected","actual","requiredPropsGuard","requiredProps","Object","keys","forEach","_","optionalPropsGuard","optionalProps","valType","locale","warnOnce","pathname","hasDynamicSegment","split","some","segment","startsWith","endsWith","useMemo","resolvedHref","child","$$typeof","Symbol","for","console","warn","Children","only","err","type","childRef","observeLinkVisibilityOnMount","useCallback","element","mountLinkInstance","unmountLinkForCurrentNavigation","unmountPrefetchableInstance","mergedRef","useMergedRef","childProps","defaultPrevented","upgradeToDynamicPrefetch","onNavigationIntent","__NEXT_LINK_NO_TOUCH_START","undefined","isAbsoluteUrl","addBasePath","link","errorOnce","cloneElement","LinkStatusContext","Provider","value","createContext","__NEXT_CACHE_COMPONENTS","Full","isLegacyBehavior","childIsHostComponent","childIsClientComponent","error","ClientLinkComponent"],"mappings":"6yBACA,GAAM,yBAAE,CAAuB,CAAE,CAAA,EAAA,CAAA,CAAA,OAEjC,EAAsB,CAAC,CAAC,EAAwB,iGAFhD,GAAM,yBAAE,CAAuB,CAAE,CAAA,EAAA,CAAA,CAAA,OAEjC,EAAsB,CAAC,CAAC,EAAwB,2MEAhD,OAyBC,CAAA,kBAzBuBA,GA2BCC,aAAa,CAAA,kBAAbA,EAAAA,aAAa,2GA7B6B,CAAA,CAAA,IAAA,IAEpD,SAASD,EACtBuC,CAAiD,EAEjD,IAAMkG,EAAmBlG,EAAMmB,cAAc,CACvCgF,EACJ,AAA0B,iBAAnBnG,EAAMK,QAAQ,EACK,UAA1B,OAAOL,EAAMK,QAAQ,EACoB,UAAzC,OAAQL,EAAMK,QAAQ,EAAUkE,KAC5B6B,EACHpG,EAAMK,QAAQ,EAAUkE,MAAMR,WAC/BC,OAAOC,GAAG,CAAC,0BAcb,OAZIiC,AAYJ,GAZyBC,GAAyBC,IAC3CpG,CAWP,CAXaK,QADW,AACH,EAAUkE,KADkB,CACZR,EADqC,SACxBC,OAAOC,GAAG,CAAC,cACzDC,CADwE,OAChEmC,KAAK,CACX,CAAC,8NAGHnC,CAHkO,CAAC,MAG3NmC,KAAK,CACX,CAAC,2MAA2M,AAK3M,CAL4M,AAK5M,EAAA,EAAA,GAAA,EAACC,EAAAA,OAAmB,CAAA,CAAE,GAAGtG,CAAK,EACvC,gPE3BA,IAAA,EAAA,EAAA,CAAA,CAAA,OAGA,IAAM,EAAgB,CAAA,EAFtB,AAEsB,EAFtB,CAAA,CAAA,OAEsB,SAAA,AAAS,EAAC,EAAA,QAAQ,EASxC,SAAS,EAAc,CAAyB,QAC9C,AAA0B,UAAtB,AAAgC,OAAzB,EAAI,MAAM,CAAsB,EAAI,MAAM,CACjD,EAAI,MAAM,EAA0B,UAAtB,OAAO,EAAI,MAAM,EAAiB,aAAc,EAAI,MAAM,CACnE,CADqE,MAC7D,EAAI,MAAM,CAAgC,QAAQ,IAE5D,EACT,CASA,SAAS,EAAc,CAA4C,CAAE,CAAiB,QACpF,AAA0B,UAAtB,AAAgC,OAAzB,EAAI,MAAM,CAAsB,EAAI,MAAM,CACjD,EAAI,MAAM,EAA0B,UAAtB,OAAO,EAAI,MAAM,EAAiB,aAAc,EAAI,MAAM,CACnE,CADqE,MAC7D,EAAI,MAAM,CAAgC,QAAQ,IAExC,UAAvB,AAAiC,OAA1B,EAAI,OAAO,CAAsB,EAAI,OAAO,CAChD,OAAO,EAChB,CAEA,eAAe,EAAiB,CAAc,EAC5C,GAAI,CACF,IAAM,EAAM,MAAM,EAAc,WAAY,EAAM,CAChD,QAAS,KACT,aAAa,EACb,UAAW,KAAK,GAClB,GAEA,CAHyB,KAGlB,CACL,IAAI,EACJ,SAAU,EACV,OAAQ,OAAO,EAAI,MAAM,EAAI,IAC7B,OAAQ,OAAO,EAAI,MAAM,EAAI,GAC/B,CACF,CAAE,MAAO,EAAY,CAEnB,IAAM,EAA+B,UAApB,OADL,AACY,EAAI,IAAI,CAAgB,EAAI,IAAI,CAAG,EAG3D,MAAO,CAAE,GAAI,YAAO,EAAU,OAFf,KAEuB,OADvB,EADc,EACK,EACW,CAC/C,CACF,CAEO,KAL0B,UAKX,EAAY,CAAc,EAO9C,GAAgB,SAAZ,CAAI,CAAC,EAAE,CAAa,OAAO,EAAiB,GAEhD,IAAM,ED3DD,AC2DO,SD3DE,EACd,IAAM,EAAO,WAA6D,iBAAiB,CAC3F,GAAI,CAAC,EACH,GADQ,GACF,AAAI,MACR,gIAGJ,OAAO,CACT,ICoDE,GAAI,CACF,IAAM,EAAO,MAAM,EAAI,OAAO,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC,cAAe,EAAK,CAAE,CAAE,UAAW,IAAO,GAQjG,EAAS,OAAO,EAAI,MAAM,EAAI,IAC9B,EAAS,OAAO,EAAI,MAAM,EAAI,IAC9B,EA3DoB,AAA5B,SA2DmB,CA3DmB,AAAlC,OAAO,EAAI,QAAQ,CA2DY,AA3DU,EAAI,QAAQ,CACjC,UAApB,AAA8B,OAAvB,EAAI,IAAI,CAAsB,EAAI,IAAI,CACvB,UAAtB,AAAgC,OAAzB,EAAI,MAAM,CAAsB,EAAI,MAAM,CAC9C,EA0DL,GAAiB,IAAb,EAAgB,MAAO,CAAE,IAAI,WAAO,SAAU,SAAQ,CAAO,EACjE,MAAO,CAAE,IAAI,EAAM,SAAU,SAAG,SAAQ,CAAO,CACjD,CAAE,MAAO,EAAY,CAEnB,IAAM,EAAW,AAAoB,iBAAb,EAAI,IAAI,CAAgB,EAAI,IAAI,CAAG,EAG3D,MAAO,CAAE,IAAI,WAAO,EAAU,OAFf,EAFH,GAI0B,OADvB,EADc,EACK,EACW,CAC/C,CACF,MAHiC,0DCzFjC,IAAA,EAAA,EAAA,CAAA,CAAA,OACA,EAAA,EAAA,CAAA,CAAA,OACA,EAAA,EAAA,CAAA,CAAA,OEIA,IAAM,EAAgB,EAAA,OAAI,CAAC,IAAI,CAAC,iBAAkB,aAM3C,SAAS,EAAqB,CAAkB,EACrD,IAAM,EAAK,OAAO,GAAc,IAAI,IAAI,GACxC,GAAI,CAAC,EAAI,MAAM,AAAI,MAAM,2BACzB,GAAI,CAAC,4BAA4B,IAAI,CAAC,GACpC,EADyC,IACnC,AAAI,MACR,6GAGJ,OAAO,CACT,CAEO,eAAe,EAAoB,CAAc,EACtD,IAAM,EAAU,MAAM,CAAA,EAAA,EAAA,mBAAA,AAAmB,EAAC,GAC1C,OAAO,EAAA,OAAI,CAAC,IAAI,CAAC,EAAS,EAC5B,CAOO,eAAe,EAAa,CAAc,CAAE,CAAkB,EACnE,IAAM,EAAK,EAAqB,GAC1B,EAAM,MAAM,EAAoB,GAChC,EAAI,EAAA,OAAI,CAAC,IAAI,CAAC,EA3Bb,CAAA,EA2BkB,AAAiB,AA3BhC,EAAW,cAAc,CAAC,EA6B9B,EAAS,KAAK,KAAK,CADb,AACc,MADR,EAAA,OAAE,CAAC,QAAQ,CAAC,EAAG,SAEjC,MAAO,CAAE,GAAI,GAAe,KAAM,EAAG,SAAU,CAAO,CACxD,gEFjCA,IAAM,EAAW,EAAA,OAAI,CAAC,IAAI,CAAC,iBAAkB,iBAsBtC,eAAe,EAAmB,CAAc,CAAE,CAAkB,EAEpE,EAAqB,GAC1B,IAAM,EAAU,MAAM,CAAA,EAAA,EAAA,mBAAA,AAAmB,EAAC,GAC1C,OAAO,EAAA,OAAI,CAAC,IAAI,CAAC,EAAS,EAC5B,CAEO,SAAS,EAAoB,CAAa,EAC/C,MAAO,CAAA,EAAG,EAAM,SAAS,CAAC,AAC5B,CA6DO,eAAe,EAAgB,CAAc,CAAE,CAAkB,CAAE,CAAa,EACrF,IAAM,EAAO,EAAqB,GAC5B,EAAM,AArFP,SAAS,AAAgB,CAAa,EAC3C,IAAM,EAAK,OAAO,GAAS,IAAI,IAAI,GACnC,GAAI,CAAC,EAAI,MAAM,AAAI,MAAM,sBAIzB,GAAI,CAAC,mCAAmC,IAAI,CAAC,GAC3C,EADgD,IACtC,AAAJ,MAAU,qDAGlB,OAAO,CACT,EA0E8B,GACtB,EAAM,MAAM,EAAmB,EAAQ,GAGvC,EAAU,EAAA,OAAI,CAAC,IAAI,CAAC,EAAK,EAAK,YACpC,GAAI,CACF,IAAM,EAAM,MAAM,EAAA,OAAE,CAAC,QAAQ,CAAC,EAAS,QACjC,EAAS,KAAK,KAAK,CAAC,GACpB,EAAM,EAAiB,EAAQ,EAAM,EAAQ,GAKnD,GAAI,MAAM,OAAO,CAAC,EAAI,KAAK,GAAK,EAAI,KAAK,CAAC,MAAM,CAAE,CAChD,IAAM,EAAa,EAAA,OAAI,CAAC,IAAI,CAAC,EAAK,EAAK,gBACnC,EAAkB,EAAE,CACxB,GAAI,CACF,EAAQ,MAAM,EAAA,OAAE,CAAC,OAAO,CAAC,EAC3B,CAAE,KAAM,CACN,EAAQ,EAAE,AACZ,CAOA,IAAM,EAAW,IAAI,IACrB,IAAK,IAAM,KAAK,EAAM,KAAK,GAAG,IAAI,GAAI,CACpC,IAAM,EAAI,EAAE,KAAK,CAAC,mBAClB,GAAI,CAAC,EAAG,SACR,IAAM,EAAS,CAAC,CAAC,EAAE,CACnB,GAAI,CAAC,EAAQ,SACb,IAAM,EAAW,EAAS,GAAG,CAAC,IAAW,EAAE,CAC3C,EAAS,IAAI,CAAC,EAAA,OAAI,CAAC,IAAI,CAAC,EAAY,IACpC,EAAS,GAAG,CAAC,EAAQ,EACvB,CAEA,MAAM,QAAQ,GAAG,CACf,EAAI,KAAK,CAAC,GAAG,CAAC,MAAO,IACnB,GAAI,CAAC,GAAkB,UAAb,OAAO,GAEb,KAAoB,IAAb,EAAE,MAAM,CAAkB,AAFJ,OAIjC,IAAM,EAAM,EAAS,GAAG,CAAC,EAAE,MAAM,EACjC,GAAI,CAAC,GAAO,CAAC,EAAI,MAAM,CAAE,OAEzB,IAAM,EAAO,EAAI,KAAK,GACtB,GAAK,CAAD,CAEJ,GAAI,CAFO,AAGT,IAAM,EAAS,MAAM,EAAA,OAAE,CAAC,QAAQ,CAAC,EAAM,QACvC,EAAE,MAAM,CAAG,KAAK,KAAK,CAAC,EACxB,CAAE,KAAM,CAER,CACF,GAEJ,CAEA,MAAO,CAAE,IAAI,EAAe,KAAM,MAAS,CAAI,CACjD,CAAE,MAAO,EAAc,CAErB,GAAI,CAAC,CAAC,GAAsB,UAAf,OAAO,GAAyD,WAApC,EAA2B,IAAS,AAAL,CAAa,CAAG,MAAM,CAChG,CAGA,IAAM,EAAK,EAAA,OAAI,CAAC,IAAI,CAAC,EAAK,EAAoB,IAC9C,GAAI,CACF,IAAM,EAAM,MAAM,EAAA,OAAE,CAAC,QAAQ,CAAC,EAAI,QAC5B,EAAS,KAAK,KAAK,CAAC,GAC1B,MAAO,CAAE,IAAI,EAAe,KAAM,EAAI,IAAK,EAAiB,EAAQ,EAAM,EAAQ,EAAK,CACzF,CAAE,MAAO,EAAc,CACrB,GAAI,AAA8B,GAAO,AAAe,iBAAR,GAAyD,WAApC,EAA2B,IAAI,CAAe,CACjH,IAAM,EAAS,EAAA,OAAI,CAAC,IAAI,CAAC,EAAK,EAAM,EAAoB,IAElD,EAAS,KAAK,KAAK,CADb,AACc,MADR,EAAA,OAAE,CAAC,QAAQ,CAAC,EAAQ,SAEtC,MAAO,CAAE,GAAI,GAAe,KAAM,EAAQ,IAAK,EAAiB,EAAQ,EAAM,EAAQ,EAAK,CAC7F,CACA,MAAM,CACR,CACF,CA0DO,eAAe,EAAoB,CAAc,EACtD,IAAM,EAAU,MAAM,CAAA,EAAA,EAAA,mBAAA,AAAmB,EAAC,GACpC,EAAM,EAAA,OAAI,CAAC,IAAI,CAAC,EAAS,GAEzB,EAA6B,EAAE,CAIrC,GAAI,CAIF,IAAK,IAAM,IAFK,CADA,IAGI,EAHE,EAAA,OAAE,CAAC,OAAO,CAAC,EAAK,CAAE,eAAe,CAAK,EAAA,EACpC,MAAM,CAAE,AAAD,GAAO,EAAE,WAAW,IAAI,GAAG,CAAC,AAAC,GAAM,EAAE,IAAI,EAAE,IAAI,GAEjD,CAC3B,IAAM,EAAO,EAAA,OAAI,CAAC,IAAI,CAAC,EAAK,EAAO,YACnC,GAAI,CACF,GAAM,CAAC,EAAK,EAAG,CAAG,MAAM,QAAQ,GAAG,CAAC,CAAC,EAAA,OAAE,CAAC,QAAQ,CAAC,EAAM,QAAS,EAAA,OAAE,CAAC,IAAI,CAAC,GAAM,EACxE,EAAa,EAAiB,EAAQ,YAAa,KAAK,KAAK,CAAC,GAAiB,GACrF,EAAK,IAAI,CAAC,CACR,WAAY,EAAW,UAAU,CACjC,MAAO,EAAW,EAAE,CACpB,OAAQ,EAAW,MAAM,CACzB,UAAW,EAAW,SAAS,CAC/B,QAAS,EAAW,OAAO,CAC3B,UAAW,EAAG,KAAK,CAAG,IAAI,KAAK,EAAG,KAAK,EAAE,WAAW,QAAK,EACzD,KAAM,CACR,EACF,CAAE,KAAM,CAER,CACF,CACF,CAAE,MAAO,EAAc,CACrB,GAAI,CAAC,CAAC,GAAsB,UAAf,OAAO,GAAyD,WAApC,EAA2B,IAAI,AAAK,CAAQ,CAAG,MAAM,CAChG,CAGA,GAAI,CAEF,IAAK,IAAM,IADK,CAAC,MAAM,CACA,CADA,OAAE,CAAC,OAAO,CAAC,EAAA,CAAI,CAAE,MAAM,CAAC,AAAC,GAAM,EAAE,QAAQ,CAAC,cAAc,IAAI,GACnD,CAC9B,IAAM,EAAQ,EAAS,OAAO,CAAC,gBAAiB,IAC1C,EAAO,EAAA,OAAI,CAAC,IAAI,CAAC,EAAK,GAC5B,GAAI,CACF,GAAM,CAAC,EAAK,EAAG,CAAG,MAAM,QAAQ,GAAG,CAAC,CAAC,EAAA,OAAE,CAAC,QAAQ,CAAC,EAAM,QAAS,EAAA,OAAE,CAAC,IAAI,CAAC,GAAM,EACxE,EAAa,EAAiB,EAAQ,YAAa,KAAK,KAAK,CAAC,GAAiB,GACrF,EAAK,IAAI,CAAC,CACR,WAAY,EAAW,UAAU,CACjC,MAAO,EAAW,EAAE,CACpB,OAAQ,EAAW,MAAM,CACzB,UAAW,EAAW,SAAS,CAC/B,QAAS,EAAW,OAAO,CAC3B,UAAW,EAAG,KAAK,CAAG,IAAI,KAAK,EAAG,KAAK,EAAE,WAAW,QAAK,EACzD,KAAM,CACR,EACF,CAAE,KAAM,CACN,EAAK,IAAI,CAAC,CAAE,WAAY,kBAAa,EAAO,KAAM,CAAK,EACzD,CACF,CACF,CAAE,MAAO,EAAc,CACrB,GAAI,CAAC,CAAC,GAAsB,UAAf,OAAO,GAAyD,WAApC,EAA2B,IAAI,AAAK,CAAQ,CAAG,MAAM,CAChG,CAIE,GAAI,CAIF,IAAK,IAAM,IAFI,CADC,MAAM,EAAA,CAGG,MAHD,CAAC,OAAO,CAAC,EAAK,CAAE,eAAe,CAAK,EAAA,EACrC,MAAM,CAAC,AAAC,GAAM,EAAE,WAAW,IAAI,GAAG,CAAE,AAAD,GAAO,EAAE,IAAI,EAAE,IAAI,GAE5C,CAC/B,IAAM,EAAQ,EAAA,OAAI,CAAC,IAAI,CAAC,EAAK,GACzB,EAAqB,EAAE,CAC3B,GAAI,CACF,EAAW,AAAC,OAAM,EAAA,OAAE,CAAC,OAAO,CAAC,EAAA,CAAM,CAAE,MAAM,CAAE,AAAD,GAAO,EAAE,QAAQ,CAAC,cAAc,IAAI,EAClF,CAAE,MAAO,EAAc,CACrB,GAAI,GAAsB,UAAf,OAAO,GAAqB,AAAoC,aAAT,IAAI,CAAe,QACrF,OAAM,CACR,CAEA,IAAK,IAAM,KAAY,EAAU,CAC/B,IAAM,EAAQ,EAAS,OAAO,CAAC,gBAAiB,IAC1C,EAAO,EAAA,OAAI,CAAC,IAAI,CAAC,EAAO,GAC9B,GAAI,CACF,GAAM,CAAC,EAAK,EAAG,CAAG,MAAM,QAAQ,GAAG,CAAC,CAAC,EAAA,OAAE,CAAC,QAAQ,CAAC,EAAM,QAAS,EAAA,OAAE,CAAC,IAAI,CAAC,GAAM,EACxE,EAAS,KAAK,KAAK,CAAC,GAC1B,EAAK,IAAI,CAAC,YACR,QACA,EACA,OAAQ,EAAO,MAAM,CACrB,UAAW,AAA4B,iBAArB,EAAO,SAAS,CAAgB,EAAO,SAAS,MAAG,EACrE,QAAmC,UAA1B,OAAO,EAAO,OAAO,CAAgB,EAAO,OAAO,MAAG,EAC/D,UAAW,EAAG,KAAK,CAAG,IAAI,KAAK,EAAG,KAAK,EAAE,WAAW,QAAK,EACzD,KAAM,CACR,EACF,CAAE,KAAM,CACN,EAAK,IAAI,CAAC,YAAE,QAAY,EAAO,KAAM,CAAK,EAC5C,CACF,CACF,CACF,CAAE,MAAO,EAAc,CACrB,GAAI,CAAC,CAAC,GAAsB,UAAf,OAAO,GAAyD,WAApC,EAA2B,IAAS,AAAL,CAAa,CAAG,MAAM,CAChG,CAIF,OADA,EAAK,IAAI,CAAC,CAAC,EAAG,IAAM,OAAO,EAAE,SAAS,EAAI,EAAE,SAAS,EAAI,IAAI,aAAa,CAAC,OAAO,EAAE,SAAS,EAAI,EAAE,SAAS,EAAI,MACzG,CAAE,IAAI,MAAe,OAAK,CAAK,CACxC,CAEA,SAAS,EAAiB,CAAc,CAAE,CAAkB,CAAE,CAAe,CAAE,CAAqB,EAElG,GAAI,GAA4B,UAAlB,OAAO,GAAmE,+BAA+B,CAA1E,EAAgC,MAAM,CACjF,OAAO,EAIT,GAAI,GAA4B,UAAlB,OAAO,GAAuB,UAAW,GAAU,aAAc,EAAQ,CAUrF,IAAM,EAAQ,EAAE,QAAQ,EAA0B,UAAtB,OAAO,EAAE,QAAQ,CAAgB,EAAE,QAAQ,CAAG,CAAC,EACrE,EAAO,AAAsB,iBAAf,EAAM,IAAI,CAAgB,EAAM,IAAI,CAAG,GACrD,EAAe,EAAO,EAAA,OAAI,CAAC,QAAQ,CAAC,GAAM,OAAO,CAAC,qBAAsB,IAAM,GAC9E,EAA2B,UAApB,OAAO,EAAM,EAAE,EAAiB,EAAM,EAAE,CAAG,EAAM,EAAE,CAAG,GAAgB,EAE7E,EAAY,OAAO,EAAE,MAAM,EAAI,IAC/B,EACU,sBAAd,EACI,uBACc,cAAd,EACE,UACc,aACZ,AADF,GAEgB,UAAd,EACE,QACc,aAAd,EACE,WACA,UAER,EAAQ,MAAM,OAAO,CAAC,EAAE,WAAW,EACrC,EAAE,WAAW,CACV,GAAG,CAAC,AAAC,IACJ,GAAI,CAAC,GAAoB,UAAd,OAAO,EAAiB,OAAO,KAS1C,IAAM,EAA6B,UAApB,OARL,AAQY,EAAE,MAAM,CAAgB,EAAE,MAAM,CAAG,GACzD,GAAI,CAAC,EAAQ,OAAO,KAEpB,IAAM,EAAK,OAAO,EAAE,MAAM,EAAI,WACxB,EACG,sBAAP,EACI,UACO,cAAP,EACE,UACO,UAAP,EACE,QACA,EAGJ,EADU,AACA,CADC,UAAW,UAAW,UAAW,UAAW,QAAS,UAAU,CAClC,QAAQ,CAAC,GAAW,EAAsC,UAElG,EACe,UAAnB,OAAO,EAAE,KAAK,CACV,EAAE,KAAK,CACP,EAAE,KAAK,EAAuB,UAAnB,OAAO,EAAE,KAAK,EAAiB,YAAa,EAAE,KAAK,CAC5D,CAAE,QAAS,OAAQ,EAAE,KAAK,CAA2B,OAAO,EAAI,GAAI,OACpE,EAEF,EAAmC,UAAvB,OAAO,EAAE,SAAS,CAAgB,EAAE,SAAS,MAAG,EAC5D,EAA+B,UAArB,OAAO,EAAE,OAAO,CAAgB,EAAE,OAAO,MAAG,EAE5D,MAAO,QACL,SACA,EACA,GAAI,EAAY,WAAE,CAAU,EAAI,CAAC,CAAC,CAClC,GAAI,EAAU,SAAE,CAAQ,EAAI,CAAC,CAAC,CAC9B,GAAI,AAAoB,SAAb,EAAE,MAAM,CAAmB,CAAE,OAAQ,EAAE,MAAM,AAAC,EAAI,CAAC,CAAC,CAC/D,GAAI,KAAiB,IAAV,EAAwB,CAAE,OAAM,EAAI,CAAC,CAAC,AACnD,CACF,GACC,MAAM,CAAC,AAAC,GAA0C,OAAN,QAC/C,EAEJ,MAAO,CACL,OAAQ,8BACR,GAAuB,UAAnB,OAAO,EAAE,KAAK,CAAgB,EAAE,KAAK,CAAG,EAC5C,WAAY,SACZ,EACA,UAAkC,UAAvB,OAAO,EAAE,SAAS,CAAgB,EAAE,SAAS,CAAG,IAAI,OAAO,WAAW,GACjF,OAAA,EACa,YAAX,GAAmC,UAAX,GAAiC,aAAX,CAAW,GAC9B,UAAvB,OAAO,EAAE,SAAS,CAChB,AAvFA,EAuFE,SAAS,MACX,SAER,EACA,CAFM,OAEG,oCACT,CACF,CACF,CAGA,MAAO,CACL,OAAQ,8BACR,GAAI,aACJ,EACA,SACA,UAAW,IAAI,OAAO,WAAW,GACjC,OAAQ,QACR,QAAS,oBACX,CACF,uHGzce,CAAA,EADf,AACe,EADf,CAAA,CAAA,OACe,uBAAA,AAAuB,EAClC,WAAa,MAAU,AAAJ,MAAU,wUAA0U,EACvW,uGACA,+DAHW,CAAA,EADf,AACe,EADf,CAAA,CAAA,OACe,uBAAA,AAAuB,EAClC,WAAa,MAAM,AAAI,MAAM,oTAAsT,EACnV,mFACA,4GCLJ,EAAA,EAAA,CAAA,CAAA,OACA,EAAA,EAAA,CAAA,CAAA,OAEA,EAAA,EAAA,CAAA,CAAA,OACA,EAAA,EAAA,CAAA,CAAA,OACA,EAAA,EAAA,CAAA,CAAA,OACA,EAAA,EAAA,CAAA,CAAA,OAKe,eAAe,EAAkB,QAC9C,CAAM,CAGP,EACC,CAAA,EAAA,EAAA,gBAAA,AAAO,IAEP,GAAM,QAAE,CAAM,YAAE,CAAU,OAAE,CAAK,CAAE,CAAG,MAAM,EACtC,EAAO,MAAM,CAAA,EAAA,EAAA,kBAAkB,AAAlB,EAAmB,GAEhC,CAAC,KAAE,CAAG,CAAE,CAAE,EAAY,CAAG,MAAM,QAAQ,GAAG,CAAC,CAC/C,CAAA,EAAA,EAAA,eAAA,AAAe,EAAC,EAAQ,EAAY,GACpC,CAAA,EAAA,EAAA,YAAA,AAAY,EAAC,EAAQ,GAAY,KAAK,CAAC,IAAM,MAC9C,EAEK,EAAS,GAAa,UAAU,KAEtC,MACE,CAAA,EAAA,EAAA,IAAA,EAAC,MAAA,CAAI,UAAU,gCACb,CAAA,EAAA,EAAA,IAAA,EAAC,MAAA,CAAI,UAAU,6DACb,CAAA,EAAA,EAAA,IAAA,EAAC,MAAA,WACC,CAAA,EAAA,EAAA,IAAA,EAAC,MAAA,CAAI,UAAU,yDACb,CAAA,EAAA,EAAA,GAAA,EAAC,EAAA,OAAI,CAAA,CAAC,KAAM,CAAC,WAAW,EAAE,mBAAmB,GAAA,CAAS,CAAE,UAAU,2BAAkB,SAGpF,CAAA,EAAA,EAAA,GAAA,EAAC,OAAA,CAAK,UAAU,gBAAO,MACvB,CAAA,EAAA,EAAA,GAAA,EAAC,OAAA,CAAK,UAAU,qBAAa,OAE/B,CAAA,EAAA,EAAA,IAAA,EAAC,KAAA,CAAG,UAAU,uDACX,EAAS,CAAA,EAAG,EAAO,MAAG,CAAC,CAAG,GAC3B,CAAA,EAAA,EAAA,GAAA,EAAC,OAAA,CAAK,UAAU,qBAAa,OAE/B,CAAA,EAAA,EAAA,IAAA,EAAC,MAAA,CAAI,UAAU,+DACZ,GAAQ,EAAO,MAAG,CAAA,EAAA,EAAA,GAAA,EAAC,OAAA,CAAK,UAAU,qBAAa,UAIpD,CAAA,EAAA,EAAA,GAAA,EAAC,MAAA,CAAI,UAAU,mCACb,CAAA,EAAA,EAAA,GAAA,EAAC,EAAA,OAAI,CAAA,CACH,KAAM,CAAC,OAAO,EAAE,mBAAmB,GAAQ,WAAW,EAAE,mBAAmB,GAAA,CAAa,CACxF,UAAU,qFACX,2BAML,CAAA,EAAA,EAAA,GAAA,EAAC,EAAA,OAAe,CAAA,CAAC,IAAK,MAG5B,kCArDuB,+BACG","ignoreList":[0,2,8]}
|
|
1
|
+
{"version":3,"sources":["../../../../node_modules/next/dist/client/app-dir/link.js/__nextjs-internal-proxy.cjs","../../../../node_modules/next/src/client/app-dir/link.tsx","../../../../node_modules/next/src/client/app-dir/link.react-server.tsx","../../../../src/lib/kitchen-api.ts","../../../../src/lib/openclaw.ts","../../../../src/lib/workflows/runs-storage.ts","../../../../src/lib/workflows/readdir.ts","../../../../src/lib/workflows/storage.ts","../../../../src/app/teams/%5BteamId%5D/runs/%5BworkflowId%5D/%5BrunId%5D/run-detail-client.tsx/__nextjs-internal-proxy.mjs","../../../../src/app/teams/%5BteamId%5D/runs/%5BworkflowId%5D/%5BrunId%5D/page.tsx"],"sourcesContent":["// This file is generated by next-core EcmascriptClientReferenceModule.\nconst { createClientModuleProxy } = require(\"react-server-dom-turbopack/server\");\n\n__turbopack_context__.n(createClientModuleProxy(\"[project]/node_modules/next/dist/client/app-dir/link.js\"));\n","'use client'\n\nimport React, { createContext, useContext, useOptimistic, useRef } from 'react'\nimport type { UrlObject } from 'url'\nimport { formatUrl } from '../../shared/lib/router/utils/format-url'\nimport { AppRouterContext } from '../../shared/lib/app-router-context.shared-runtime'\nimport { useMergedRef } from '../use-merged-ref'\nimport { isAbsoluteUrl } from '../../shared/lib/utils'\nimport { addBasePath } from '../add-base-path'\nimport { warnOnce } from '../../shared/lib/utils/warn-once'\nimport type { PENDING_LINK_STATUS } from '../components/links'\nimport {\n IDLE_LINK_STATUS,\n mountLinkInstance,\n onNavigationIntent,\n unmountLinkForCurrentNavigation,\n unmountPrefetchableInstance,\n type LinkInstance,\n} from '../components/links'\nimport { isLocalURL } from '../../shared/lib/router/utils/is-local-url'\nimport {\n FetchStrategy,\n type PrefetchTaskFetchStrategy,\n} from '../components/segment-cache/types'\nimport { errorOnce } from '../../shared/lib/utils/error-once'\n\ntype Url = string | UrlObject\ntype RequiredKeys<T> = {\n [K in keyof T]-?: {} extends Pick<T, K> ? never : K\n}[keyof T]\ntype OptionalKeys<T> = {\n [K in keyof T]-?: {} extends Pick<T, K> ? K : never\n}[keyof T]\n\ntype OnNavigateEventHandler = (event: { preventDefault: () => void }) => void\n\ntype InternalLinkProps = {\n /**\n * **Required**. The path or URL to navigate to. It can also be an object (similar to `URL`).\n *\n * @example\n * ```tsx\n * // Navigate to /dashboard:\n * <Link href=\"/dashboard\">Dashboard</Link>\n *\n * // Navigate to /about?name=test:\n * <Link href={{ pathname: '/about', query: { name: 'test' } }}>\n * About\n * </Link>\n * ```\n *\n * @remarks\n * - For external URLs, use a fully qualified URL such as `https://...`.\n * - In the App Router, dynamic routes must not include bracketed segments in `href`.\n */\n href: Url\n\n /**\n * @deprecated v10.0.0: `href` props pointing to a dynamic route are\n * automatically resolved and no longer require the `as` prop.\n */\n as?: Url\n\n /**\n * Replace the current `history` state instead of adding a new URL into the stack.\n *\n * @defaultValue `false`\n *\n * @example\n * ```tsx\n * <Link href=\"/about\" replace>\n * About (replaces the history state)\n * </Link>\n * ```\n */\n replace?: boolean\n\n /**\n * Whether to override the default scroll behavior. If `true`, Next.js attempts to maintain\n * the scroll position if the newly navigated page is still visible. If not, it scrolls to the top.\n *\n * If `false`, Next.js will not modify the scroll behavior at all.\n *\n * @defaultValue `true`\n *\n * @example\n * ```tsx\n * <Link href=\"/dashboard\" scroll={false}>\n * No auto scroll\n * </Link>\n * ```\n */\n scroll?: boolean\n\n /**\n * Update the path of the current page without rerunning data fetching methods\n * like `getStaticProps`, `getServerSideProps`, or `getInitialProps`.\n *\n * @remarks\n * `shallow` only applies to the Pages Router. For the App Router, see the\n * [following documentation](https://nextjs.org/docs/app/building-your-application/routing/linking-and-navigating#using-the-native-history-api).\n *\n * @defaultValue `false`\n *\n * @example\n * ```tsx\n * <Link href=\"/blog\" shallow>\n * Shallow navigation\n * </Link>\n * ```\n */\n shallow?: boolean\n\n /**\n * Forces `Link` to pass its `href` to the child component. Useful if the child is a custom\n * component that wraps an `<a>` tag, or if you're using certain styling libraries.\n *\n * @defaultValue `false`\n *\n * @example\n * ```tsx\n * <Link href=\"/dashboard\" passHref legacyBehavior>\n * <MyStyledAnchor>Dashboard</MyStyledAnchor>\n * </Link>\n * ```\n */\n passHref?: boolean\n\n /**\n * Prefetch the page in the background.\n * Any `<Link />` that is in the viewport (initially or through scroll) will be prefetched.\n * Prefetch can be disabled by passing `prefetch={false}`.\n *\n * @remarks\n * Prefetching is only enabled in production.\n *\n * - In the **App Router**:\n * - `\"auto\"`, `null`, `undefined` (default): Prefetch behavior depends on static vs dynamic routes:\n * - Static routes: fully prefetched\n * - Dynamic routes: partial prefetch to the nearest segment with a `loading.js`\n * - `true`: Always prefetch the full route and data.\n * - `false`: Disable prefetching on both viewport and hover.\n * - In the **Pages Router**:\n * - `true` (default): Prefetches the route and data in the background on viewport or hover.\n * - `false`: Prefetch only on hover, not on viewport.\n *\n * @defaultValue `true` (Pages Router) or `null` (App Router)\n *\n * @example\n * ```tsx\n * <Link href=\"/dashboard\" prefetch={false}>\n * Dashboard\n * </Link>\n * ```\n */\n prefetch?: boolean | 'auto' | null\n\n /**\n * (unstable) Switch to a full prefetch on hover. Effectively the same as\n * updating the prefetch prop to `true` in a mouse event.\n */\n unstable_dynamicOnHover?: boolean\n\n /**\n * The active locale is automatically prepended in the Pages Router. `locale` allows for providing\n * a different locale, or can be set to `false` to opt out of automatic locale behavior.\n *\n * @remarks\n * Note: locale only applies in the Pages Router and is ignored in the App Router.\n *\n * @example\n * ```tsx\n * // Use the 'fr' locale:\n * <Link href=\"/about\" locale=\"fr\">\n * About (French)\n * </Link>\n *\n * // Disable locale prefix:\n * <Link href=\"/about\" locale={false}>\n * About (no locale prefix)\n * </Link>\n * ```\n */\n locale?: string | false\n\n /**\n * Enable legacy link behavior.\n *\n * @deprecated This will be removed in a future version\n * @defaultValue `false`\n * @see https://github.com/vercel/next.js/commit/489e65ed98544e69b0afd7e0cfc3f9f6c2b803b7\n */\n legacyBehavior?: boolean\n\n /**\n * Optional event handler for when the mouse pointer is moved onto the `<Link>`.\n */\n onMouseEnter?: React.MouseEventHandler<HTMLAnchorElement>\n\n /**\n * Optional event handler for when the `<Link>` is touched.\n */\n onTouchStart?: React.TouchEventHandler<HTMLAnchorElement>\n\n /**\n * Optional event handler for when the `<Link>` is clicked.\n */\n onClick?: React.MouseEventHandler<HTMLAnchorElement>\n\n /**\n * Optional event handler for when the `<Link>` is navigated.\n */\n onNavigate?: OnNavigateEventHandler\n}\n\n// TODO-APP: Include the full set of Anchor props\n// adding this to the publicly exported type currently breaks existing apps\n\n// `RouteInferType` is a stub here to avoid breaking `typedRoutes` when the type\n// isn't generated yet. It will be replaced when type generation runs.\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport type LinkProps<RouteInferType = any> = InternalLinkProps\ntype LinkPropsRequired = RequiredKeys<LinkProps>\ntype LinkPropsOptional = OptionalKeys<Omit<InternalLinkProps, 'locale'>>\n\nfunction isModifiedEvent(event: React.MouseEvent): boolean {\n const eventTarget = event.currentTarget as HTMLAnchorElement | SVGAElement\n const target = eventTarget.getAttribute('target')\n return (\n (target && target !== '_self') ||\n event.metaKey ||\n event.ctrlKey ||\n event.shiftKey ||\n event.altKey || // triggers resource download\n (event.nativeEvent && event.nativeEvent.which === 2)\n )\n}\n\nfunction linkClicked(\n e: React.MouseEvent,\n href: string,\n as: string,\n linkInstanceRef: React.RefObject<LinkInstance | null>,\n replace?: boolean,\n scroll?: boolean,\n onNavigate?: OnNavigateEventHandler\n): void {\n if (typeof window !== 'undefined') {\n const { nodeName } = e.currentTarget\n\n // anchors inside an svg have a lowercase nodeName\n const isAnchorNodeName = nodeName.toUpperCase() === 'A'\n if (\n (isAnchorNodeName && isModifiedEvent(e)) ||\n e.currentTarget.hasAttribute('download')\n ) {\n // ignore click for browser’s default behavior\n return\n }\n\n if (!isLocalURL(href)) {\n if (replace) {\n // browser default behavior does not replace the history state\n // so we need to do it manually\n e.preventDefault()\n location.replace(href)\n }\n\n // ignore click for browser’s default behavior\n return\n }\n\n e.preventDefault()\n\n if (onNavigate) {\n let isDefaultPrevented = false\n\n onNavigate({\n preventDefault: () => {\n isDefaultPrevented = true\n },\n })\n\n if (isDefaultPrevented) {\n return\n }\n }\n\n const { dispatchNavigateAction } =\n require('../components/app-router-instance') as typeof import('../components/app-router-instance')\n\n React.startTransition(() => {\n dispatchNavigateAction(\n as || href,\n replace ? 'replace' : 'push',\n scroll ?? true,\n linkInstanceRef.current\n )\n })\n }\n}\n\nfunction formatStringOrUrl(urlObjOrString: UrlObject | string): string {\n if (typeof urlObjOrString === 'string') {\n return urlObjOrString\n }\n\n return formatUrl(urlObjOrString)\n}\n\n/**\n * A React component that extends the HTML `<a>` element to provide\n * [prefetching](https://nextjs.org/docs/app/building-your-application/routing/linking-and-navigating#2-prefetching)\n * and client-side navigation. This is the primary way to navigate between routes in Next.js.\n *\n * @remarks\n * - Prefetching is only enabled in production.\n *\n * @see https://nextjs.org/docs/app/api-reference/components/link\n */\nexport default function LinkComponent(\n props: LinkProps & {\n children: React.ReactNode\n ref: React.Ref<HTMLAnchorElement>\n }\n) {\n const [linkStatus, setOptimisticLinkStatus] = useOptimistic(IDLE_LINK_STATUS)\n\n let children: React.ReactNode\n\n const linkInstanceRef = useRef<LinkInstance | null>(null)\n\n const {\n href: hrefProp,\n as: asProp,\n children: childrenProp,\n prefetch: prefetchProp = null,\n passHref,\n replace,\n shallow,\n scroll,\n onClick,\n onMouseEnter: onMouseEnterProp,\n onTouchStart: onTouchStartProp,\n legacyBehavior = false,\n onNavigate,\n ref: forwardedRef,\n unstable_dynamicOnHover,\n ...restProps\n } = props\n\n children = childrenProp\n\n if (\n legacyBehavior &&\n (typeof children === 'string' || typeof children === 'number')\n ) {\n children = <a>{children}</a>\n }\n\n const router = React.useContext(AppRouterContext)\n\n const prefetchEnabled = prefetchProp !== false\n\n const fetchStrategy =\n prefetchProp !== false\n ? getFetchStrategyFromPrefetchProp(prefetchProp)\n : // TODO: it makes no sense to assign a fetchStrategy when prefetching is disabled.\n FetchStrategy.PPR\n\n if (process.env.NODE_ENV !== 'production') {\n function createPropError(args: {\n key: string\n expected: string\n actual: string\n }) {\n return new Error(\n `Failed prop type: The prop \\`${args.key}\\` expects a ${args.expected} in \\`<Link>\\`, but got \\`${args.actual}\\` instead.` +\n (typeof window !== 'undefined'\n ? \"\\nOpen your browser's console to view the Component stack trace.\"\n : '')\n )\n }\n\n // TypeScript trick for type-guarding:\n const requiredPropsGuard: Record<LinkPropsRequired, true> = {\n href: true,\n } as const\n const requiredProps: LinkPropsRequired[] = Object.keys(\n requiredPropsGuard\n ) as LinkPropsRequired[]\n requiredProps.forEach((key: LinkPropsRequired) => {\n if (key === 'href') {\n if (\n props[key] == null ||\n (typeof props[key] !== 'string' && typeof props[key] !== 'object')\n ) {\n throw createPropError({\n key,\n expected: '`string` or `object`',\n actual: props[key] === null ? 'null' : typeof props[key],\n })\n }\n } else {\n // TypeScript trick for type-guarding:\n const _: never = key\n }\n })\n\n // TypeScript trick for type-guarding:\n const optionalPropsGuard: Record<LinkPropsOptional, true> = {\n as: true,\n replace: true,\n scroll: true,\n shallow: true,\n passHref: true,\n prefetch: true,\n unstable_dynamicOnHover: true,\n onClick: true,\n onMouseEnter: true,\n onTouchStart: true,\n legacyBehavior: true,\n onNavigate: true,\n } as const\n const optionalProps: LinkPropsOptional[] = Object.keys(\n optionalPropsGuard\n ) as LinkPropsOptional[]\n optionalProps.forEach((key: LinkPropsOptional) => {\n const valType = typeof props[key]\n\n if (key === 'as') {\n if (props[key] && valType !== 'string' && valType !== 'object') {\n throw createPropError({\n key,\n expected: '`string` or `object`',\n actual: valType,\n })\n }\n } else if (\n key === 'onClick' ||\n key === 'onMouseEnter' ||\n key === 'onTouchStart' ||\n key === 'onNavigate'\n ) {\n if (props[key] && valType !== 'function') {\n throw createPropError({\n key,\n expected: '`function`',\n actual: valType,\n })\n }\n } else if (\n key === 'replace' ||\n key === 'scroll' ||\n key === 'shallow' ||\n key === 'passHref' ||\n key === 'legacyBehavior' ||\n key === 'unstable_dynamicOnHover'\n ) {\n if (props[key] != null && valType !== 'boolean') {\n throw createPropError({\n key,\n expected: '`boolean`',\n actual: valType,\n })\n }\n } else if (key === 'prefetch') {\n if (\n props[key] != null &&\n valType !== 'boolean' &&\n props[key] !== 'auto'\n ) {\n throw createPropError({\n key,\n expected: '`boolean | \"auto\"`',\n actual: valType,\n })\n }\n } else {\n // TypeScript trick for type-guarding:\n const _: never = key\n }\n })\n }\n\n if (process.env.NODE_ENV !== 'production') {\n if (props.locale) {\n warnOnce(\n 'The `locale` prop is not supported in `next/link` while using the `app` router. Read more about app router internalization: https://nextjs.org/docs/app/building-your-application/routing/internationalization'\n )\n }\n if (!asProp) {\n let href: string | undefined\n if (typeof hrefProp === 'string') {\n href = hrefProp\n } else if (\n typeof hrefProp === 'object' &&\n typeof hrefProp.pathname === 'string'\n ) {\n href = hrefProp.pathname\n }\n\n if (href) {\n const hasDynamicSegment = href\n .split('/')\n .some((segment) => segment.startsWith('[') && segment.endsWith(']'))\n\n if (hasDynamicSegment) {\n throw new Error(\n `Dynamic href \\`${href}\\` found in <Link> while using the \\`/app\\` router, this is not supported. Read more: https://nextjs.org/docs/messages/app-dir-dynamic-href`\n )\n }\n }\n }\n }\n\n const { href, as } = React.useMemo(() => {\n const resolvedHref = formatStringOrUrl(hrefProp)\n return {\n href: resolvedHref,\n as: asProp ? formatStringOrUrl(asProp) : resolvedHref,\n }\n }, [hrefProp, asProp])\n\n // This will return the first child, if multiple are provided it will throw an error\n let child: any\n if (legacyBehavior) {\n if ((children as any)?.$$typeof === Symbol.for('react.lazy')) {\n throw new Error(\n `\\`<Link legacyBehavior>\\` received a direct child that is either a Server Component, or JSX that was loaded with React.lazy(). This is not supported. Either remove legacyBehavior, or make the direct child a Client Component that renders the Link's \\`<a>\\` tag.`\n )\n }\n\n if (process.env.NODE_ENV === 'development') {\n if (onClick) {\n console.warn(\n `\"onClick\" was passed to <Link> with \\`href\\` of \\`${hrefProp}\\` but \"legacyBehavior\" was set. The legacy behavior requires onClick be set on the child of next/link`\n )\n }\n if (onMouseEnterProp) {\n console.warn(\n `\"onMouseEnter\" was passed to <Link> with \\`href\\` of \\`${hrefProp}\\` but \"legacyBehavior\" was set. The legacy behavior requires onMouseEnter be set on the child of next/link`\n )\n }\n try {\n child = React.Children.only(children)\n } catch (err) {\n if (!children) {\n throw new Error(\n `No children were passed to <Link> with \\`href\\` of \\`${hrefProp}\\` but one child is required https://nextjs.org/docs/messages/link-no-children`\n )\n }\n throw new Error(\n `Multiple children were passed to <Link> with \\`href\\` of \\`${hrefProp}\\` but only one child is supported https://nextjs.org/docs/messages/link-multiple-children` +\n (typeof window !== 'undefined'\n ? \" \\nOpen your browser's console to view the Component stack trace.\"\n : '')\n )\n }\n } else {\n child = React.Children.only(children)\n }\n } else {\n if (process.env.NODE_ENV === 'development') {\n if ((children as any)?.type === 'a') {\n throw new Error(\n 'Invalid <Link> with <a> child. Please remove <a> or use <Link legacyBehavior>.\\nLearn more: https://nextjs.org/docs/messages/invalid-new-link-with-extra-anchor'\n )\n }\n }\n }\n\n const childRef: any = legacyBehavior\n ? child && typeof child === 'object' && child.ref\n : forwardedRef\n\n // Use a callback ref to attach an IntersectionObserver to the anchor tag on\n // mount. In the future we will also use this to keep track of all the\n // currently mounted <Link> instances, e.g. so we can re-prefetch them after\n // a revalidation or refresh.\n const observeLinkVisibilityOnMount = React.useCallback(\n (element: HTMLAnchorElement | SVGAElement) => {\n if (router !== null) {\n linkInstanceRef.current = mountLinkInstance(\n element,\n href,\n router,\n fetchStrategy,\n prefetchEnabled,\n setOptimisticLinkStatus\n )\n }\n\n return () => {\n if (linkInstanceRef.current) {\n unmountLinkForCurrentNavigation(linkInstanceRef.current)\n linkInstanceRef.current = null\n }\n unmountPrefetchableInstance(element)\n }\n },\n [prefetchEnabled, href, router, fetchStrategy, setOptimisticLinkStatus]\n )\n\n const mergedRef = useMergedRef(observeLinkVisibilityOnMount, childRef)\n\n const childProps: {\n onTouchStart?: React.TouchEventHandler<HTMLAnchorElement>\n onMouseEnter: React.MouseEventHandler<HTMLAnchorElement>\n onClick: React.MouseEventHandler<HTMLAnchorElement>\n href?: string\n ref?: any\n } = {\n ref: mergedRef,\n onClick(e) {\n if (process.env.NODE_ENV !== 'production') {\n if (!e) {\n throw new Error(\n `Component rendered inside next/link has to pass click event to \"onClick\" prop.`\n )\n }\n }\n\n if (!legacyBehavior && typeof onClick === 'function') {\n onClick(e)\n }\n\n if (\n legacyBehavior &&\n child.props &&\n typeof child.props.onClick === 'function'\n ) {\n child.props.onClick(e)\n }\n\n if (!router) {\n return\n }\n if (e.defaultPrevented) {\n return\n }\n linkClicked(e, href, as, linkInstanceRef, replace, scroll, onNavigate)\n },\n onMouseEnter(e) {\n if (!legacyBehavior && typeof onMouseEnterProp === 'function') {\n onMouseEnterProp(e)\n }\n\n if (\n legacyBehavior &&\n child.props &&\n typeof child.props.onMouseEnter === 'function'\n ) {\n child.props.onMouseEnter(e)\n }\n\n if (!router) {\n return\n }\n if (!prefetchEnabled || process.env.NODE_ENV === 'development') {\n return\n }\n\n const upgradeToDynamicPrefetch = unstable_dynamicOnHover === true\n onNavigationIntent(\n e.currentTarget as HTMLAnchorElement | SVGAElement,\n upgradeToDynamicPrefetch\n )\n },\n onTouchStart: process.env.__NEXT_LINK_NO_TOUCH_START\n ? undefined\n : function onTouchStart(e) {\n if (!legacyBehavior && typeof onTouchStartProp === 'function') {\n onTouchStartProp(e)\n }\n\n if (\n legacyBehavior &&\n child.props &&\n typeof child.props.onTouchStart === 'function'\n ) {\n child.props.onTouchStart(e)\n }\n\n if (!router) {\n return\n }\n if (!prefetchEnabled) {\n return\n }\n\n const upgradeToDynamicPrefetch = unstable_dynamicOnHover === true\n onNavigationIntent(\n e.currentTarget as HTMLAnchorElement | SVGAElement,\n upgradeToDynamicPrefetch\n )\n },\n }\n\n // If the url is absolute, we can bypass the logic to prepend the basePath.\n if (isAbsoluteUrl(as)) {\n childProps.href = as\n } else if (\n !legacyBehavior ||\n passHref ||\n (child.type === 'a' && !('href' in child.props))\n ) {\n childProps.href = addBasePath(as)\n }\n\n let link: React.ReactNode\n\n if (legacyBehavior) {\n if (process.env.NODE_ENV === 'development') {\n errorOnce(\n '`legacyBehavior` is deprecated and will be removed in a future ' +\n 'release. A codemod is available to upgrade your components:\\n\\n' +\n 'npx @next/codemod@latest new-link .\\n\\n' +\n 'Learn more: https://nextjs.org/docs/app/building-your-application/upgrading/codemods#remove-a-tags-from-link-components'\n )\n }\n link = React.cloneElement(child, childProps)\n } else {\n link = (\n <a {...restProps} {...childProps}>\n {children}\n </a>\n )\n }\n\n return (\n <LinkStatusContext.Provider value={linkStatus}>\n {link}\n </LinkStatusContext.Provider>\n )\n}\n\nconst LinkStatusContext = createContext<\n typeof PENDING_LINK_STATUS | typeof IDLE_LINK_STATUS\n>(IDLE_LINK_STATUS)\n\nexport const useLinkStatus = () => {\n return useContext(LinkStatusContext)\n}\n\nfunction getFetchStrategyFromPrefetchProp(\n prefetchProp: Exclude<LinkProps['prefetch'], undefined | false>\n): PrefetchTaskFetchStrategy {\n if (process.env.__NEXT_CACHE_COMPONENTS) {\n if (prefetchProp === true) {\n return FetchStrategy.Full\n }\n\n // `null` or `\"auto\"`: this is the default \"auto\" mode, where we will prefetch partially if the link is in the viewport.\n // This will also include invalid prop values that don't match the types specified here.\n // (although those should've been filtered out by prop validation in dev)\n prefetchProp satisfies null | 'auto'\n return FetchStrategy.PPR\n } else {\n return prefetchProp === null || prefetchProp === 'auto'\n ? // We default to PPR, and we'll discover whether or not the route supports it with the initial prefetch.\n FetchStrategy.PPR\n : // In the old implementation without runtime prefetches, `prefetch={true}` forces all dynamic data to be prefetched.\n // To preserve backwards-compatibility, anything other than `false`, `null`, or `\"auto\"` results in a full prefetch.\n // (although invalid values should've been filtered out by prop validation in dev)\n FetchStrategy.Full\n }\n}\n","import type { ComponentProps } from 'react'\nimport ClientLinkComponent, { type LinkProps, useLinkStatus } from './link'\n\nexport default function LinkComponent(\n props: ComponentProps<typeof ClientLinkComponent>\n) {\n const isLegacyBehavior = props.legacyBehavior\n const childIsHostComponent =\n typeof props.children === 'string' ||\n typeof props.children === 'number' ||\n typeof (props.children as any)?.type === 'string'\n const childIsClientComponent =\n (props.children as any)?.type?.$$typeof ===\n Symbol.for('react.client.reference')\n\n if (isLegacyBehavior && !childIsHostComponent && !childIsClientComponent) {\n if ((props.children as any)?.type?.$$typeof === Symbol.for('react.lazy')) {\n console.error(\n `Using a Lazy Component as a direct child of \\`<Link legacyBehavior>\\` from a Server Component is not supported. If you need legacyBehavior, wrap your Lazy Component in a Client Component that renders the Link's \\`<a>\\` tag.`\n )\n } else {\n console.error(\n `Using a Server Component as a direct child of \\`<Link legacyBehavior>\\` is not supported. If you need legacyBehavior, wrap your Server Component in a Client Component that renders the Link's \\`<a>\\` tag.`\n )\n }\n }\n\n return <ClientLinkComponent {...props} />\n}\n\nexport { type LinkProps, useLinkStatus }\n","// NOTE: This file is compiled by Next.js.\n// Do not import `openclaw/plugin-sdk` here (it is provided by the gateway runtime, not as an npm dep).\n\ntype KitchenApi = {\n config: unknown;\n runtime: {\n system: { runCommandWithTimeout: (argv: string[], opts: { timeoutMs: number }) => Promise<{ stdout?: string; stderr?: string }> };\n };\n};\n\nexport function getKitchenApi(): KitchenApi {\n const api = (globalThis as unknown as { __clawkitchen_api?: KitchenApi }).__clawkitchen_api;\n if (!api) {\n throw new Error(\n \"ClawKitchen: OpenClaw plugin API not available. (This should only happen if Kitchen is started outside the gateway process.)\",\n );\n }\n return api;\n}\n","import { getKitchenApi } from \"@/lib/kitchen-api\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\nexport type OpenClawExecResult = {\n ok: boolean;\n exitCode: number;\n stdout: string;\n stderr: string;\n};\n\nfunction extractStdout(err: { stdout?: unknown }): string {\n if (typeof err.stdout === \"string\") return err.stdout;\n if (err.stdout && typeof err.stdout === \"object\" && \"toString\" in err.stdout) {\n return String((err.stdout as { toString: () => string }).toString());\n }\n return \"\";\n}\n\nfunction resolveExitCode(res: { exitCode?: unknown; code?: unknown; status?: unknown }): number {\n if (typeof res.exitCode === \"number\") return res.exitCode;\n if (typeof res.code === \"number\") return res.code;\n if (typeof res.status === \"number\") return res.status;\n return 0;\n}\n\nfunction extractStderr(err: { stderr?: unknown; message?: unknown }, fallback: unknown): string {\n if (typeof err.stderr === \"string\") return err.stderr;\n if (err.stderr && typeof err.stderr === \"object\" && \"toString\" in err.stderr) {\n return String((err.stderr as { toString: () => string }).toString());\n }\n if (typeof err.message === \"string\") return err.message;\n return String(fallback);\n}\n\nasync function runOpenClawLocal(args: string[]): Promise<OpenClawExecResult> {\n try {\n const res = await execFileAsync(\"openclaw\", args, {\n timeout: 120000,\n windowsHide: true,\n maxBuffer: 10 * 1024 * 1024,\n });\n\n return {\n ok: true,\n exitCode: 0,\n stdout: String(res.stdout ?? \"\"),\n stderr: String(res.stderr ?? \"\"),\n };\n } catch (e: unknown) {\n const err = e as { code?: unknown; stdout?: unknown; stderr?: unknown; message?: unknown };\n const exitCode = typeof err.code === \"number\" ? err.code : 1;\n const stdout = extractStdout(err);\n const stderr = extractStderr(err, e);\n return { ok: false, exitCode, stdout, stderr };\n }\n}\n\nexport async function runOpenClaw(args: string[]): Promise<OpenClawExecResult> {\n // In some Kitchen runtime contexts, `api.runtime.system.runCommandWithTimeout`\n // is executed with a restricted allowlist that does not include the `cron` tool,\n // causing `openclaw cron ...` to fail with \"Tool not available: cron\".\n //\n // Cron routes need to work in the gateway-run Kitchen environment, so for cron\n // specifically we prefer a local exec (host OpenClaw).\n if (args[0] === \"cron\") return runOpenClawLocal(args);\n\n const api = getKitchenApi();\n try {\n const res = (await api.runtime.system.runCommandWithTimeout([\"openclaw\", ...args], { timeoutMs: 120000 })) as {\n stdout?: unknown;\n stderr?: unknown;\n exitCode?: unknown;\n code?: unknown;\n status?: unknown;\n };\n\n const stdout = String(res.stdout ?? \"\");\n const stderr = String(res.stderr ?? \"\");\n const exitCode = resolveExitCode(res);\n\n if (exitCode !== 0) return { ok: false, exitCode, stdout, stderr };\n return { ok: true, exitCode: 0, stdout, stderr };\n } catch (e: unknown) {\n const err = e as { code?: unknown; stdout?: unknown; stderr?: unknown; message?: unknown };\n const exitCode = typeof err.code === \"number\" ? err.code : 1;\n const stdout = extractStdout(err);\n const stderr = extractStderr(err, e);\n return { ok: false, exitCode, stdout, stderr };\n }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { getTeamWorkspaceDir } from \"@/lib/paths\";\nimport { readdirFiles } from \"@/lib/workflows/readdir\";\nimport { assertSafeWorkflowId } from \"@/lib/workflows/storage\";\nimport type { WorkflowRunFileV1, WorkflowRunNodeResultV1 } from \"@/lib/workflows/runs-types\";\n\nconst RUNS_DIR = path.join(\"shared-context\", \"workflow-runs\");\n\n/**\n * Back-compat:\n * - Legacy Kitchen layout: shared-context/workflow-runs/<workflowId>/*.run.json\n * - Runner layout (preferred): shared-context/workflow-runs/*.run.json\n */\nconst LEGACY_PER_WORKFLOW_LAYOUT = true;\n\nexport function assertSafeRunId(runId: string) {\n const id = String(runId ?? \"\").trim();\n if (!id) throw new Error(\"run id is required\");\n\n // Accept both Kitchen-style run ids (lowercase) and runner ids that include ISO timestamps (T/Z).\n // We still keep it conservative: only letters/numbers/dashes.\n if (!/^[A-Za-z0-9][A-Za-z0-9-]{0,120}$/.test(id)) {\n throw new Error(\"Invalid run id. Use letters, numbers, and dashes.\"\n );\n }\n return id;\n}\n\nexport async function getWorkflowRunsDir(teamId: string, workflowId: string) {\n // Validate workflowId (we no longer use it for the directory name).\n void assertSafeWorkflowId(workflowId);\n const teamDir = await getTeamWorkspaceDir(teamId);\n return path.join(teamDir, RUNS_DIR);\n}\n\nexport function workflowRunFileName(runId: string) {\n return `${runId}.run.json`;\n}\n\nexport async function listWorkflowRuns(teamId: string, workflowId: string) {\n const wfId = assertSafeWorkflowId(workflowId);\n const dir = await getWorkflowRunsDir(teamId, wfId);\n\n const runIds: string[] = [];\n\n // Preferred: runner directory-per-run layout: shared-context/workflow-runs/<runId>/run.json\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n const runDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();\n\n for (const runId of runDirs) {\n const p = path.join(dir, runId, \"run.json\");\n try {\n const raw = await fs.readFile(p, \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n const normalized = normalizeRunFile(teamId, wfId, parsed, runId);\n if (normalized.workflowId === wfId) runIds.push(runId);\n } catch {\n // ignore\n }\n }\n } catch {\n // ignore\n }\n\n // Legacy: flat *.run.json at root\n try {\n const { files } = await readdirFiles(dir, \".run.json\", true);\n for (const f of files) {\n const runId = f.replace(/\\.run\\.json$/i, \"\");\n try {\n const raw = await fs.readFile(path.join(dir, f), \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n const normalized = normalizeRunFile(teamId, wfId, parsed, runId);\n if (normalized.workflowId === wfId) runIds.push(runId);\n } catch {\n // ignore\n }\n }\n } catch {\n // ignore\n }\n\n // Back-compat: legacy per-workflow directory.\n if (LEGACY_PER_WORKFLOW_LAYOUT) {\n try {\n const legacyDir = path.join(dir, wfId);\n const { files } = await readdirFiles(legacyDir, \".run.json\", true);\n for (const f of files) runIds.push(f.replace(/\\.run\\.json$/i, \"\"));\n } catch {\n // ignore\n }\n }\n\n const uniq = Array.from(new Set(runIds)).sort().reverse();\n return { ok: true as const, dir, runIds: uniq };\n}\n\nexport async function readWorkflowRun(teamId: string, workflowId: string, runId: string) {\n const wfId = assertSafeWorkflowId(workflowId);\n const rId = assertSafeRunId(runId);\n const dir = await getWorkflowRunsDir(teamId, wfId);\n\n // Preferred (runner) layout: shared-context/workflow-runs/<runId>/run.json\n const pRunner = path.join(dir, rId, \"run.json\");\n try {\n const raw = await fs.readFile(pRunner, \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n const run = normalizeRunFile(teamId, wfId, parsed, rId);\n\n // Runner stores rich node outputs as files under:\n // workflow-runs/<runId>/node-outputs/*-<nodeId>.json\n // Normalize by hydrating node.output from those files when missing.\n if (Array.isArray(run.nodes) && run.nodes.length) {\n const outputsDir = path.join(dir, rId, \"node-outputs\");\n let files: string[] = [];\n try {\n files = await fs.readdir(outputsDir);\n } catch {\n files = [];\n }\n\n // Multiple output files may exist for the same nodeId when a workflow executes a node\n // more than once. Preserve ordering by assigning outputs sequentially per nodeId.\n //\n // NOTE: Runner output filenames are typically prefixed with a sortable counter (e.g. 001-<nodeId>.json).\n // We rely on lexicographic order of filenames within node-outputs.\n const byNodeId = new Map<string, string[]>();\n for (const f of files.slice().sort()) {\n const m = f.match(/-([^/]+)\\.json$/);\n if (!m) continue;\n const nodeId = m[1];\n if (!nodeId) continue;\n const existing = byNodeId.get(nodeId) ?? [];\n existing.push(path.join(outputsDir, f));\n byNodeId.set(nodeId, existing);\n }\n\n await Promise.all(\n run.nodes.map(async (n) => {\n if (!n || typeof n !== \"object\") return;\n // Only hydrate if output is truly missing.\n if (typeof n.output !== \"undefined\") return;\n\n const arr = byNodeId.get(n.nodeId);\n if (!arr || !arr.length) return;\n\n const pOut = arr.shift();\n if (!pOut) return;\n\n try {\n const outRaw = await fs.readFile(pOut, \"utf8\");\n n.output = JSON.parse(outRaw) as unknown;\n } catch {\n // ignore\n }\n })\n );\n }\n\n return { ok: true as const, path: pRunner, run };\n } catch (err: unknown) {\n // fallthrough\n if (!(err && typeof err === \"object\" && (err as { code?: unknown }).code === \"ENOENT\")) throw err;\n }\n\n // Legacy flat layout: shared-context/workflow-runs/<runId>.run.json\n const p0 = path.join(dir, workflowRunFileName(rId));\n try {\n const raw = await fs.readFile(p0, \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n return { ok: true as const, path: p0, run: normalizeRunFile(teamId, wfId, parsed, rId) };\n } catch (err: unknown) {\n if (LEGACY_PER_WORKFLOW_LAYOUT && err && typeof err === \"object\" && (err as { code?: unknown }).code === \"ENOENT\") {\n const legacy = path.join(dir, wfId, workflowRunFileName(rId));\n const raw = await fs.readFile(legacy, \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n return { ok: true as const, path: legacy, run: normalizeRunFile(teamId, wfId, parsed, rId) };\n }\n throw err;\n }\n}\n\nexport async function writeWorkflowRun(teamId: string, workflowId: string, run: WorkflowRunFileV1) {\n const wfId = assertSafeWorkflowId(workflowId);\n const rId = assertSafeRunId(run.id);\n const dir = await getWorkflowRunsDir(teamId, wfId);\n await fs.mkdir(dir, { recursive: true });\n\n // Default layout: write to shared-context/workflow-runs/<runId>.run.json\n //\n // IMPORTANT: that filename may already be occupied by a runner-owned run log (different schema).\n // In that case, keep the runner log intact and write the Kitchen run file to the legacy\n // per-workflow directory shared-context/workflow-runs/<workflowId>/<runId>.run.json.\n const p0 = path.join(dir, workflowRunFileName(rId));\n\n let p = p0;\n try {\n const raw = await fs.readFile(p0, \"utf8\");\n const existing = JSON.parse(raw) as unknown;\n const isRunnerLog =\n Boolean(existing) &&\n typeof existing === \"object\" &&\n !Array.isArray(existing) &&\n \"runId\" in (existing as Record<string, unknown>) &&\n !(\"schema\" in (existing as Record<string, unknown>));\n\n if (isRunnerLog && LEGACY_PER_WORKFLOW_LAYOUT) {\n const legacyDir = path.join(dir, wfId);\n await fs.mkdir(legacyDir, { recursive: true });\n p = path.join(legacyDir, workflowRunFileName(rId));\n }\n } catch {\n // ignore read/parse errors; we'll write to p0\n }\n\n const toWrite: WorkflowRunFileV1 = {\n ...run,\n schema: \"clawkitchen.workflow-run.v1\",\n id: rId,\n workflowId: wfId,\n teamId,\n };\n await fs.writeFile(p, JSON.stringify(toWrite, null, 2) + \"\\n\", \"utf8\");\n return { ok: true as const, path: p };\n}\n\nexport type WorkflowRunSummary = {\n workflowId: string;\n runId: string;\n status?: WorkflowRunFileV1[\"status\"];\n startedAt?: string;\n endedAt?: string;\n /** ISO timestamp derived from file mtime (best-effort) */\n updatedAt?: string;\n /** Absolute path to the run file on disk (server-side only) */\n path: string;\n};\n\nexport async function listAllWorkflowRuns(teamId: string): Promise<{ ok: true; dir: string; runs: WorkflowRunSummary[] }> {\n const teamDir = await getTeamWorkspaceDir(teamId);\n const dir = path.join(teamDir, RUNS_DIR);\n\n const runs: WorkflowRunSummary[] = [];\n\n // Preferred: runner directory-per-run layout:\n // shared-context/workflow-runs/<runId>/run.json\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n const runDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();\n\n for (const runId of runDirs) {\n const full = path.join(dir, runId, \"run.json\");\n try {\n const [raw, st] = await Promise.all([fs.readFile(full, \"utf8\"), fs.stat(full)]);\n const normalized = normalizeRunFile(teamId, \"(unknown)\", JSON.parse(raw) as unknown, runId);\n runs.push({\n workflowId: normalized.workflowId,\n runId: normalized.id,\n status: normalized.status,\n startedAt: normalized.startedAt,\n endedAt: normalized.endedAt,\n updatedAt: st.mtime ? new Date(st.mtime).toISOString() : undefined,\n path: full,\n });\n } catch {\n // ignore missing run.json/parse errors; keep going\n }\n }\n } catch (err: unknown) {\n if (!(err && typeof err === \"object\" && (err as { code?: unknown }).code === \"ENOENT\")) throw err;\n }\n\n // Legacy: flat run files at root: shared-context/workflow-runs/<runId>.run.json\n try {\n const entries = (await fs.readdir(dir)).filter((n) => n.endsWith(\".run.json\")).sort();\n for (const fileName of entries) {\n const runId = fileName.replace(/\\.run\\.json$/i, \"\");\n const full = path.join(dir, fileName);\n try {\n const [raw, st] = await Promise.all([fs.readFile(full, \"utf8\"), fs.stat(full)]);\n const normalized = normalizeRunFile(teamId, \"(unknown)\", JSON.parse(raw) as unknown, runId);\n runs.push({\n workflowId: normalized.workflowId,\n runId: normalized.id,\n status: normalized.status,\n startedAt: normalized.startedAt,\n endedAt: normalized.endedAt,\n updatedAt: st.mtime ? new Date(st.mtime).toISOString() : undefined,\n path: full,\n });\n } catch {\n runs.push({ workflowId: \"(unknown)\", runId, path: full });\n }\n }\n } catch (err: unknown) {\n if (!(err && typeof err === \"object\" && (err as { code?: unknown }).code === \"ENOENT\")) throw err;\n }\n\n // Back-compat: legacy per-workflow directory.\n if (LEGACY_PER_WORKFLOW_LAYOUT) {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n const wfDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();\n\n for (const workflowId of wfDirs) {\n const wfDir = path.join(dir, workflowId);\n let entries2: string[] = [];\n try {\n entries2 = (await fs.readdir(wfDir)).filter((n) => n.endsWith(\".run.json\")).sort();\n } catch (err: unknown) {\n if (err && typeof err === \"object\" && (err as { code?: unknown }).code === \"ENOENT\") continue;\n throw err;\n }\n\n for (const fileName of entries2) {\n const runId = fileName.replace(/\\.run\\.json$/i, \"\");\n const full = path.join(wfDir, fileName);\n try {\n const [raw, st] = await Promise.all([fs.readFile(full, \"utf8\"), fs.stat(full)]);\n const parsed = JSON.parse(raw) as Partial<WorkflowRunFileV1>;\n runs.push({\n workflowId,\n runId,\n status: parsed.status as WorkflowRunFileV1[\"status\"],\n startedAt: typeof parsed.startedAt === \"string\" ? parsed.startedAt : undefined,\n endedAt: typeof parsed.endedAt === \"string\" ? parsed.endedAt : undefined,\n updatedAt: st.mtime ? new Date(st.mtime).toISOString() : undefined,\n path: full,\n });\n } catch {\n runs.push({ workflowId, runId, path: full });\n }\n }\n }\n } catch (err: unknown) {\n if (!(err && typeof err === \"object\" && (err as { code?: unknown }).code === \"ENOENT\")) throw err;\n }\n }\n\n // Deduplicate across layouts. It's possible for the same logical run to appear in multiple places\n // (runner dir-per-run + legacy flat/per-workflow layouts). Prefer the richest record.\n const byKey = new Map<string, WorkflowRunSummary>();\n for (const r of runs) {\n const key = `${r.workflowId}:${r.runId}`;\n const prev = byKey.get(key);\n if (!prev) {\n byKey.set(key, r);\n continue;\n }\n\n const score = (x: WorkflowRunSummary) => {\n let s = 0;\n if (x.status) s += 2;\n if (x.startedAt) s += 1;\n if (x.endedAt) s += 1;\n if (x.updatedAt) s += 1;\n // Prefer runner dir-per-run run.json over legacy .run.json files.\n if (x.path.endsWith(`${path.sep}run.json`)) s += 2;\n return s;\n };\n\n if (score(r) >= score(prev)) byKey.set(key, r);\n }\n\n const deduped = Array.from(byKey.values());\n deduped.sort((a, b) => String(b.updatedAt ?? b.startedAt ?? \"\").localeCompare(String(a.updatedAt ?? a.startedAt ?? \"\")));\n return { ok: true as const, dir, runs: deduped };\n}\n\nfunction normalizeRunFile(teamId: string, workflowId: string, parsed: unknown, runIdFallback: string): WorkflowRunFileV1 {\n // Kitchen schema\n if (parsed && typeof parsed === \"object\" && (parsed as { schema?: unknown }).schema === \"clawkitchen.workflow-run.v1\") {\n return parsed as WorkflowRunFileV1;\n }\n\n // Runner schema (RunLog from ClawRecipes). Convert to Kitchen's display schema.\n if (parsed && typeof parsed === \"object\" && \"runId\" in parsed && \"workflow\" in parsed) {\n const o = parsed as {\n runId?: unknown;\n createdAt?: unknown;\n updatedAt?: unknown;\n status?: unknown;\n workflow?: { id?: unknown; file?: unknown };\n nodeResults?: unknown;\n };\n\n const wfObj = o.workflow && typeof o.workflow === \"object\" ? o.workflow : {};\n const file = typeof wfObj.file === \"string\" ? wfObj.file : \"\";\n const inferredWfId = file ? path.basename(file).replace(/\\.workflow\\.json$/i, \"\") : \"\";\n const wfId = typeof wfObj.id === \"string\" && wfObj.id ? wfObj.id : inferredWfId || workflowId;\n\n const statusRaw = String(o.status ?? \"\");\n const status: WorkflowRunFileV1[\"status\"] =\n statusRaw === \"awaiting_approval\"\n ? \"waiting_for_approval\"\n : statusRaw === \"completed\"\n ? \"success\"\n : statusRaw === \"rejected\"\n ? \"error\"\n : statusRaw === \"error\"\n ? \"error\"\n : statusRaw === \"canceled\"\n ? \"canceled\"\n : \"running\";\n\n const nodes = Array.isArray(o.nodeResults)\n ? o.nodeResults\n .map((nr) => {\n if (!nr || typeof nr !== \"object\") return null;\n const r = nr as {\n nodeId?: unknown;\n status?: unknown;\n startedAt?: unknown;\n endedAt?: unknown;\n output?: unknown;\n error?: unknown;\n };\n const nodeId = typeof r.nodeId === \"string\" ? r.nodeId : \"\";\n if (!nodeId) return null;\n\n const st = String(r.status ?? \"pending\");\n const mapped =\n st === \"awaiting_approval\"\n ? \"waiting\"\n : st === \"completed\"\n ? \"success\"\n : st === \"error\"\n ? \"error\"\n : st;\n\n const allowed = [\"pending\", \"running\", \"waiting\", \"success\", \"error\", \"skipped\"] as const;\n const status = (allowed as readonly string[]).includes(mapped) ? (mapped as (typeof allowed)[number]) : \"pending\";\n\n const error =\n typeof r.error === \"string\"\n ? r.error\n : r.error && typeof r.error === \"object\" && \"message\" in r.error\n ? { message: String((r.error as { message?: unknown }).message ?? \"\") }\n : undefined;\n\n const startedAt = typeof r.startedAt === \"string\" ? r.startedAt : undefined;\n const endedAt = typeof r.endedAt === \"string\" ? r.endedAt : undefined;\n\n return {\n nodeId,\n status,\n ...(startedAt ? { startedAt } : {}),\n ...(endedAt ? { endedAt } : {}),\n ...(typeof r.output !== \"undefined\" ? { output: r.output } : {}),\n ...(typeof error !== \"undefined\" ? { error } : {}),\n } satisfies WorkflowRunNodeResultV1;\n })\n .filter((x): x is WorkflowRunNodeResultV1 => x !== null)\n : undefined;\n\n return {\n schema: \"clawkitchen.workflow-run.v1\",\n id: typeof o.runId === \"string\" ? o.runId : runIdFallback,\n workflowId: wfId,\n teamId,\n startedAt: typeof o.createdAt === \"string\" ? o.createdAt : new Date().toISOString(),\n endedAt:\n status === \"success\" || status === \"error\" || status === \"canceled\"\n ? typeof o.updatedAt === \"string\"\n ? o.updatedAt\n : undefined\n : undefined,\n status,\n summary: \"Executed by workflow runner\",\n nodes,\n };\n }\n\n // Unknown schema; keep a placeholder so UI can still render.\n return {\n schema: \"clawkitchen.workflow-run.v1\",\n id: runIdFallback,\n workflowId,\n teamId,\n startedAt: new Date().toISOString(),\n status: \"error\",\n summary: \"Malformed run file\",\n };\n}","import fs from \"node:fs/promises\";\n\n/** Reads dir with optional suffix filter and reverse sort. Returns { ok, dir, files } or { ok, dir, files: [] } on ENOENT. */\nexport async function readdirFiles(\n dir: string,\n suffix: string,\n reverse = false\n): Promise<{ ok: true; dir: string; files: string[] }> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n let files = entries\n .filter((e) => e.isFile() && e.name.endsWith(suffix))\n .map((e) => e.name)\n .sort();\n if (reverse) files = files.reverse();\n return { ok: true as const, dir, files };\n } catch (err: unknown) {\n if (err && typeof err === \"object\" && (err as { code?: unknown }).code === \"ENOENT\") {\n return { ok: true as const, dir, files: [] as string[] };\n }\n throw err;\n }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { getTeamWorkspaceDir } from \"@/lib/paths\";\nimport { readdirFiles } from \"@/lib/workflows/readdir\";\nimport type { WorkflowFileV1 } from \"@/lib/workflows/types\";\n\nconst WORKFLOWS_DIR = path.join(\"shared-context\", \"workflows\");\n\nexport function workflowFileName(workflowId: string) {\n return `${workflowId}.workflow.json`;\n}\n\nexport function assertSafeWorkflowId(workflowId: string) {\n const id = String(workflowId ?? \"\").trim();\n if (!id) throw new Error(\"workflow id is required\");\n if (!/^[a-z0-9][a-z0-9-]{0,62}$/.test(id)) {\n throw new Error(\n \"Invalid workflow id. Use lowercase letters, numbers, and dashes (max 63 chars), e.g. marketing-cadence-v1\"\n );\n }\n return id;\n}\n\nexport async function getTeamWorkflowsDir(teamId: string) {\n const teamDir = await getTeamWorkspaceDir(teamId);\n return path.join(teamDir, WORKFLOWS_DIR);\n}\n\nexport async function listWorkflows(teamId: string) {\n const dir = await getTeamWorkflowsDir(teamId);\n return readdirFiles(dir, \".workflow.json\");\n}\n\nexport async function readWorkflow(teamId: string, workflowId: string) {\n const id = assertSafeWorkflowId(workflowId);\n const dir = await getTeamWorkflowsDir(teamId);\n const p = path.join(dir, workflowFileName(id));\n const raw = await fs.readFile(p, \"utf8\");\n const parsed = JSON.parse(raw) as WorkflowFileV1;\n return { ok: true as const, path: p, workflow: parsed };\n}\n\nexport async function writeWorkflow(teamId: string, workflow: WorkflowFileV1) {\n const id = assertSafeWorkflowId(workflow.id);\n const dir = await getTeamWorkflowsDir(teamId);\n await fs.mkdir(dir, { recursive: true });\n const p = path.join(dir, workflowFileName(id));\n const toWrite: WorkflowFileV1 = {\n ...workflow,\n schema: \"clawkitchen.workflow.v1\",\n id,\n };\n await fs.writeFile(p, JSON.stringify(toWrite, null, 2) + \"\\n\", \"utf8\");\n return { ok: true as const, path: p };\n}\n\nexport async function deleteWorkflow(teamId: string, workflowId: string) {\n const id = assertSafeWorkflowId(workflowId);\n const dir = await getTeamWorkflowsDir(teamId);\n const p = path.join(dir, workflowFileName(id));\n try {\n await fs.unlink(p);\n } catch (err: unknown) {\n if (err && typeof err === \"object\" && (err as { code?: unknown }).code === \"ENOENT\") {\n return { ok: true as const, path: p, existed: false as const };\n }\n throw err;\n }\n return { ok: true as const, path: p, existed: true as const };\n}\n","// This file is generated by next-core EcmascriptClientReferenceModule.\nimport { registerClientReference } from \"react-server-dom-turbopack/server\";\nexport default registerClientReference(\n function() { throw new Error(\"Attempted to call the default export of [project]/src/app/teams/[teamId]/runs/[workflowId]/[runId]/run-detail-client.tsx from the server, but it's on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.\"); },\n \"[project]/src/app/teams/[teamId]/runs/[workflowId]/[runId]/run-detail-client.tsx\",\n \"default\",\n);\n","import Link from \"next/link\";\nimport { unstable_noStore as noStore } from \"next/cache\";\n\nimport { getTeamDisplayName } from \"@/lib/recipes\";\nimport { readWorkflowRun } from \"@/lib/workflows/runs-storage\";\nimport { readWorkflow } from \"@/lib/workflows/storage\";\nimport RunDetailClient from \"./run-detail-client\";\n\nexport const dynamic = \"force-dynamic\";\nexport const revalidate = 0;\n\nexport default async function TeamRunDetailPage({\n params,\n}: {\n params: Promise<{ teamId: string; workflowId: string; runId: string }>;\n}) {\n noStore();\n\n const { teamId, workflowId, runId } = await params;\n const name = await getTeamDisplayName(teamId);\n\n const [{ run }, workflowRes] = await Promise.all([\n readWorkflowRun(teamId, workflowId, runId),\n readWorkflow(teamId, workflowId).catch(() => null),\n ]);\n\n const wfName = workflowRes?.workflow?.name;\n\n return (\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div>\n <div className=\"text-xs text-[color:var(--ck-text-tertiary)]\">\n <Link href={`/runs?team=${encodeURIComponent(teamId)}`} className=\"hover:underline\">\n Runs\n </Link>\n <span className=\"mx-2\">/</span>\n <span className=\"font-mono\">{workflowId}</span>\n </div>\n <h1 className=\"mt-1 text-2xl font-semibold tracking-tight\">\n {wfName ? `${wfName} · ` : \"\"}\n <span className=\"font-mono\">{runId}</span>\n </h1>\n <div className=\"mt-1 text-sm text-[color:var(--ck-text-secondary)]\">\n {name || teamId} · <span className=\"font-mono\">{teamId}</span>\n </div>\n </div>\n\n <div className=\"flex items-center gap-3\">\n <Link\n href={`/teams/${encodeURIComponent(teamId)}/workflows/${encodeURIComponent(workflowId)}`}\n className=\"text-sm font-medium text-[color:var(--ck-text-secondary)] hover:underline\"\n >\n Workflow editor →\n </Link>\n </div>\n </div>\n\n <RunDetailClient run={run} />\n </div>\n );\n}\n"],"names":["LinkComponent","useLinkStatus","isModifiedEvent","event","eventTarget","currentTarget","target","getAttribute","metaKey","ctrlKey","shiftKey","altKey","nativeEvent","which","linkClicked","e","href","as","linkInstanceRef","replace","scroll","onNavigate","window","nodeName","isAnchorNodeName","toUpperCase","hasAttribute","isLocalURL","preventDefault","location","isDefaultPrevented","dispatchNavigateAction","require","React","startTransition","current","formatStringOrUrl","urlObjOrString","formatUrl","props","linkStatus","setOptimisticLinkStatus","useOptimistic","IDLE_LINK_STATUS","children","useRef","hrefProp","asProp","childrenProp","prefetch","prefetchProp","passHref","shallow","onClick","onMouseEnter","onMouseEnterProp","onTouchStart","onTouchStartProp","legacyBehavior","ref","forwardedRef","unstable_dynamicOnHover","restProps","a","router","useContext","AppRouterContext","prefetchEnabled","fetchStrategy","getFetchStrategyFromPrefetchProp","FetchStrategy","PPR","process","env","NODE_ENV","createPropError","args","Error","key","expected","actual","requiredPropsGuard","requiredProps","Object","keys","forEach","_","optionalPropsGuard","optionalProps","valType","locale","warnOnce","pathname","hasDynamicSegment","split","some","segment","startsWith","endsWith","useMemo","resolvedHref","child","$$typeof","Symbol","for","console","warn","Children","only","err","type","childRef","observeLinkVisibilityOnMount","useCallback","element","mountLinkInstance","unmountLinkForCurrentNavigation","unmountPrefetchableInstance","mergedRef","useMergedRef","childProps","defaultPrevented","upgradeToDynamicPrefetch","onNavigationIntent","__NEXT_LINK_NO_TOUCH_START","undefined","isAbsoluteUrl","addBasePath","link","errorOnce","cloneElement","LinkStatusContext","Provider","value","createContext","__NEXT_CACHE_COMPONENTS","Full","isLegacyBehavior","childIsHostComponent","childIsClientComponent","error","ClientLinkComponent"],"mappings":"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","ignoreList":[0,2,8]}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
module.exports=[74621,a=>{"use strict";function b(a){return a instanceof Error?a.message:String(a)}a.s(["errorMessage",()=>b])},16627,a=>{"use strict";var b=a.i(87924),c=a.i(38246),d=a.i(50944),e=a.i(72131),f=a.i(61889),g=a.i(74621),h=a.i(51200);function i({teamId:a,ticket:c,currentOwner:f}){let g=(0,d.useRouter)(),[h,i]=(0,e.useState)([]),[j,k]=(0,e.useState)(f??""),[l,m]=(0,e.useState)(!1),[n,o]=(0,e.useState)(null);(0,e.useEffect)(()=>{let b=!1;return(async()=>{try{let c=a?`/api/teams/${encodeURIComponent(a)}/tickets/assignees`:"/api/tickets/assignees",d=await fetch(c,{cache:"no-store"}),e=await d.json();if(b)return;i(Array.isArray(e.assignees)?e.assignees:[])}catch{if(b)return;i([])}})(),()=>{b=!0}},[]);let p=(0,e.useMemo)(()=>{let a=new Set(h);return f&&a.add(f),Array.from(a).sort()},[h,f]);async function q(){m(!0),o(null);try{let b=a?`/api/teams/${encodeURIComponent(a)}/tickets/assign`:"/api/tickets/assign",d=await fetch(b,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({ticket:c,assignee:j})}),e=await d.json();if(!d.ok)return void o(e.error||"Assign failed");g.refresh()}catch{o("Assign failed")}finally{m(!1)}}return(0,b.jsxs)("div",{className:"ck-glass p-4",children:[(0,b.jsxs)("div",{className:"flex flex-wrap items-center gap-3",children:[(0,b.jsx)("div",{className:"text-xs font-medium text-[color:var(--ck-text-tertiary)]",children:"Assignee"}),(0,b.jsxs)("select",{className:"rounded-md border border-white/10 bg-black/20 px-3 py-2 text-sm text-[color:var(--ck-text-primary)]",value:j,onChange:a=>k(a.target.value),children:[(0,b.jsx)("option",{value:"",disabled:!0,children:"Select…"}),p.map(a=>(0,b.jsx)("option",{value:a,children:a},a))]}),(0,b.jsx)("button",{type:"button",onClick:q,disabled:l||!j||j===(f??""),className:"rounded-md bg-emerald-600 px-3 py-2 text-sm font-semibold text-white hover:bg-emerald-500 disabled:cursor-not-allowed disabled:opacity-50",children:l?"Assigning…":"Assign"}),f?(0,b.jsxs)("div",{className:"text-xs text-[color:var(--ck-text-tertiary)]",children:["Currently: ",f]}):null]}),n?(0,b.jsx)("div",{className:"mt-2 text-xs text-red-300",children:n}):null]})}function j(a){let j=(0,d.useRouter)(),k=(0,f.useToast)(),[l,m]=(0,e.useTransition)(),[n,o]=(0,e.useState)(null),[p,q]=(0,e.useState)(null),r=(0,e.useMemo)(()=>(function(a){let b=function(a){let b=/^## Comments\s*$/gim.exec(a);if(!b)return null;let c=b.index+b[0].length,d=a.slice(c).search(/^##\s+/m),e=-1===d?a.length:c+d;return{start:c,end:e}}(a);if(!b)return[];let c=a.slice(b.start,b.end).replace(/^\s+/,"").split("\n"),d=[],e=null,f=/^- \*\*(.+?)\*\*\s+—\s+(.*)$/;for(let a of c){let b=a.replace(/\r$/,""),c=b.match(f);if(c){e&&d.push({...e,body:e.body.trimEnd()});let a=c[1].trim(),b=c[2].trim(),f=b.indexOf(":"),g=(f>=0?b.slice(0,f):b).trim()||"unknown",h=f>=0?b.slice(f+1).trimStart():"";e={timestamp:a,author:g,body:h?h+"\n":""};continue}e&&(e.body+=b.replace(/^\s{0,2}/,"")+"\n")}return e&&d.push({...e,body:e.body.trimEnd()}),d})(a.markdown),[a.markdown]),[s,t]=(0,e.useState)(""),[u,v]=(0,e.useState)("");async function w(){o(null),await (0,h.fetchJson)(`/api/teams/${encodeURIComponent(a.teamId)}/tickets/move-to-goals`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({ticket:a.ticketId})})}async function x(){o(null),await (0,h.fetchJson)(`/api/teams/${encodeURIComponent(a.teamId)}/tickets/delete`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({ticket:a.ticketId})})}async function y(){o(null);let b=u.trim();if(!b)throw Error("Comment cannot be empty.");await (0,h.fetchJson)(`/api/teams/${encodeURIComponent(a.teamId)}/tickets/comment`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({ticket:a.ticketId,author:s.trim()||void 0,comment:b})}),v(""),k.push({kind:"success",message:"Comment added."}),j.refresh()}return(0,b.jsxs)("div",{className:"space-y-4",children:[(0,b.jsxs)("div",{className:"flex items-center justify-between gap-3",children:[(0,b.jsx)(c.default,{href:a.backHref??"/tickets",className:"text-sm font-medium hover:underline",children:"← Back"}),(0,b.jsx)("span",{className:"text-xs text-[color:var(--ck-text-tertiary)]",children:a.file})]}),n?(0,b.jsx)("div",{className:"ck-glass border border-[color:var(--ck-border-strong)] p-3 text-sm text-[color:var(--ck-text-primary)]",children:n}):null,void 0!==a.currentOwner?(0,b.jsx)(i,{teamId:a.teamId,ticket:a.ticketId,currentOwner:a.currentOwner??null}):null,(0,b.jsx)("div",{className:"ck-glass p-4",children:(0,b.jsxs)("div",{className:"flex flex-wrap items-center justify-end gap-2",children:[(0,b.jsx)("button",{className:"rounded border border-[color:var(--ck-border-subtle)] px-3 py-1.5 text-xs font-medium text-[color:var(--ck-text-secondary)] hover:border-[color:var(--ck-border-strong)]",onClick:()=>q({kind:"goals"}),disabled:l,children:"Move to Goals"}),(0,b.jsx)("button",{className:"rounded border border-[color:var(--ck-accent-red)] bg-[color:var(--ck-accent-red-soft)] px-3 py-1.5 text-xs font-medium text-[color:var(--ck-accent-red)] hover:bg-[color:var(--ck-accent-red-soft-strong)]",onClick:()=>q({kind:"delete"}),disabled:l,children:"Delete Ticket"})]})}),(0,b.jsxs)("div",{className:"ck-glass p-6",children:[(0,b.jsxs)("div",{className:"flex items-center justify-between gap-3",children:[(0,b.jsx)("div",{className:"text-sm font-semibold text-[color:var(--ck-text-primary)]",children:"Comments"}),(0,b.jsx)("div",{className:"text-xs text-[color:var(--ck-text-tertiary)]",children:r.length})]}),r.length?(0,b.jsx)("div",{className:"mt-3 space-y-3",children:r.map((a,c)=>(0,b.jsxs)("div",{className:"rounded-[var(--ck-radius-sm)] border border-[color:var(--ck-border-subtle)] bg-black/10 p-3",children:[(0,b.jsxs)("div",{className:"flex flex-wrap items-baseline justify-between gap-2",children:[(0,b.jsx)("div",{className:"text-xs font-medium text-[color:var(--ck-text-primary)]",children:a.author}),(0,b.jsx)("div",{className:"text-xs text-[color:var(--ck-text-tertiary)]",children:a.timestamp})]}),(0,b.jsx)("div",{className:"mt-2 whitespace-pre-wrap break-words text-sm leading-6 text-[color:var(--ck-text-secondary)]",children:a.body})]},`${a.timestamp}-${c}`))}):(0,b.jsx)("div",{className:"mt-3 text-sm text-[color:var(--ck-text-secondary)]",children:"No comments yet."}),(0,b.jsxs)("div",{className:"mt-4 border-t border-[color:var(--ck-border-subtle)] pt-4",children:[(0,b.jsx)("div",{className:"text-sm font-semibold text-[color:var(--ck-text-primary)]",children:"Add comment"}),(0,b.jsxs)("div",{className:"mt-3 grid gap-3",children:[(0,b.jsxs)("div",{className:"grid gap-1",children:[(0,b.jsx)("label",{className:"text-xs font-medium text-[color:var(--ck-text-secondary)]",htmlFor:"ck-comment-author",children:"Author (optional)"}),(0,b.jsx)("input",{id:"ck-comment-author",value:s,onChange:a=>t(a.target.value),placeholder:"unknown",className:"w-full rounded border border-[color:var(--ck-border-subtle)] bg-black/20 px-3 py-2 text-sm text-[color:var(--ck-text-primary)] outline-none focus:border-[color:var(--ck-border-strong)]",disabled:l})]}),(0,b.jsxs)("div",{className:"grid gap-1",children:[(0,b.jsx)("label",{className:"text-xs font-medium text-[color:var(--ck-text-secondary)]",htmlFor:"ck-comment-body",children:"Comment"}),(0,b.jsx)("textarea",{id:"ck-comment-body",value:u,onChange:a=>v(a.target.value),rows:4,className:"w-full rounded border border-[color:var(--ck-border-subtle)] bg-black/20 px-3 py-2 text-sm text-[color:var(--ck-text-primary)] outline-none focus:border-[color:var(--ck-border-strong)]",disabled:l})]}),(0,b.jsx)("div",{className:"flex items-center justify-end",children:(0,b.jsx)("button",{className:"rounded bg-[color:var(--ck-accent)] px-3 py-1.5 text-xs font-semibold text-black disabled:opacity-50",onClick:()=>{m(()=>{y().catch(a=>o((0,g.errorMessage)(a)))})},disabled:l||!u.trim(),children:"Post comment"})})]})]})]}),(0,b.jsx)("div",{className:"ck-glass p-6",children:(0,b.jsx)("pre",{className:"whitespace-pre-wrap break-words text-sm leading-6 text-[color:var(--ck-text-primary)]",children:a.markdown})}),p?(0,b.jsx)("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4",children:(0,b.jsxs)("div",{className:"ck-glass w-full max-w-lg rounded-[var(--ck-radius-md)] border border-[color:var(--ck-border-strong)] p-4",children:[(0,b.jsx)("div",{className:"text-sm font-semibold text-[color:var(--ck-text-primary)]",children:"goals"===p.kind?"Move to Goals":"Delete ticket"}),(0,b.jsx)("div",{className:"mt-2 text-sm text-[color:var(--ck-text-secondary)]",children:"goals"===p.kind?(0,b.jsxs)(b.Fragment,{children:["This will move the ticket out of the work lanes into ",(0,b.jsx)("code",{children:"work/goals/"})," so it won’t be picked up by automation."]}):(0,b.jsx)(b.Fragment,{children:"This will permanently remove the ticket markdown file. Assignment stubs (if any) will be archived."})}),(0,b.jsxs)("div",{className:"mt-4 flex items-center justify-end gap-2",children:[(0,b.jsx)("button",{className:"rounded border border-[color:var(--ck-border-subtle)] px-3 py-1.5 text-xs text-[color:var(--ck-text-secondary)]",onClick:()=>q(null),disabled:l,children:"Cancel"}),(0,b.jsx)("button",{className:"delete"===p.kind?"rounded bg-[color:var(--ck-accent-red)] px-3 py-1.5 text-xs font-medium text-white":"rounded bg-[color:var(--ck-accent)] px-3 py-1.5 text-xs font-medium text-black",onClick:()=>{let b=p.kind;q(null),m(()=>{("goals"===b?w():x()).then(()=>{k.push({kind:"success",message:"delete"===b?"Ticket deleted.":"Moved to Goals."}),"delete"===b?j.push(a.backHref??"/tickets"):j.refresh()}).catch(a=>o((0,g.errorMessage)(a)))})},disabled:l,children:"Confirm"})]})]})}):null]})}a.s(["TicketDetailClient",()=>j],16627)}];
|
|
1
|
+
module.exports=[74621,a=>{"use strict";function b(a){return a instanceof Error?a.message:String(a)}a.s(["errorMessage",()=>b])},16627,a=>{"use strict";var b=a.i(87924),c=a.i(38246),d=a.i(50944),e=a.i(72131),f=a.i(61889),g=a.i(74621),h=a.i(51200);function i({teamId:a,ticket:c,currentOwner:f}){let g=(0,d.useRouter)(),[h,i]=(0,e.useState)([]),[j,k]=(0,e.useState)(f??""),[l,m]=(0,e.useState)(!1),[n,o]=(0,e.useState)(null);(0,e.useEffect)(()=>{let b=!1;return(async()=>{try{let c=a?`/api/teams/${encodeURIComponent(a)}/tickets/assignees`:"/api/tickets/assignees",d=await fetch(c,{cache:"no-store"}),e=await d.json();if(b)return;i(Array.isArray(e.assignees)?e.assignees:[])}catch{if(b)return;i([])}})(),()=>{b=!0}},[]);let p=(0,e.useMemo)(()=>{let a=new Set(h);return f&&a.add(f),Array.from(a).sort()},[h,f]);async function q(){m(!0),o(null);try{let b=a?`/api/teams/${encodeURIComponent(a)}/tickets/assign`:"/api/tickets/assign",d=await fetch(b,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({ticket:c,assignee:j})}),e=await d.json();if(!d.ok)return void o(e.error||"Assign failed");g.refresh()}catch{o("Assign failed")}finally{m(!1)}}return(0,b.jsxs)("div",{className:"ck-glass p-4",children:[(0,b.jsxs)("div",{className:"flex flex-wrap items-center gap-3",children:[(0,b.jsx)("div",{className:"text-xs font-medium text-[color:var(--ck-text-tertiary)]",children:"Assignee"}),(0,b.jsxs)("select",{className:"rounded-md border border-white/10 bg-black/20 px-3 py-2 text-sm text-[color:var(--ck-text-primary)]",value:j,onChange:a=>k(a.target.value),children:[(0,b.jsx)("option",{value:"",disabled:!0,children:"Select…"}),p.map(a=>(0,b.jsx)("option",{value:a,children:a},a))]}),(0,b.jsx)("button",{type:"button",onClick:q,disabled:l||!j||j===(f??""),className:"rounded-md bg-emerald-600 px-3 py-2 text-sm font-semibold text-white hover:bg-emerald-500 disabled:cursor-not-allowed disabled:opacity-50",children:l?"Assigning…":"Assign"}),f?(0,b.jsxs)("div",{className:"text-xs text-[color:var(--ck-text-tertiary)]",children:["Currently: ",f]}):null]}),n?(0,b.jsx)("div",{className:"mt-2 text-xs text-red-300",children:n}):null]})}function j(a,b){return b||0==a.trim().length}function k(a){let k=(0,d.useRouter)(),l=(0,f.useToast)(),[m,n]=(0,e.useTransition)(),[o,p]=(0,e.useState)(null),[q,r]=(0,e.useState)(null),s=(0,e.useMemo)(()=>(function(a){let b=function(a){let b=/^## Comments\s*$/gim.exec(a);if(!b)return null;let c=b.index+b[0].length,d=a.slice(c).search(/^##\s+/m),e=-1===d?a.length:c+d;return{start:c,end:e}}(a);if(!b)return[];let c=a.slice(b.start,b.end).replace(/^\s+/,"").split("\n"),d=[],e=null,f=/^- \*\*(.+?)\*\*\s+—\s+(.*)$/;for(let a of c){let b=a.replace(/\r$/,""),c=b.match(f);if(c){e&&d.push({...e,body:e.body.trimEnd()});let a=c[1].trim(),b=c[2].trim(),f=b.indexOf(":"),g=(f>=0?b.slice(0,f):b).trim()||"unknown",h=f>=0?b.slice(f+1).trimStart():"";e={timestamp:a,author:g,body:h?h+"\n":""};continue}e&&(e.body+=b.replace(/^\s{0,2}/,"")+"\n")}return e&&d.push({...e,body:e.body.trimEnd()}),d})(a.markdown),[a.markdown]),[t,u]=(0,e.useState)(""),[v,w]=(0,e.useState)("");async function x(){p(null),await (0,h.fetchJson)(`/api/teams/${encodeURIComponent(a.teamId)}/tickets/move-to-goals`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({ticket:a.ticketId})})}async function y(){p(null),await (0,h.fetchJson)(`/api/teams/${encodeURIComponent(a.teamId)}/tickets/delete`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({ticket:a.ticketId})})}async function z(){p(null);let b=v.trim();if(!b)throw Error("Comment cannot be empty.");await (0,h.fetchJson)(`/api/teams/${encodeURIComponent(a.teamId)}/tickets/comment`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({ticket:a.ticketId,author:t.trim()||void 0,comment:b})}),w(""),l.push({kind:"success",message:"Comment added."}),k.refresh()}return(0,b.jsxs)("div",{className:"space-y-4",children:[(0,b.jsxs)("div",{className:"flex items-center justify-between gap-3",children:[(0,b.jsx)(c.default,{href:a.backHref??"/tickets",className:"text-sm font-medium hover:underline",children:"← Back"}),(0,b.jsx)("span",{className:"text-xs text-[color:var(--ck-text-tertiary)]",children:a.file})]}),o?(0,b.jsx)("div",{className:"ck-glass border border-[color:var(--ck-border-strong)] p-3 text-sm text-[color:var(--ck-text-primary)]",children:o}):null,void 0!==a.currentOwner?(0,b.jsx)(i,{teamId:a.teamId,ticket:a.ticketId,currentOwner:a.currentOwner??null}):null,(0,b.jsx)("div",{className:"ck-glass p-4",children:(0,b.jsxs)("div",{className:"flex flex-wrap items-center justify-end gap-2",children:[(0,b.jsx)("button",{className:"rounded border border-[color:var(--ck-border-subtle)] px-3 py-1.5 text-xs font-medium text-[color:var(--ck-text-secondary)] hover:border-[color:var(--ck-border-strong)]",onClick:()=>r({kind:"goals"}),disabled:m,children:"Move to Goals"}),(0,b.jsx)("button",{className:"rounded border border-[color:var(--ck-accent-red)] bg-[color:var(--ck-accent-red-soft)] px-3 py-1.5 text-xs font-medium text-[color:var(--ck-accent-red)] hover:bg-[color:var(--ck-accent-red-soft-strong)]",onClick:()=>r({kind:"delete"}),disabled:m,children:"Delete Ticket"})]})}),(0,b.jsxs)("div",{className:"ck-glass p-6",children:[(0,b.jsxs)("div",{className:"flex items-center justify-between gap-3",children:[(0,b.jsx)("div",{className:"text-sm font-semibold text-[color:var(--ck-text-primary)]",children:"Comments"}),(0,b.jsx)("div",{className:"text-xs text-[color:var(--ck-text-tertiary)]",children:s.length})]}),s.length?(0,b.jsx)("div",{className:"mt-3 space-y-3",children:s.map((a,c)=>(0,b.jsxs)("div",{className:"rounded-[var(--ck-radius-sm)] border border-[color:var(--ck-border-subtle)] bg-black/10 p-3",children:[(0,b.jsxs)("div",{className:"flex flex-wrap items-baseline justify-between gap-2",children:[(0,b.jsx)("div",{className:"text-xs font-medium text-[color:var(--ck-text-primary)]",children:a.author}),(0,b.jsx)("div",{className:"text-xs text-[color:var(--ck-text-tertiary)]",children:a.timestamp})]}),(0,b.jsx)("div",{className:"mt-2 whitespace-pre-wrap break-words text-sm leading-6 text-[color:var(--ck-text-secondary)]",children:a.body})]},`${a.timestamp}-${c}`))}):(0,b.jsx)("div",{className:"mt-3 text-sm text-[color:var(--ck-text-secondary)]",children:"No comments yet."}),(0,b.jsxs)("div",{className:"mt-4 border-t border-[color:var(--ck-border-subtle)] pt-4",children:[(0,b.jsx)("div",{className:"text-sm font-semibold text-[color:var(--ck-text-primary)]",children:"Add comment"}),(0,b.jsxs)("div",{className:"mt-3 grid gap-3",children:[(0,b.jsxs)("div",{className:"grid gap-1",children:[(0,b.jsx)("label",{className:"text-xs font-medium text-[color:var(--ck-text-secondary)]",htmlFor:"ck-comment-author",children:"Author (optional)"}),(0,b.jsx)("input",{id:"ck-comment-author",value:t,onChange:a=>u(a.target.value),placeholder:"unknown",className:"w-full rounded border border-[color:var(--ck-border-subtle)] bg-black/20 px-3 py-2 text-sm text-[color:var(--ck-text-primary)] outline-none focus:border-[color:var(--ck-border-strong)]",disabled:m})]}),(0,b.jsxs)("div",{className:"grid gap-1",children:[(0,b.jsx)("label",{className:"text-xs font-medium text-[color:var(--ck-text-secondary)]",htmlFor:"ck-comment-body",children:"Comment"}),(0,b.jsx)("textarea",{id:"ck-comment-body",value:v,onChange:a=>w(a.target.value),rows:4,className:"w-full rounded border border-[color:var(--ck-border-subtle)] bg-black/20 px-3 py-2 text-sm text-[color:var(--ck-text-primary)] outline-none focus:border-[color:var(--ck-border-strong)]",disabled:m})]}),(0,b.jsx)("div",{className:"flex items-center justify-end",children:(0,b.jsx)("button",{type:"button","aria-busy":m,className:"rounded-[var(--ck-radius-sm)] bg-[color:var(--ck-accent)] px-3 py-2 text-xs font-semibold text-black transition hover:brightness-110 active:brightness-95 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--ck-border-strong)] focus-visible:ring-offset-2 focus-visible:ring-offset-black/30 disabled:cursor-not-allowed disabled:border disabled:border-[color:var(--ck-border-subtle)] disabled:bg-[color:var(--ck-bg-glass)] disabled:text-[color:var(--ck-text-tertiary)]",onClick:()=>{n(()=>{z().catch(a=>p((0,g.errorMessage)(a)))})},disabled:j(v,m),children:m?"Posting…":"Post comment"})})]})]})]}),(0,b.jsx)("div",{className:"ck-glass p-6",children:(0,b.jsx)("pre",{className:"whitespace-pre-wrap break-words text-sm leading-6 text-[color:var(--ck-text-primary)]",children:a.markdown})}),q?(0,b.jsx)("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4",children:(0,b.jsxs)("div",{className:"ck-glass w-full max-w-lg rounded-[var(--ck-radius-md)] border border-[color:var(--ck-border-strong)] p-4",children:[(0,b.jsx)("div",{className:"text-sm font-semibold text-[color:var(--ck-text-primary)]",children:"goals"===q.kind?"Move to Goals":"Delete ticket"}),(0,b.jsx)("div",{className:"mt-2 text-sm text-[color:var(--ck-text-secondary)]",children:"goals"===q.kind?(0,b.jsxs)(b.Fragment,{children:["This will move the ticket out of the work lanes into ",(0,b.jsx)("code",{children:"work/goals/"})," so it won’t be picked up by automation."]}):(0,b.jsx)(b.Fragment,{children:"This will permanently remove the ticket markdown file. Assignment stubs (if any) will be archived."})}),(0,b.jsxs)("div",{className:"mt-4 flex items-center justify-end gap-2",children:[(0,b.jsx)("button",{className:"rounded border border-[color:var(--ck-border-subtle)] px-3 py-1.5 text-xs text-[color:var(--ck-text-secondary)]",onClick:()=>r(null),disabled:m,children:"Cancel"}),(0,b.jsx)("button",{className:"delete"===q.kind?"rounded bg-[color:var(--ck-accent-red)] px-3 py-1.5 text-xs font-medium text-white":"rounded bg-[color:var(--ck-accent)] px-3 py-1.5 text-xs font-medium text-black",onClick:()=>{let b=q.kind;r(null),n(()=>{("goals"===b?x():y()).then(()=>{l.push({kind:"success",message:"delete"===b?"Ticket deleted.":"Moved to Goals."}),"delete"===b?k.push(a.backHref??"/tickets"):k.refresh()}).catch(a=>p((0,g.errorMessage)(a)))})},disabled:m,children:"Confirm"})]})]})}):null]})}a.s(["TicketDetailClient",()=>k,"isPostCommentDisabled",()=>j],16627)}];
|
|
2
2
|
|
|
3
3
|
//# sourceMappingURL=src_59477309._.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/errors.ts","../../../../src/app/tickets/TicketDetailClient.tsx","../../../../src/lib/ticket-comments.ts","../../../../src/app/tickets/%5Bticket%5D/TicketAssignControl.tsx"],"sourcesContent":["/**\n * Extracts a string message from an unknown error value.\n * Used consistently across API routes and client components.\n */\nexport function errorMessage(e: unknown): string {\n return e instanceof Error ? e.message : String(e);\n}\n","\"use client\";\n\nimport Link from \"next/link\";\nimport { useRouter } from \"next/navigation\";\nimport { useMemo, useState, useTransition } from \"react\";\n\nimport { useToast } from \"@/components/ToastProvider\";\nimport { errorMessage } from \"@/lib/errors\";\nimport { fetchJson } from \"@/lib/fetch-json\";\nimport { parseTicketComments } from \"@/lib/ticket-comments\";\nimport { TicketAssignControl } from \"@/app/tickets/[ticket]/TicketAssignControl\";\n\nexport function TicketDetailClient(props: {\n teamId: string;\n ticketId: string;\n file: string;\n markdown: string;\n backHref?: string;\n currentOwner?: string | null;\n}) {\n const router = useRouter();\n const toast = useToast();\n const [isPending, startTransition] = useTransition();\n const [error, setError] = useState<string | null>(null);\n const [confirm, setConfirm] = useState<null | { kind: \"goals\" | \"delete\" }>(null);\n\n const comments = useMemo(() => parseTicketComments(props.markdown), [props.markdown]);\n\n const [commentAuthor, setCommentAuthor] = useState<string>(\"\");\n const [commentBody, setCommentBody] = useState<string>(\"\");\n\n async function moveToGoals() {\n setError(null);\n await fetchJson(`/api/teams/${encodeURIComponent(props.teamId)}/tickets/move-to-goals`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ ticket: props.ticketId }),\n });\n }\n\n async function deleteTicket() {\n setError(null);\n await fetchJson(`/api/teams/${encodeURIComponent(props.teamId)}/tickets/delete`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ ticket: props.ticketId }),\n });\n }\n\n async function submitComment() {\n setError(null);\n const body = commentBody.trim();\n if (!body) throw new Error(\"Comment cannot be empty.\");\n\n await fetchJson(`/api/teams/${encodeURIComponent(props.teamId)}/tickets/comment`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n ticket: props.ticketId,\n author: commentAuthor.trim() || undefined,\n comment: body,\n }),\n });\n\n setCommentBody(\"\");\n toast.push({ kind: \"success\", message: \"Comment added.\" });\n router.refresh();\n }\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between gap-3\">\n <Link href={props.backHref ?? \"/tickets\"} className=\"text-sm font-medium hover:underline\">\n ← Back\n </Link>\n <span className=\"text-xs text-[color:var(--ck-text-tertiary)]\">{props.file}</span>\n </div>\n\n {error ? (\n <div className=\"ck-glass border border-[color:var(--ck-border-strong)] p-3 text-sm text-[color:var(--ck-text-primary)]\">\n {error}\n </div>\n ) : null}\n\n {props.currentOwner !== undefined ? (\n <TicketAssignControl teamId={props.teamId} ticket={props.ticketId} currentOwner={props.currentOwner ?? null} />\n ) : null}\n\n <div className=\"ck-glass p-4\">\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n <button\n className=\"rounded border border-[color:var(--ck-border-subtle)] px-3 py-1.5 text-xs font-medium text-[color:var(--ck-text-secondary)] hover:border-[color:var(--ck-border-strong)]\"\n onClick={() => setConfirm({ kind: \"goals\" })}\n disabled={isPending}\n >\n Move to Goals\n </button>\n <button\n className=\"rounded border border-[color:var(--ck-accent-red)] bg-[color:var(--ck-accent-red-soft)] px-3 py-1.5 text-xs font-medium text-[color:var(--ck-accent-red)] hover:bg-[color:var(--ck-accent-red-soft-strong)]\"\n onClick={() => setConfirm({ kind: \"delete\" })}\n disabled={isPending}\n >\n Delete Ticket\n </button>\n </div>\n </div>\n\n <div className=\"ck-glass p-6\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"text-sm font-semibold text-[color:var(--ck-text-primary)]\">Comments</div>\n <div className=\"text-xs text-[color:var(--ck-text-tertiary)]\">{comments.length}</div>\n </div>\n\n {comments.length ? (\n <div className=\"mt-3 space-y-3\">\n {comments.map((c, idx) => (\n <div\n key={`${c.timestamp}-${idx}`}\n className=\"rounded-[var(--ck-radius-sm)] border border-[color:var(--ck-border-subtle)] bg-black/10 p-3\"\n >\n <div className=\"flex flex-wrap items-baseline justify-between gap-2\">\n <div className=\"text-xs font-medium text-[color:var(--ck-text-primary)]\">{c.author}</div>\n <div className=\"text-xs text-[color:var(--ck-text-tertiary)]\">{c.timestamp}</div>\n </div>\n <div className=\"mt-2 whitespace-pre-wrap break-words text-sm leading-6 text-[color:var(--ck-text-secondary)]\">\n {c.body}\n </div>\n </div>\n ))}\n </div>\n ) : (\n <div className=\"mt-3 text-sm text-[color:var(--ck-text-secondary)]\">No comments yet.</div>\n )}\n\n <div className=\"mt-4 border-t border-[color:var(--ck-border-subtle)] pt-4\">\n <div className=\"text-sm font-semibold text-[color:var(--ck-text-primary)]\">Add comment</div>\n\n <div className=\"mt-3 grid gap-3\">\n <div className=\"grid gap-1\">\n <label className=\"text-xs font-medium text-[color:var(--ck-text-secondary)]\" htmlFor=\"ck-comment-author\">\n Author (optional)\n </label>\n <input\n id=\"ck-comment-author\"\n value={commentAuthor}\n onChange={(e) => setCommentAuthor(e.target.value)}\n placeholder=\"unknown\"\n className=\"w-full rounded border border-[color:var(--ck-border-subtle)] bg-black/20 px-3 py-2 text-sm text-[color:var(--ck-text-primary)] outline-none focus:border-[color:var(--ck-border-strong)]\"\n disabled={isPending}\n />\n </div>\n\n <div className=\"grid gap-1\">\n <label className=\"text-xs font-medium text-[color:var(--ck-text-secondary)]\" htmlFor=\"ck-comment-body\">\n Comment\n </label>\n <textarea\n id=\"ck-comment-body\"\n value={commentBody}\n onChange={(e) => setCommentBody(e.target.value)}\n rows={4}\n className=\"w-full rounded border border-[color:var(--ck-border-subtle)] bg-black/20 px-3 py-2 text-sm text-[color:var(--ck-text-primary)] outline-none focus:border-[color:var(--ck-border-strong)]\"\n disabled={isPending}\n />\n </div>\n\n <div className=\"flex items-center justify-end\">\n <button\n className=\"rounded bg-[color:var(--ck-accent)] px-3 py-1.5 text-xs font-semibold text-black disabled:opacity-50\"\n onClick={() => {\n startTransition(() => {\n submitComment().catch((e: unknown) => setError(errorMessage(e)));\n });\n }}\n disabled={isPending || !commentBody.trim()}\n >\n Post comment\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <div className=\"ck-glass p-6\">\n <pre className=\"whitespace-pre-wrap break-words text-sm leading-6 text-[color:var(--ck-text-primary)]\">\n {props.markdown}\n </pre>\n </div>\n\n {confirm ? (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4\">\n <div className=\"ck-glass w-full max-w-lg rounded-[var(--ck-radius-md)] border border-[color:var(--ck-border-strong)] p-4\">\n <div className=\"text-sm font-semibold text-[color:var(--ck-text-primary)]\">\n {confirm.kind === \"goals\" ? \"Move to Goals\" : \"Delete ticket\"}\n </div>\n\n <div className=\"mt-2 text-sm text-[color:var(--ck-text-secondary)]\">\n {confirm.kind === \"goals\" ? (\n <>\n This will move the ticket out of the work lanes into <code>work/goals/</code> so it won’t be picked up by\n automation.\n </>\n ) : (\n <>This will permanently remove the ticket markdown file. Assignment stubs (if any) will be archived.</>\n )}\n </div>\n\n <div className=\"mt-4 flex items-center justify-end gap-2\">\n <button\n className=\"rounded border border-[color:var(--ck-border-subtle)] px-3 py-1.5 text-xs text-[color:var(--ck-text-secondary)]\"\n onClick={() => setConfirm(null)}\n disabled={isPending}\n >\n Cancel\n </button>\n <button\n className={\n confirm.kind === \"delete\"\n ? \"rounded bg-[color:var(--ck-accent-red)] px-3 py-1.5 text-xs font-medium text-white\"\n : \"rounded bg-[color:var(--ck-accent)] px-3 py-1.5 text-xs font-medium text-black\"\n }\n onClick={() => {\n const kind = confirm.kind;\n setConfirm(null);\n startTransition(() => {\n const op = kind === \"goals\" ? moveToGoals() : deleteTicket();\n op\n .then(() => {\n toast.push({\n kind: \"success\",\n message: kind === \"delete\" ? \"Ticket deleted.\" : \"Moved to Goals.\",\n });\n if (kind === \"delete\") {\n router.push(props.backHref ?? \"/tickets\");\n } else {\n router.refresh();\n }\n })\n .catch((e: unknown) => setError(errorMessage(e)));\n });\n }}\n disabled={isPending}\n >\n Confirm\n </button>\n </div>\n </div>\n </div>\n ) : null}\n </div>\n );\n}\n","export type TicketComment = {\n timestamp: string;\n author: string;\n body: string;\n};\n\nfunction findCommentsSection(md: string): { start: number; end: number } | null {\n const headerRe = /^## Comments\\s*$/gim;\n const m = headerRe.exec(md);\n if (!m) return null;\n\n const start = m.index + m[0].length;\n\n // Find next heading of same or higher level.\n const rest = md.slice(start);\n const nextHeading = rest.search(/^##\\s+/m);\n const end = nextHeading === -1 ? md.length : start + nextHeading;\n return { start, end };\n}\n\nexport function parseTicketComments(md: string): TicketComment[] {\n const section = findCommentsSection(md);\n if (!section) return [];\n\n const body = md.slice(section.start, section.end).replace(/^\\s+/, \"\");\n const lines = body.split(\"\\n\");\n\n const out: TicketComment[] = [];\n let current: TicketComment | null = null;\n\n const header = /^- \\*\\*(.+?)\\*\\*\\s+—\\s+(.*)$/;\n\n for (const rawLine of lines) {\n const line = rawLine.replace(/\\r$/, \"\");\n const m = line.match(header);\n if (m) {\n if (current) out.push({ ...current, body: current.body.trimEnd() });\n const timestamp = m[1].trim();\n const rest = m[2].trim();\n // Support either \"author: first line\" or just \"author\".\n const colonIdx = rest.indexOf(\":\");\n const author = (colonIdx >= 0 ? rest.slice(0, colonIdx) : rest).trim() || \"unknown\";\n const firstBody = colonIdx >= 0 ? rest.slice(colonIdx + 1).trimStart() : \"\";\n current = { timestamp, author, body: firstBody ? firstBody + \"\\n\" : \"\" };\n continue;\n }\n\n if (!current) continue;\n\n // Allow list paragraph indentation, but keep it simple.\n current.body += line.replace(/^\\s{0,2}/, \"\") + \"\\n\";\n }\n\n if (current) out.push({ ...current, body: current.body.trimEnd() });\n return out;\n}\n\nexport function formatTicketCommentMarkdown(args: {\n timestamp: string;\n author: string;\n body: string;\n}): string {\n const body = args.body.trim().replace(/\\r\\n/g, \"\\n\");\n const indented = body\n .split(\"\\n\")\n .map((l) => ` ${l}`)\n .join(\"\\n\");\n\n const author = args.author?.trim() || \"unknown\";\n return `\\n- **${args.timestamp}** — ${author}:\\n${indented}\\n`;\n}\n","\"use client\";\n\nimport { useEffect, useMemo, useState } from \"react\";\nimport { useRouter } from \"next/navigation\";\n\nexport function TicketAssignControl({\n teamId,\n ticket,\n currentOwner,\n}: {\n teamId?: string | null;\n ticket: string;\n currentOwner: string | null;\n}) {\n const router = useRouter();\n const [assignees, setAssignees] = useState<string[]>([]);\n const [selected, setSelected] = useState<string>(currentOwner ?? \"\");\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n (async () => {\n try {\n const url = teamId\n ? `/api/teams/${encodeURIComponent(teamId)}/tickets/assignees`\n : \"/api/tickets/assignees\";\n const res = await fetch(url, { cache: \"no-store\" });\n const json = (await res.json()) as { assignees?: string[] };\n if (cancelled) return;\n setAssignees(Array.isArray(json.assignees) ? json.assignees : []);\n } catch {\n if (cancelled) return;\n setAssignees([]);\n }\n })();\n return () => {\n cancelled = true;\n };\n }, []);\n\n const options = useMemo(() => {\n const base = new Set(assignees);\n if (currentOwner) base.add(currentOwner);\n return Array.from(base).sort();\n }, [assignees, currentOwner]);\n\n async function onAssign() {\n setLoading(true);\n setError(null);\n try {\n const url = teamId\n ? `/api/teams/${encodeURIComponent(teamId)}/tickets/assign`\n : \"/api/tickets/assign\";\n\n const res = await fetch(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ ticket, assignee: selected }),\n });\n\n const json = (await res.json()) as { error?: string };\n if (!res.ok) {\n setError(json.error || \"Assign failed\");\n return;\n }\n\n router.refresh();\n } catch {\n setError(\"Assign failed\");\n } finally {\n setLoading(false);\n }\n }\n\n return (\n <div className=\"ck-glass p-4\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"text-xs font-medium text-[color:var(--ck-text-tertiary)]\">Assignee</div>\n <select\n className=\"rounded-md border border-white/10 bg-black/20 px-3 py-2 text-sm text-[color:var(--ck-text-primary)]\"\n value={selected}\n onChange={(e) => setSelected(e.target.value)}\n >\n <option value=\"\" disabled>\n Select…\n </option>\n {options.map((a) => (\n <option key={a} value={a}>\n {a}\n </option>\n ))}\n </select>\n\n <button\n type=\"button\"\n onClick={onAssign}\n disabled={loading || !selected || selected === (currentOwner ?? \"\")}\n className=\"rounded-md bg-emerald-600 px-3 py-2 text-sm font-semibold text-white hover:bg-emerald-500 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {loading ? \"Assigning…\" : \"Assign\"}\n </button>\n\n {currentOwner ? (\n <div className=\"text-xs text-[color:var(--ck-text-tertiary)]\">Currently: {currentOwner}</div>\n ) : null}\n </div>\n\n {error ? <div className=\"mt-2 text-xs text-red-300\">{error}</div> : null}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/errors.ts","../../../../src/app/tickets/TicketDetailClient.tsx","../../../../src/lib/ticket-comments.ts","../../../../src/app/tickets/%5Bticket%5D/TicketAssignControl.tsx"],"sourcesContent":["/**\n * Extracts a string message from an unknown error value.\n * Used consistently across API routes and client components.\n */\nexport function errorMessage(e: unknown): string {\n return e instanceof Error ? e.message : String(e);\n}\n","\"use client\";\n\nimport Link from \"next/link\";\nimport { useRouter } from \"next/navigation\";\nimport { useMemo, useState, useTransition } from \"react\";\n\nimport { useToast } from \"@/components/ToastProvider\";\nimport { errorMessage } from \"@/lib/errors\";\nimport { fetchJson } from \"@/lib/fetch-json\";\nimport { parseTicketComments } from \"@/lib/ticket-comments\";\nimport { TicketAssignControl } from \"@/app/tickets/[ticket]/TicketAssignControl\";\n\nexport function isPostCommentDisabled(commentBody: string, isPending: boolean) {\n return isPending || commentBody.trim().length == 0;\n}\n\nexport function TicketDetailClient(props: {\n teamId: string;\n ticketId: string;\n file: string;\n markdown: string;\n backHref?: string;\n currentOwner?: string | null;\n}) {\n const router = useRouter();\n const toast = useToast();\n const [isPending, startTransition] = useTransition();\n const [error, setError] = useState<string | null>(null);\n const [confirm, setConfirm] = useState<null | { kind: \"goals\" | \"delete\" }>(null);\n\n const comments = useMemo(() => parseTicketComments(props.markdown), [props.markdown]);\n\n const [commentAuthor, setCommentAuthor] = useState<string>(\"\");\n const [commentBody, setCommentBody] = useState<string>(\"\");\n\n async function moveToGoals() {\n setError(null);\n await fetchJson(`/api/teams/${encodeURIComponent(props.teamId)}/tickets/move-to-goals`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ ticket: props.ticketId }),\n });\n }\n\n async function deleteTicket() {\n setError(null);\n await fetchJson(`/api/teams/${encodeURIComponent(props.teamId)}/tickets/delete`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ ticket: props.ticketId }),\n });\n }\n\n async function submitComment() {\n setError(null);\n const body = commentBody.trim();\n if (!body) throw new Error(\"Comment cannot be empty.\");\n\n await fetchJson(`/api/teams/${encodeURIComponent(props.teamId)}/tickets/comment`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n ticket: props.ticketId,\n author: commentAuthor.trim() || undefined,\n comment: body,\n }),\n });\n\n setCommentBody(\"\");\n toast.push({ kind: \"success\", message: \"Comment added.\" });\n router.refresh();\n }\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between gap-3\">\n <Link href={props.backHref ?? \"/tickets\"} className=\"text-sm font-medium hover:underline\">\n ← Back\n </Link>\n <span className=\"text-xs text-[color:var(--ck-text-tertiary)]\">{props.file}</span>\n </div>\n\n {error ? (\n <div className=\"ck-glass border border-[color:var(--ck-border-strong)] p-3 text-sm text-[color:var(--ck-text-primary)]\">\n {error}\n </div>\n ) : null}\n\n {props.currentOwner !== undefined ? (\n <TicketAssignControl teamId={props.teamId} ticket={props.ticketId} currentOwner={props.currentOwner ?? null} />\n ) : null}\n\n <div className=\"ck-glass p-4\">\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n <button\n className=\"rounded border border-[color:var(--ck-border-subtle)] px-3 py-1.5 text-xs font-medium text-[color:var(--ck-text-secondary)] hover:border-[color:var(--ck-border-strong)]\"\n onClick={() => setConfirm({ kind: \"goals\" })}\n disabled={isPending}\n >\n Move to Goals\n </button>\n <button\n className=\"rounded border border-[color:var(--ck-accent-red)] bg-[color:var(--ck-accent-red-soft)] px-3 py-1.5 text-xs font-medium text-[color:var(--ck-accent-red)] hover:bg-[color:var(--ck-accent-red-soft-strong)]\"\n onClick={() => setConfirm({ kind: \"delete\" })}\n disabled={isPending}\n >\n Delete Ticket\n </button>\n </div>\n </div>\n\n <div className=\"ck-glass p-6\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"text-sm font-semibold text-[color:var(--ck-text-primary)]\">Comments</div>\n <div className=\"text-xs text-[color:var(--ck-text-tertiary)]\">{comments.length}</div>\n </div>\n\n {comments.length ? (\n <div className=\"mt-3 space-y-3\">\n {comments.map((c, idx) => (\n <div\n key={`${c.timestamp}-${idx}`}\n className=\"rounded-[var(--ck-radius-sm)] border border-[color:var(--ck-border-subtle)] bg-black/10 p-3\"\n >\n <div className=\"flex flex-wrap items-baseline justify-between gap-2\">\n <div className=\"text-xs font-medium text-[color:var(--ck-text-primary)]\">{c.author}</div>\n <div className=\"text-xs text-[color:var(--ck-text-tertiary)]\">{c.timestamp}</div>\n </div>\n <div className=\"mt-2 whitespace-pre-wrap break-words text-sm leading-6 text-[color:var(--ck-text-secondary)]\">\n {c.body}\n </div>\n </div>\n ))}\n </div>\n ) : (\n <div className=\"mt-3 text-sm text-[color:var(--ck-text-secondary)]\">No comments yet.</div>\n )}\n\n <div className=\"mt-4 border-t border-[color:var(--ck-border-subtle)] pt-4\">\n <div className=\"text-sm font-semibold text-[color:var(--ck-text-primary)]\">Add comment</div>\n\n <div className=\"mt-3 grid gap-3\">\n <div className=\"grid gap-1\">\n <label className=\"text-xs font-medium text-[color:var(--ck-text-secondary)]\" htmlFor=\"ck-comment-author\">\n Author (optional)\n </label>\n <input\n id=\"ck-comment-author\"\n value={commentAuthor}\n onChange={(e) => setCommentAuthor(e.target.value)}\n placeholder=\"unknown\"\n className=\"w-full rounded border border-[color:var(--ck-border-subtle)] bg-black/20 px-3 py-2 text-sm text-[color:var(--ck-text-primary)] outline-none focus:border-[color:var(--ck-border-strong)]\"\n disabled={isPending}\n />\n </div>\n\n <div className=\"grid gap-1\">\n <label className=\"text-xs font-medium text-[color:var(--ck-text-secondary)]\" htmlFor=\"ck-comment-body\">\n Comment\n </label>\n <textarea\n id=\"ck-comment-body\"\n value={commentBody}\n onChange={(e) => setCommentBody(e.target.value)}\n rows={4}\n className=\"w-full rounded border border-[color:var(--ck-border-subtle)] bg-black/20 px-3 py-2 text-sm text-[color:var(--ck-text-primary)] outline-none focus:border-[color:var(--ck-border-strong)]\"\n disabled={isPending}\n />\n </div>\n\n <div className=\"flex items-center justify-end\">\n <button\n type=\"button\"\n aria-busy={isPending}\n className={\n \"rounded-[var(--ck-radius-sm)] bg-[color:var(--ck-accent)] px-3 py-2 text-xs font-semibold text-black transition \" +\n \"hover:brightness-110 active:brightness-95 \" +\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--ck-border-strong)] focus-visible:ring-offset-2 focus-visible:ring-offset-black/30 \" +\n \"disabled:cursor-not-allowed disabled:border disabled:border-[color:var(--ck-border-subtle)] disabled:bg-[color:var(--ck-bg-glass)] disabled:text-[color:var(--ck-text-tertiary)]\"\n }\n onClick={() => {\n startTransition(() => {\n submitComment().catch((e: unknown) => setError(errorMessage(e)));\n });\n }}\n disabled={isPostCommentDisabled(commentBody, isPending)}\n >\n {isPending ? \"Posting…\" : \"Post comment\"}\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <div className=\"ck-glass p-6\">\n <pre className=\"whitespace-pre-wrap break-words text-sm leading-6 text-[color:var(--ck-text-primary)]\">\n {props.markdown}\n </pre>\n </div>\n\n {confirm ? (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4\">\n <div className=\"ck-glass w-full max-w-lg rounded-[var(--ck-radius-md)] border border-[color:var(--ck-border-strong)] p-4\">\n <div className=\"text-sm font-semibold text-[color:var(--ck-text-primary)]\">\n {confirm.kind === \"goals\" ? \"Move to Goals\" : \"Delete ticket\"}\n </div>\n\n <div className=\"mt-2 text-sm text-[color:var(--ck-text-secondary)]\">\n {confirm.kind === \"goals\" ? (\n <>\n This will move the ticket out of the work lanes into <code>work/goals/</code> so it won’t be picked up by\n automation.\n </>\n ) : (\n <>This will permanently remove the ticket markdown file. Assignment stubs (if any) will be archived.</>\n )}\n </div>\n\n <div className=\"mt-4 flex items-center justify-end gap-2\">\n <button\n className=\"rounded border border-[color:var(--ck-border-subtle)] px-3 py-1.5 text-xs text-[color:var(--ck-text-secondary)]\"\n onClick={() => setConfirm(null)}\n disabled={isPending}\n >\n Cancel\n </button>\n <button\n className={\n confirm.kind === \"delete\"\n ? \"rounded bg-[color:var(--ck-accent-red)] px-3 py-1.5 text-xs font-medium text-white\"\n : \"rounded bg-[color:var(--ck-accent)] px-3 py-1.5 text-xs font-medium text-black\"\n }\n onClick={() => {\n const kind = confirm.kind;\n setConfirm(null);\n startTransition(() => {\n const op = kind === \"goals\" ? moveToGoals() : deleteTicket();\n op\n .then(() => {\n toast.push({\n kind: \"success\",\n message: kind === \"delete\" ? \"Ticket deleted.\" : \"Moved to Goals.\",\n });\n if (kind === \"delete\") {\n router.push(props.backHref ?? \"/tickets\");\n } else {\n router.refresh();\n }\n })\n .catch((e: unknown) => setError(errorMessage(e)));\n });\n }}\n disabled={isPending}\n >\n Confirm\n </button>\n </div>\n </div>\n </div>\n ) : null}\n </div>\n );\n}\n","export type TicketComment = {\n timestamp: string;\n author: string;\n body: string;\n};\n\nfunction findCommentsSection(md: string): { start: number; end: number } | null {\n const headerRe = /^## Comments\\s*$/gim;\n const m = headerRe.exec(md);\n if (!m) return null;\n\n const start = m.index + m[0].length;\n\n // Find next heading of same or higher level.\n const rest = md.slice(start);\n const nextHeading = rest.search(/^##\\s+/m);\n const end = nextHeading === -1 ? md.length : start + nextHeading;\n return { start, end };\n}\n\nexport function parseTicketComments(md: string): TicketComment[] {\n const section = findCommentsSection(md);\n if (!section) return [];\n\n const body = md.slice(section.start, section.end).replace(/^\\s+/, \"\");\n const lines = body.split(\"\\n\");\n\n const out: TicketComment[] = [];\n let current: TicketComment | null = null;\n\n const header = /^- \\*\\*(.+?)\\*\\*\\s+—\\s+(.*)$/;\n\n for (const rawLine of lines) {\n const line = rawLine.replace(/\\r$/, \"\");\n const m = line.match(header);\n if (m) {\n if (current) out.push({ ...current, body: current.body.trimEnd() });\n const timestamp = m[1].trim();\n const rest = m[2].trim();\n // Support either \"author: first line\" or just \"author\".\n const colonIdx = rest.indexOf(\":\");\n const author = (colonIdx >= 0 ? rest.slice(0, colonIdx) : rest).trim() || \"unknown\";\n const firstBody = colonIdx >= 0 ? rest.slice(colonIdx + 1).trimStart() : \"\";\n current = { timestamp, author, body: firstBody ? firstBody + \"\\n\" : \"\" };\n continue;\n }\n\n if (!current) continue;\n\n // Allow list paragraph indentation, but keep it simple.\n current.body += line.replace(/^\\s{0,2}/, \"\") + \"\\n\";\n }\n\n if (current) out.push({ ...current, body: current.body.trimEnd() });\n return out;\n}\n\nexport function formatTicketCommentMarkdown(args: {\n timestamp: string;\n author: string;\n body: string;\n}): string {\n const body = args.body.trim().replace(/\\r\\n/g, \"\\n\");\n const indented = body\n .split(\"\\n\")\n .map((l) => ` ${l}`)\n .join(\"\\n\");\n\n const author = args.author?.trim() || \"unknown\";\n return `\\n- **${args.timestamp}** — ${author}:\\n${indented}\\n`;\n}\n","\"use client\";\n\nimport { useEffect, useMemo, useState } from \"react\";\nimport { useRouter } from \"next/navigation\";\n\nexport function TicketAssignControl({\n teamId,\n ticket,\n currentOwner,\n}: {\n teamId?: string | null;\n ticket: string;\n currentOwner: string | null;\n}) {\n const router = useRouter();\n const [assignees, setAssignees] = useState<string[]>([]);\n const [selected, setSelected] = useState<string>(currentOwner ?? \"\");\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n (async () => {\n try {\n const url = teamId\n ? `/api/teams/${encodeURIComponent(teamId)}/tickets/assignees`\n : \"/api/tickets/assignees\";\n const res = await fetch(url, { cache: \"no-store\" });\n const json = (await res.json()) as { assignees?: string[] };\n if (cancelled) return;\n setAssignees(Array.isArray(json.assignees) ? json.assignees : []);\n } catch {\n if (cancelled) return;\n setAssignees([]);\n }\n })();\n return () => {\n cancelled = true;\n };\n }, []);\n\n const options = useMemo(() => {\n const base = new Set(assignees);\n if (currentOwner) base.add(currentOwner);\n return Array.from(base).sort();\n }, [assignees, currentOwner]);\n\n async function onAssign() {\n setLoading(true);\n setError(null);\n try {\n const url = teamId\n ? `/api/teams/${encodeURIComponent(teamId)}/tickets/assign`\n : \"/api/tickets/assign\";\n\n const res = await fetch(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ ticket, assignee: selected }),\n });\n\n const json = (await res.json()) as { error?: string };\n if (!res.ok) {\n setError(json.error || \"Assign failed\");\n return;\n }\n\n router.refresh();\n } catch {\n setError(\"Assign failed\");\n } finally {\n setLoading(false);\n }\n }\n\n return (\n <div className=\"ck-glass p-4\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"text-xs font-medium text-[color:var(--ck-text-tertiary)]\">Assignee</div>\n <select\n className=\"rounded-md border border-white/10 bg-black/20 px-3 py-2 text-sm text-[color:var(--ck-text-primary)]\"\n value={selected}\n onChange={(e) => setSelected(e.target.value)}\n >\n <option value=\"\" disabled>\n Select…\n </option>\n {options.map((a) => (\n <option key={a} value={a}>\n {a}\n </option>\n ))}\n </select>\n\n <button\n type=\"button\"\n onClick={onAssign}\n disabled={loading || !selected || selected === (currentOwner ?? \"\")}\n className=\"rounded-md bg-emerald-600 px-3 py-2 text-sm font-semibold text-white hover:bg-emerald-500 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {loading ? \"Assigning…\" : \"Assign\"}\n </button>\n\n {currentOwner ? (\n <div className=\"text-xs text-[color:var(--ck-text-tertiary)]\">Currently: {currentOwner}</div>\n ) : null}\n </div>\n\n {error ? <div className=\"mt-2 text-xs text-red-300\">{error}</div> : null}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|