@axhub/acp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -0
- package/.next/app-path-routes-manifest.json +21 -0
- package/.next/build-manifest.json +22 -0
- package/.next/export-marker.json +6 -0
- package/.next/fallback-build-manifest.json +13 -0
- package/.next/images-manifest.json +68 -0
- package/.next/next-minimal-server.js.nft.json +1 -0
- package/.next/next-server.js.nft.json +1 -0
- package/.next/package.json +1 -0
- package/.next/prerender-manifest.json +90 -0
- package/.next/required-server-files.js +336 -0
- package/.next/required-server-files.json +336 -0
- package/.next/routes-manifest.json +171 -0
- package/.next/server/app/_global-error/page/app-paths-manifest.json +3 -0
- package/.next/server/app/_global-error/page/build-manifest.json +18 -0
- package/.next/server/app/_global-error/page/next-font-manifest.json +6 -0
- package/.next/server/app/_global-error/page/react-loadable-manifest.json +1 -0
- package/.next/server/app/_global-error/page/server-reference-manifest.json +4 -0
- package/.next/server/app/_global-error/page.js +10 -0
- package/.next/server/app/_global-error/page.js.nft.json +1 -0
- package/.next/server/app/_global-error/page_client-reference-manifest.js +3 -0
- package/.next/server/app/_global-error.html +1 -0
- package/.next/server/app/_global-error.meta +15 -0
- package/.next/server/app/_global-error.rsc +15 -0
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +5 -0
- package/.next/server/app/_global-error.segments/_full.segment.rsc +15 -0
- package/.next/server/app/_global-error.segments/_head.segment.rsc +6 -0
- package/.next/server/app/_global-error.segments/_index.segment.rsc +5 -0
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -0
- package/.next/server/app/_not-found/page/app-paths-manifest.json +3 -0
- package/.next/server/app/_not-found/page/build-manifest.json +18 -0
- package/.next/server/app/_not-found/page/next-font-manifest.json +6 -0
- package/.next/server/app/_not-found/page/react-loadable-manifest.json +1 -0
- package/.next/server/app/_not-found/page/server-reference-manifest.json +4 -0
- package/.next/server/app/_not-found/page.js +13 -0
- package/.next/server/app/_not-found/page.js.nft.json +1 -0
- package/.next/server/app/_not-found/page_client-reference-manifest.js +3 -0
- package/.next/server/app/_not-found.html +1 -0
- package/.next/server/app/_not-found.meta +16 -0
- package/.next/server/app/_not-found.rsc +17 -0
- package/.next/server/app/_not-found.segments/_full.segment.rsc +17 -0
- package/.next/server/app/_not-found.segments/_head.segment.rsc +6 -0
- package/.next/server/app/_not-found.segments/_index.segment.rsc +6 -0
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +5 -0
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +5 -0
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -0
- package/.next/server/app/api/acp/capabilities/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/acp/capabilities/route/build-manifest.json +9 -0
- package/.next/server/app/api/acp/capabilities/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/acp/capabilities/route.js +9 -0
- package/.next/server/app/api/acp/capabilities/route.js.nft.json +1 -0
- package/.next/server/app/api/acp/capabilities/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/acp/commands/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/acp/commands/route/build-manifest.json +9 -0
- package/.next/server/app/api/acp/commands/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/acp/commands/route.js +6 -0
- package/.next/server/app/api/acp/commands/route.js.nft.json +1 -0
- package/.next/server/app/api/acp/commands/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/acp/runtime/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/acp/runtime/route/build-manifest.json +9 -0
- package/.next/server/app/api/acp/runtime/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/acp/runtime/route.js +6 -0
- package/.next/server/app/api/acp/runtime/route.js.nft.json +1 -0
- package/.next/server/app/api/acp/runtime/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/chat/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/chat/route/build-manifest.json +9 -0
- package/.next/server/app/api/chat/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/chat/route.js +9 -0
- package/.next/server/app/api/chat/route.js.nft.json +1 -0
- package/.next/server/app/api/chat/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/conversations/[threadId]/messages/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/conversations/[threadId]/messages/route/build-manifest.json +9 -0
- package/.next/server/app/api/conversations/[threadId]/messages/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/conversations/[threadId]/messages/route.js +8 -0
- package/.next/server/app/api/conversations/[threadId]/messages/route.js.nft.json +1 -0
- package/.next/server/app/api/conversations/[threadId]/messages/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/conversations/[threadId]/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/conversations/[threadId]/route/build-manifest.json +9 -0
- package/.next/server/app/api/conversations/[threadId]/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/conversations/[threadId]/route.js +8 -0
- package/.next/server/app/api/conversations/[threadId]/route.js.nft.json +1 -0
- package/.next/server/app/api/conversations/[threadId]/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/conversations/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/conversations/route/build-manifest.json +9 -0
- package/.next/server/app/api/conversations/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/conversations/route.js +8 -0
- package/.next/server/app/api/conversations/route.js.nft.json +1 -0
- package/.next/server/app/api/conversations/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/local-files/image/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/local-files/image/route/build-manifest.json +9 -0
- package/.next/server/app/api/local-files/image/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/local-files/image/route.js +6 -0
- package/.next/server/app/api/local-files/image/route.js.nft.json +1 -0
- package/.next/server/app/api/local-files/image/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/local-files/open/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/local-files/open/route/build-manifest.json +9 -0
- package/.next/server/app/api/local-files/open/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/local-files/open/route.js +6 -0
- package/.next/server/app/api/local-files/open/route.js.nft.json +1 -0
- package/.next/server/app/api/local-files/open/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/output-artifacts/workspace/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/output-artifacts/workspace/route/build-manifest.json +9 -0
- package/.next/server/app/api/output-artifacts/workspace/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/output-artifacts/workspace/route.js +6 -0
- package/.next/server/app/api/output-artifacts/workspace/route.js.nft.json +1 -0
- package/.next/server/app/api/output-artifacts/workspace/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/tools/image-generation/files/[id]/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/tools/image-generation/files/[id]/route/build-manifest.json +9 -0
- package/.next/server/app/api/tools/image-generation/files/[id]/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/tools/image-generation/files/[id]/route.js +7 -0
- package/.next/server/app/api/tools/image-generation/files/[id]/route.js.nft.json +1 -0
- package/.next/server/app/api/tools/image-generation/files/[id]/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/tools/image-generation/records/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/tools/image-generation/records/route/build-manifest.json +9 -0
- package/.next/server/app/api/tools/image-generation/records/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/tools/image-generation/records/route.js +7 -0
- package/.next/server/app/api/tools/image-generation/records/route.js.nft.json +1 -0
- package/.next/server/app/api/tools/image-generation/records/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/tools/user-choice/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/tools/user-choice/route/build-manifest.json +9 -0
- package/.next/server/app/api/tools/user-choice/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/tools/user-choice/route.js +6 -0
- package/.next/server/app/api/tools/user-choice/route.js.nft.json +1 -0
- package/.next/server/app/api/tools/user-choice/route_client-reference-manifest.js +3 -0
- package/.next/server/app/favicon.ico/route/app-paths-manifest.json +3 -0
- package/.next/server/app/favicon.ico/route/build-manifest.json +9 -0
- package/.next/server/app/favicon.ico/route.js +7 -0
- package/.next/server/app/favicon.ico/route.js.nft.json +1 -0
- package/.next/server/app/favicon.ico.body +0 -0
- package/.next/server/app/favicon.ico.meta +1 -0
- package/.next/server/app/page/app-paths-manifest.json +3 -0
- package/.next/server/app/page/build-manifest.json +18 -0
- package/.next/server/app/page/next-font-manifest.json +6 -0
- package/.next/server/app/page/react-loadable-manifest.json +1 -0
- package/.next/server/app/page/server-reference-manifest.json +4 -0
- package/.next/server/app/page.js +14 -0
- package/.next/server/app/page.js.nft.json +1 -0
- package/.next/server/app/page_client-reference-manifest.js +3 -0
- package/.next/server/app/session/[provider]/[sessionId]/page/app-paths-manifest.json +3 -0
- package/.next/server/app/session/[provider]/[sessionId]/page/build-manifest.json +18 -0
- package/.next/server/app/session/[provider]/[sessionId]/page/next-font-manifest.json +6 -0
- package/.next/server/app/session/[provider]/[sessionId]/page/react-loadable-manifest.json +1 -0
- package/.next/server/app/session/[provider]/[sessionId]/page/server-reference-manifest.json +4 -0
- package/.next/server/app/session/[provider]/[sessionId]/page.js +16 -0
- package/.next/server/app/session/[provider]/[sessionId]/page.js.nft.json +1 -0
- package/.next/server/app/session/[provider]/[sessionId]/page_client-reference-manifest.js +3 -0
- package/.next/server/app/thread/[threadId]/page/app-paths-manifest.json +3 -0
- package/.next/server/app/thread/[threadId]/page/build-manifest.json +18 -0
- package/.next/server/app/thread/[threadId]/page/next-font-manifest.json +6 -0
- package/.next/server/app/thread/[threadId]/page/react-loadable-manifest.json +1 -0
- package/.next/server/app/thread/[threadId]/page/server-reference-manifest.json +4 -0
- package/.next/server/app/thread/[threadId]/page.js +15 -0
- package/.next/server/app/thread/[threadId]/page.js.nft.json +1 -0
- package/.next/server/app/thread/[threadId]/page_client-reference-manifest.js +3 -0
- package/.next/server/app-paths-manifest.json +21 -0
- package/.next/server/chunks/0zjb_server_app_api_conversations_[threadId]_messages_route_actions_0poed25.js +3 -0
- package/.next/server/chunks/0zjb_server_app_api_tools_image-generation_files_[id]_route_actions_0-tbvpw.js +3 -0
- package/.next/server/chunks/0zjb_server_app_api_tools_image-generation_records_route_actions_0vu38s2.js +3 -0
- package/.next/server/chunks/[externals]_next_dist_0.m~pv6._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__04xq..~._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__07sxz4_._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0dwg3fr._.js +178 -0
- package/.next/server/chunks/[root-of-the-server]__0eanzwb._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0gqx~5k._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0iokgmz._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0j-lxr4._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0ly6hop._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0nil~wi._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0tmhg7j._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0txmfnw._.js +4 -0
- package/.next/server/chunks/[root-of-the-server]__0wo0b8z._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0~mtsby._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__10-n4io._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__10g507v._.js +3 -0
- package/.next/server/chunks/[turbopack]_runtime.js +903 -0
- package/.next/server/chunks/_next-internal_server_app_api_acp_capabilities_route_actions_08lck.p.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_acp_commands_route_actions_0as.2z6.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_acp_runtime_route_actions_0o1ybkn.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_chat_route_actions_0tmcf6..js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_conversations_[threadId]_route_actions_12yq8z_.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_conversations_route_actions_131xv69.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_local-files_image_route_actions_0f~10mn.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_local-files_open_route_actions_026mg-2.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_output-artifacts_workspace_route_actions_1239-n7.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_tools_user-choice_route_actions_09xqbn_.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_favicon_ico_route_actions_095lj93.js +3 -0
- package/.next/server/chunks/instrumentation_ts_0zq9-xz._.js +3 -0
- package/.next/server/chunks/lib_conversations_store_ts_0gzcj38._.js +7 -0
- package/.next/server/chunks/node_modules_@vercel_oidc_dist_token_0zdeuds.js +3 -0
- package/.next/server/chunks/node_modules_next_00hve1e._.js +13 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__02qo-zr._.js +19 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0488vn3._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__08e5v4-._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__08iwq-u._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0a1m1mq._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0i9qg2.._.js +33 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0icm-_h._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0piffp7._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0xn-a8p._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0~c929r._.js +33 -0
- package/.next/server/chunks/ssr/[turbopack]_runtime.js +903 -0
- package/.next/server/chunks/ssr/_0-oaqqe._.js +6 -0
- package/.next/server/chunks/ssr/_006q235._.js +3 -0
- package/.next/server/chunks/ssr/_03.pm1z._.js +108 -0
- package/.next/server/chunks/ssr/_0txwi90._.js +3 -0
- package/.next/server/chunks/ssr/_next-internal_server_app__global-error_page_actions_0k77kol.js +3 -0
- package/.next/server/chunks/ssr/_next-internal_server_app__not-found_page_actions_0eq97pa.js +3 -0
- package/.next/server/chunks/ssr/_next-internal_server_app_page_actions_09-gtaw.js +3 -0
- package/.next/server/chunks/ssr/_next-internal_server_app_session_[provider]_[sessionId]_page_actions_0_67qh_.js +3 -0
- package/.next/server/chunks/ssr/_next-internal_server_app_thread_[threadId]_page_actions_11mcypo.js +3 -0
- package/.next/server/chunks/ssr/lib_conversations_store_ts_0-pd6d3._.js +4 -0
- package/.next/server/chunks/ssr/node_modules_09w7yel._.js +33 -0
- package/.next/server/chunks/ssr/node_modules_0tev5qq._.js +3 -0
- package/.next/server/chunks/ssr/node_modules_@vercel_oidc_dist_token_0yj7kvj.js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_09jzzl8._.js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_0h9llsw._.js +6 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_0t-hj0x._.js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_0inhx6q._.js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_forbidden_0ghu-f7.js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_global-error_0lgvd_..js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_unauthorized_0cjv-23.js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_00297nb.js +4 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_002l7yi.js +4 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_02suzhc.js +4 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_04x9gct.js +4 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0nsg22r.js +4 -0
- package/.next/server/chunks/tools_image-generation_service_mjs_0k_th7-._.js +3 -0
- package/.next/server/edge/chunks/_0e82_ds._.js +3 -0
- package/.next/server/edge/chunks/turbopack-node_modules_next_dist_esm_build_templates_edge-wrapper_00ue99t.js +3 -0
- package/.next/server/functions-config-manifest.json +11 -0
- package/.next/server/instrumentation/middleware-manifest.json +12 -0
- package/.next/server/instrumentation.js +4 -0
- package/.next/server/instrumentation.js.nft.json +1 -0
- package/.next/server/interception-route-rewrite-manifest.js +1 -0
- package/.next/server/middleware-build-manifest.js +22 -0
- package/.next/server/middleware-manifest.json +6 -0
- package/.next/server/next-font-manifest.js +1 -0
- package/.next/server/next-font-manifest.json +6 -0
- package/.next/server/pages/404.html +1 -0
- package/.next/server/pages/500.html +1 -0
- package/.next/server/pages-manifest.json +4 -0
- package/.next/server/prefetch-hints.json +1 -0
- package/.next/server/server-reference-manifest.js +1 -0
- package/.next/server/server-reference-manifest.json +5 -0
- package/.next/static/chunks/01xlw8hd842-c.js +1 -0
- package/.next/static/chunks/03edqrb4zdj~g.js +31 -0
- package/.next/static/chunks/03qq5fxc~pg00.js +1 -0
- package/.next/static/chunks/03~yq9q893hmn.js +1 -0
- package/.next/static/chunks/0l-_vd.~~z2bw.js +2 -0
- package/.next/static/chunks/0qid5lwcbx9s0.js +1 -0
- package/.next/static/chunks/0t.k8~x~sghva.js +4 -0
- package/.next/static/chunks/0tmpufzgpw2-w.js +1 -0
- package/.next/static/chunks/0v_ue~msygctw.js +1 -0
- package/.next/static/chunks/0y2l0xgouorae.css +1 -0
- package/.next/static/chunks/0zftsky7gte_9.js +102 -0
- package/.next/static/chunks/13nvdsh0e09d0.js +1 -0
- package/.next/static/chunks/1610ha42i.fl~.css +1 -0
- package/.next/static/chunks/turbopack-0mxz-ap91rcoe.js +1 -0
- package/.next/static/mbk_N5Gs4ZJg3lciRL6ya/_buildManifest.js +11 -0
- package/.next/static/mbk_N5Gs4ZJg3lciRL6ya/_clientMiddlewareManifest.js +1 -0
- package/.next/static/mbk_N5Gs4ZJg3lciRL6ya/_ssgManifest.js +1 -0
- package/.next/static/media/favicon.0x3dzn~oxb6tn.ico +0 -0
- package/README.md +113 -0
- package/THIRD_PARTY_NOTICES.md +34 -0
- package/bin/acp.mjs +224 -0
- package/dist/app/globals.css +146 -0
- package/dist/components/assistant-ui/acp-command-menu.d.ts +2 -0
- package/dist/components/assistant-ui/acp-command-menu.mjs +176 -0
- package/dist/components/assistant-ui/acp-elicitation-option-list.d.ts +3 -0
- package/dist/components/assistant-ui/acp-elicitation-option-list.mjs +157 -0
- package/dist/components/assistant-ui/acp-selectors.d.ts +2 -0
- package/dist/components/assistant-ui/acp-selectors.mjs +301 -0
- package/dist/components/assistant-ui/acp-tool-content.d.ts +4 -0
- package/dist/components/assistant-ui/acp-tool-content.mjs +127 -0
- package/dist/components/assistant-ui/addons/host.d.ts +4 -0
- package/dist/components/assistant-ui/addons/host.mjs +85 -0
- package/dist/components/assistant-ui/addons/image-generation-records-section.d.ts +14 -0
- package/dist/components/assistant-ui/addons/image-generation-records-section.mjs +116 -0
- package/dist/components/assistant-ui/addons/output-artifacts-section.d.ts +9 -0
- package/dist/components/assistant-ui/addons/output-artifacts-section.mjs +24 -0
- package/dist/components/assistant-ui/addons/output-artifacts.d.ts +14 -0
- package/dist/components/assistant-ui/addons/output-artifacts.mjs +87 -0
- package/dist/components/assistant-ui/addons/plan-addon.d.ts +2 -0
- package/dist/components/assistant-ui/addons/plan-addon.mjs +39 -0
- package/dist/components/assistant-ui/addons/plan-data.d.ts +5 -0
- package/dist/components/assistant-ui/addons/plan-data.mjs +97 -0
- package/dist/components/assistant-ui/addons/registry.d.ts +2 -0
- package/dist/components/assistant-ui/addons/registry.mjs +8 -0
- package/dist/components/assistant-ui/addons/types.d.ts +13 -0
- package/dist/components/assistant-ui/addons/types.mjs +1 -0
- package/dist/components/assistant-ui/attachment.d.ts +7 -0
- package/dist/components/assistant-ui/attachment.mjs +88 -0
- package/dist/components/assistant-ui/file.d.ts +37 -0
- package/dist/components/assistant-ui/file.mjs +107 -0
- package/dist/components/assistant-ui/image-generation-settings-dialog.d.ts +4 -0
- package/dist/components/assistant-ui/image-generation-settings-dialog.mjs +28 -0
- package/dist/components/assistant-ui/image.d.ts +51 -0
- package/dist/components/assistant-ui/image.mjs +215 -0
- package/dist/components/assistant-ui/markdown-text.d.ts +2 -0
- package/dist/components/assistant-ui/markdown-text.mjs +208 -0
- package/dist/components/assistant-ui/thread/composer.d.ts +27 -0
- package/dist/components/assistant-ui/thread/composer.mjs +15 -0
- package/dist/components/assistant-ui/thread/context-chips.d.ts +2 -0
- package/dist/components/assistant-ui/thread/context-chips.mjs +123 -0
- package/dist/components/assistant-ui/thread/empty-state.d.ts +9 -0
- package/dist/components/assistant-ui/thread/empty-state.mjs +24 -0
- package/dist/components/assistant-ui/thread/index.d.ts +11 -0
- package/dist/components/assistant-ui/thread/index.mjs +43 -0
- package/dist/components/assistant-ui/thread/message-list.d.ts +3 -0
- package/dist/components/assistant-ui/thread/message-list.mjs +16 -0
- package/dist/components/assistant-ui/thread/messages.d.ts +9 -0
- package/dist/components/assistant-ui/thread/messages.mjs +71 -0
- package/dist/components/assistant-ui/thread/scroll-to-bottom.d.ts +2 -0
- package/dist/components/assistant-ui/thread/scroll-to-bottom.mjs +8 -0
- package/dist/components/assistant-ui/thread-list.d.ts +2 -0
- package/dist/components/assistant-ui/thread-list.mjs +31 -0
- package/dist/components/assistant-ui/thread.d.ts +1 -0
- package/dist/components/assistant-ui/thread.mjs +2 -0
- package/dist/components/assistant-ui/threadlist-sidebar.d.ts +6 -0
- package/dist/components/assistant-ui/threadlist-sidebar.mjs +32 -0
- package/dist/components/assistant-ui/tool-fallback.d.ts +31 -0
- package/dist/components/assistant-ui/tool-fallback.mjs +114 -0
- package/dist/components/assistant-ui/tooltip-icon-button.d.ts +7 -0
- package/dist/components/assistant-ui/tooltip-icon-button.mjs +23 -0
- package/dist/components/icons/github.d.ts +3 -0
- package/dist/components/icons/github.mjs +4 -0
- package/dist/components/theme-toggle.d.ts +1 -0
- package/dist/components/theme-toggle.mjs +26 -0
- package/dist/components/tool-ui/option-list.d.ts +34 -0
- package/dist/components/tool-ui/option-list.mjs +163 -0
- package/dist/components/ui/avatar.d.ts +11 -0
- package/dist/components/ui/avatar.mjs +40 -0
- package/dist/components/ui/button.d.ts +10 -0
- package/dist/components/ui/button.mjs +47 -0
- package/dist/components/ui/collapsible.d.ts +5 -0
- package/dist/components/ui/collapsible.mjs +27 -0
- package/dist/components/ui/dialog.d.ts +17 -0
- package/dist/components/ui/dialog.mjs +58 -0
- package/dist/components/ui/input.d.ts +3 -0
- package/dist/components/ui/input.mjs +18 -0
- package/dist/components/ui/label.d.ts +4 -0
- package/dist/components/ui/label.mjs +20 -0
- package/dist/components/ui/scroll-area.d.ts +5 -0
- package/dist/components/ui/scroll-area.mjs +26 -0
- package/dist/components/ui/select.d.ts +13 -0
- package/dist/components/ui/select.mjs +59 -0
- package/dist/components/ui/separator.d.ts +4 -0
- package/dist/components/ui/separator.mjs +20 -0
- package/dist/components/ui/sheet.d.ts +14 -0
- package/dist/components/ui/sheet.mjs +61 -0
- package/dist/components/ui/sidebar.d.ts +69 -0
- package/dist/components/ui/sidebar.mjs +245 -0
- package/dist/components/ui/skeleton.d.ts +2 -0
- package/dist/components/ui/skeleton.mjs +18 -0
- package/dist/components/ui/switch.d.ts +4 -0
- package/dist/components/ui/switch.mjs +20 -0
- package/dist/components/ui/tabs.d.ts +7 -0
- package/dist/components/ui/tabs.mjs +32 -0
- package/dist/components/ui/tooltip.d.ts +7 -0
- package/dist/components/ui/tooltip.mjs +32 -0
- package/dist/hooks/use-mobile.d.ts +1 -0
- package/dist/hooks/use-mobile.mjs +15 -0
- package/dist/lib/acp2aisdk/capabilities.d.ts +6 -0
- package/dist/lib/acp2aisdk/capabilities.mjs +210 -0
- package/dist/lib/acp2aisdk/capability-cache.d.ts +17 -0
- package/dist/lib/acp2aisdk/capability-cache.mjs +259 -0
- package/dist/lib/acp2aisdk/client-context.d.ts +38 -0
- package/dist/lib/acp2aisdk/client-context.mjs +247 -0
- package/dist/lib/acp2aisdk/commands.d.ts +11 -0
- package/dist/lib/acp2aisdk/commands.mjs +121 -0
- package/dist/lib/acp2aisdk/config-options.d.ts +3 -0
- package/dist/lib/acp2aisdk/config-options.mjs +86 -0
- package/dist/lib/acp2aisdk/context.d.ts +88 -0
- package/dist/lib/acp2aisdk/context.mjs +33 -0
- package/dist/lib/acp2aisdk/default-capabilities.d.ts +275 -0
- package/dist/lib/acp2aisdk/default-capabilities.mjs +421 -0
- package/dist/lib/acp2aisdk/elicitation.d.ts +37 -0
- package/dist/lib/acp2aisdk/elicitation.mjs +193 -0
- package/dist/lib/acp2aisdk/hitl.d.ts +25 -0
- package/dist/lib/acp2aisdk/hitl.mjs +134 -0
- package/dist/lib/acp2aisdk/index.d.ts +12 -0
- package/dist/lib/acp2aisdk/index.mjs +182 -0
- package/dist/lib/acp2aisdk/mcp-servers.d.ts +17 -0
- package/dist/lib/acp2aisdk/mcp-servers.mjs +153 -0
- package/dist/lib/acp2aisdk/model-selection.d.ts +16 -0
- package/dist/lib/acp2aisdk/model-selection.mjs +65 -0
- package/dist/lib/acp2aisdk/prompt-history.d.ts +9 -0
- package/dist/lib/acp2aisdk/prompt-history.mjs +168 -0
- package/dist/lib/acp2aisdk/provider-compat.d.ts +85 -0
- package/dist/lib/acp2aisdk/provider-compat.mjs +501 -0
- package/dist/lib/acp2aisdk/provider-registry.d.ts +15 -0
- package/dist/lib/acp2aisdk/provider-registry.mjs +196 -0
- package/dist/lib/acp2aisdk/response.d.ts +3 -0
- package/dist/lib/acp2aisdk/response.mjs +33 -0
- package/dist/lib/acp2aisdk/runtime-options.d.ts +2 -0
- package/dist/lib/acp2aisdk/runtime-options.mjs +10 -0
- package/dist/lib/acp2aisdk/session-runtime.d.ts +5 -0
- package/dist/lib/acp2aisdk/session-runtime.mjs +186 -0
- package/dist/lib/acp2aisdk/session-store.d.ts +31 -0
- package/dist/lib/acp2aisdk/session-store.mjs +169 -0
- package/dist/lib/acp2aisdk/skill-command-cache.d.ts +20 -0
- package/dist/lib/acp2aisdk/skill-command-cache.mjs +122 -0
- package/dist/lib/acp2aisdk/stream-metadata.d.ts +10 -0
- package/dist/lib/acp2aisdk/stream-metadata.mjs +251 -0
- package/dist/lib/acp2aisdk/types.d.ts +129 -0
- package/dist/lib/acp2aisdk/types.mjs +0 -0
- package/dist/lib/acp2aisdk/vendor/acp-ai-provider/index.d.ts +10 -0
- package/dist/lib/acp2aisdk/vendor/acp-ai-provider/index.mjs +10 -0
- package/dist/lib/acp2aisdk/vendor/acp-ai-provider/package/index.mjs +1945 -0
- package/dist/lib/api/client.d.ts +93 -0
- package/dist/lib/api/client.mjs +199 -0
- package/dist/lib/api/cors.d.ts +2 -0
- package/dist/lib/api/cors.mjs +47 -0
- package/dist/lib/conversations/client-adapter.d.ts +21 -0
- package/dist/lib/conversations/client-adapter.mjs +329 -0
- package/dist/lib/conversations/message-repository.d.ts +8 -0
- package/dist/lib/conversations/message-repository.mjs +87 -0
- package/dist/lib/conversations/store.d.ts +17 -0
- package/dist/lib/conversations/store.mjs +678 -0
- package/dist/lib/conversations/types.d.ts +92 -0
- package/dist/lib/conversations/types.mjs +0 -0
- package/dist/lib/local-image-files.d.ts +5 -0
- package/dist/lib/local-image-files.mjs +76 -0
- package/dist/lib/local-image-paths.d.ts +7 -0
- package/dist/lib/local-image-paths.mjs +106 -0
- package/dist/lib/output-artifacts/workspace.d.ts +8 -0
- package/dist/lib/output-artifacts/workspace.mjs +174 -0
- package/dist/lib/provider-history/codex.d.ts +4 -0
- package/dist/lib/provider-history/codex.mjs +272 -0
- package/dist/lib/provider-history/index.d.ts +7 -0
- package/dist/lib/provider-history/index.mjs +88 -0
- package/dist/lib/provider-history/types.d.ts +31 -0
- package/dist/lib/provider-history/types.mjs +0 -0
- package/dist/lib/server-runtime.d.ts +22 -0
- package/dist/lib/server-runtime.mjs +78 -0
- package/dist/lib/url-routing.d.ts +18 -0
- package/dist/lib/url-routing.mjs +59 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.mjs +5 -0
- package/dist/lib/workspace-metadata.d.ts +2 -0
- package/dist/lib/workspace-metadata.mjs +20 -0
- package/dist/public-api/react.d.ts +14 -0
- package/dist/public-api/react.mjs +16 -0
- package/dist/public-api/runtime.d.ts +8 -0
- package/dist/public-api/runtime.mjs +7 -0
- package/dist/public-api/server.d.ts +5 -0
- package/dist/public-api/server.mjs +4 -0
- package/dist/public-api/styles.css +1 -0
- package/dist/public-api/ui.d.ts +13 -0
- package/dist/public-api/ui.mjs +13 -0
- package/dist/tools/client-registry.d.ts +9 -0
- package/dist/tools/client-registry.mjs +70 -0
- package/dist/tools/image-generation/client.d.ts +18 -0
- package/dist/tools/image-generation/client.mjs +31 -0
- package/dist/tools/image-generation/config.d.ts +9 -0
- package/dist/tools/image-generation/config.example.json +14 -0
- package/dist/tools/image-generation/config.mjs +11 -0
- package/dist/tools/image-generation/mcp-server.mjs +87 -0
- package/dist/tools/image-generation/registry.d.ts +13 -0
- package/dist/tools/image-generation/registry.mjs +30 -0
- package/dist/tools/image-generation/server.d.ts +6 -0
- package/dist/tools/image-generation/server.mjs +58 -0
- package/dist/tools/image-generation/service.mjs +978 -0
- package/dist/tools/image-generation/shared.d.ts +103 -0
- package/dist/tools/image-generation/shared.mjs +80 -0
- package/dist/tools/image-generation/storage.d.ts +22 -0
- package/dist/tools/image-generation/storage.mjs +8 -0
- package/dist/tools/image-generation/ui-detail.d.ts +26 -0
- package/dist/tools/image-generation/ui-detail.mjs +109 -0
- package/dist/tools/image-generation/ui.d.ts +8 -0
- package/dist/tools/image-generation/ui.mjs +121 -0
- package/dist/tools/registry.d.ts +32 -0
- package/dist/tools/registry.mjs +142 -0
- package/dist/tools/ui-registry.d.ts +3 -0
- package/dist/tools/ui-registry.mjs +20 -0
- package/dist/tools/user-choice/client.d.ts +10 -0
- package/dist/tools/user-choice/client.mjs +18 -0
- package/dist/tools/user-choice/registry.d.ts +12 -0
- package/dist/tools/user-choice/registry.mjs +21 -0
- package/dist/tools/user-choice/server.d.ts +6 -0
- package/dist/tools/user-choice/server.mjs +42 -0
- package/dist/tools/user-choice/shared.d.ts +79 -0
- package/dist/tools/user-choice/shared.mjs +56 -0
- package/dist/tools/user-choice/store.d.ts +19 -0
- package/dist/tools/user-choice/store.mjs +95 -0
- package/dist/tools/user-choice/ui.d.ts +3 -0
- package/dist/tools/user-choice/ui.mjs +396 -0
- package/package.json +118 -0
|
@@ -0,0 +1,978 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fsSync from "node:fs";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
export const IMAGE_GENERATION_MCP_NAME = "acp-ui-image-generation";
|
|
8
|
+
export const IMAGE_GENERATION_TOOL_NAME = "generate_image";
|
|
9
|
+
export const IMAGE_GENERATION_TOOL_ID = "image-generation";
|
|
10
|
+
|
|
11
|
+
const TOOL_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const INDEX_FILE = "index.json";
|
|
13
|
+
const CONVERSATION_STORE_PATH = path.join(
|
|
14
|
+
".axhub",
|
|
15
|
+
"sessions",
|
|
16
|
+
"conversations.json",
|
|
17
|
+
);
|
|
18
|
+
const DEFAULT_CONFIG = {
|
|
19
|
+
provider: "openai",
|
|
20
|
+
baseUrl: "https://api.openai.com/v1",
|
|
21
|
+
apiKey: "",
|
|
22
|
+
model: "",
|
|
23
|
+
outputDir: ".axhub/sessions/image-generation",
|
|
24
|
+
maxInputBytes: 10 * 1024 * 1024,
|
|
25
|
+
maxOutputBytes: 50 * 1024 * 1024,
|
|
26
|
+
downloadTimeoutMs: 30_000,
|
|
27
|
+
requestTimeoutMs: 180_000,
|
|
28
|
+
allowSavePath: true,
|
|
29
|
+
savePathBaseDir: ".",
|
|
30
|
+
savePathPattern: "",
|
|
31
|
+
preservePrompt: true,
|
|
32
|
+
};
|
|
33
|
+
const PROMPT_REWRITE_GUARD_PREFIX =
|
|
34
|
+
"Use the following text as the complete prompt. Do not rewrite it:";
|
|
35
|
+
const SUPPORTED_MIME_TYPES = new Set(["image/png", "image/jpeg", "image/webp"]);
|
|
36
|
+
|
|
37
|
+
function isRecord(value) {
|
|
38
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function createId() {
|
|
42
|
+
return crypto.randomBytes(16).toString("hex");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function nowIso() {
|
|
46
|
+
return new Date().toISOString();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function normalizeWorkspacePath(workspacePath) {
|
|
50
|
+
const fallback = path.resolve(/* turbopackIgnore: true */ process.cwd());
|
|
51
|
+
const raw =
|
|
52
|
+
typeof workspacePath === "string" && workspacePath.trim()
|
|
53
|
+
? workspacePath.trim()
|
|
54
|
+
: fallback;
|
|
55
|
+
const resolved = path.resolve(/* turbopackIgnore: true */ raw);
|
|
56
|
+
try {
|
|
57
|
+
return fsSync.statSync(/* turbopackIgnore: true */ resolved).isDirectory()
|
|
58
|
+
? resolved
|
|
59
|
+
: fallback;
|
|
60
|
+
} catch {
|
|
61
|
+
return fallback;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function readJsonFile(filePath) {
|
|
66
|
+
if (!fsSync.existsSync(/* turbopackIgnore: true */ filePath)) return null;
|
|
67
|
+
return JSON.parse(
|
|
68
|
+
fsSync.readFileSync(/* turbopackIgnore: true */ filePath, "utf8"),
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function stringValue(value, fallback) {
|
|
73
|
+
return typeof value === "string" && value.trim() ? value.trim() : fallback;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function booleanValue(value, fallback) {
|
|
77
|
+
return typeof value === "boolean" ? value : fallback;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function numberValue(value, fallback) {
|
|
81
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0
|
|
82
|
+
? value
|
|
83
|
+
: fallback;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function mergeConfig(base, raw) {
|
|
87
|
+
if (!isRecord(raw)) return base;
|
|
88
|
+
return {
|
|
89
|
+
...base,
|
|
90
|
+
provider: stringValue(raw.provider, base.provider),
|
|
91
|
+
baseUrl: stringValue(raw.baseUrl, base.baseUrl).replace(/\/+$/, ""),
|
|
92
|
+
apiKey: stringValue(raw.apiKey, base.apiKey),
|
|
93
|
+
model: stringValue(raw.model, base.model),
|
|
94
|
+
outputDir: stringValue(raw.outputDir, base.outputDir),
|
|
95
|
+
maxInputBytes: numberValue(raw.maxInputBytes, base.maxInputBytes),
|
|
96
|
+
maxOutputBytes: numberValue(raw.maxOutputBytes, base.maxOutputBytes),
|
|
97
|
+
downloadTimeoutMs: numberValue(
|
|
98
|
+
raw.downloadTimeoutMs,
|
|
99
|
+
base.downloadTimeoutMs,
|
|
100
|
+
),
|
|
101
|
+
requestTimeoutMs: numberValue(raw.requestTimeoutMs, base.requestTimeoutMs),
|
|
102
|
+
allowSavePath: booleanValue(raw.allowSavePath, base.allowSavePath),
|
|
103
|
+
savePathBaseDir: stringValue(raw.savePathBaseDir, base.savePathBaseDir),
|
|
104
|
+
savePathPattern: stringValue(raw.savePathPattern, base.savePathPattern),
|
|
105
|
+
preservePrompt: booleanValue(raw.preservePrompt, base.preservePrompt),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function normalizeRuntimeSettings(value) {
|
|
110
|
+
if (!isRecord(value)) return null;
|
|
111
|
+
return {
|
|
112
|
+
...(typeof value.baseUrl === "string" && value.baseUrl.trim()
|
|
113
|
+
? { baseUrl: value.baseUrl.trim().replace(/\/+$/, "") }
|
|
114
|
+
: {}),
|
|
115
|
+
...(typeof value.apiKey === "string" && value.apiKey.trim()
|
|
116
|
+
? { apiKey: value.apiKey.trim() }
|
|
117
|
+
: {}),
|
|
118
|
+
...(typeof value.model === "string" && value.model.trim()
|
|
119
|
+
? { model: value.model.trim() }
|
|
120
|
+
: {}),
|
|
121
|
+
...(typeof value.savePathPattern === "string" &&
|
|
122
|
+
value.savePathPattern.trim()
|
|
123
|
+
? { savePathPattern: value.savePathPattern.trim() }
|
|
124
|
+
: {}),
|
|
125
|
+
...(typeof value.preservePrompt === "boolean"
|
|
126
|
+
? { preservePrompt: value.preservePrompt }
|
|
127
|
+
: {}),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function getConversationStorePath(workspacePath) {
|
|
132
|
+
const overridePath = process.env.ACP_UI_CONVERSATION_STORE;
|
|
133
|
+
if (overridePath)
|
|
134
|
+
return path.resolve(/* turbopackIgnore: true */ overridePath);
|
|
135
|
+
return path.join(
|
|
136
|
+
/* turbopackIgnore: true */ normalizeWorkspacePath(workspacePath),
|
|
137
|
+
CONVERSATION_STORE_PATH,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function readThreadRuntimeSettings({ workspacePath, threadId }) {
|
|
142
|
+
const normalizedThreadId =
|
|
143
|
+
typeof threadId === "string" && threadId.trim() ? threadId.trim() : null;
|
|
144
|
+
if (!normalizedThreadId) return null;
|
|
145
|
+
try {
|
|
146
|
+
const store = readJsonFile(getConversationStorePath(workspacePath));
|
|
147
|
+
const conversations = Array.isArray(store?.conversations)
|
|
148
|
+
? store.conversations
|
|
149
|
+
: [];
|
|
150
|
+
const conversation = conversations.find(
|
|
151
|
+
(entry) =>
|
|
152
|
+
isRecord(entry) &&
|
|
153
|
+
entry.threadId === normalizedThreadId &&
|
|
154
|
+
entry.status !== "deleted",
|
|
155
|
+
);
|
|
156
|
+
const builtinToolSettings = conversation?.builtinToolSettings;
|
|
157
|
+
return normalizeRuntimeSettings(
|
|
158
|
+
builtinToolSettings?.[IMAGE_GENERATION_TOOL_ID] ??
|
|
159
|
+
builtinToolSettings?.imageGeneration,
|
|
160
|
+
);
|
|
161
|
+
} catch {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function resolveOutputDir(outputDir, workspacePath) {
|
|
167
|
+
if (path.isAbsolute(outputDir)) return outputDir;
|
|
168
|
+
return path.resolve(
|
|
169
|
+
/* turbopackIgnore: true */ normalizeWorkspacePath(workspacePath),
|
|
170
|
+
outputDir,
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function resolveSavePathBaseDir(savePathBaseDir, workspacePath) {
|
|
175
|
+
const workspace = normalizeWorkspacePath(workspacePath);
|
|
176
|
+
if (path.isAbsolute(savePathBaseDir)) return savePathBaseDir;
|
|
177
|
+
return path.resolve(/* turbopackIgnore: true */ workspace, savePathBaseDir);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function getImageGenerationToolDir() {
|
|
181
|
+
return TOOL_DIR;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function loadImageGenerationConfig(options = {}) {
|
|
185
|
+
const exampleConfig = readJsonFile(
|
|
186
|
+
path.join(TOOL_DIR, "config.example.json"),
|
|
187
|
+
);
|
|
188
|
+
const localConfig = readJsonFile(path.join(TOOL_DIR, "config.local.json"));
|
|
189
|
+
const threadRuntimeSettings = readThreadRuntimeSettings({
|
|
190
|
+
workspacePath: options.workspacePath,
|
|
191
|
+
threadId: options.threadId,
|
|
192
|
+
});
|
|
193
|
+
const runtimeSettings = normalizeRuntimeSettings(options.runtimeSettings);
|
|
194
|
+
const merged = mergeConfig(
|
|
195
|
+
mergeConfig(
|
|
196
|
+
mergeConfig(mergeConfig(DEFAULT_CONFIG, exampleConfig), localConfig),
|
|
197
|
+
threadRuntimeSettings,
|
|
198
|
+
),
|
|
199
|
+
runtimeSettings,
|
|
200
|
+
);
|
|
201
|
+
return {
|
|
202
|
+
...merged,
|
|
203
|
+
outputDir: resolveOutputDir(merged.outputDir, options.workspacePath),
|
|
204
|
+
savePathBaseDir: resolveSavePathBaseDir(
|
|
205
|
+
merged.savePathBaseDir,
|
|
206
|
+
options.workspacePath,
|
|
207
|
+
),
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function mimeToExtension(mimeType) {
|
|
212
|
+
if (mimeType === "image/jpeg") return "jpg";
|
|
213
|
+
if (mimeType === "image/webp") return "webp";
|
|
214
|
+
return "png";
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function normalizeSavePathPattern(value) {
|
|
218
|
+
if (typeof value !== "string") return null;
|
|
219
|
+
const pattern = value.trim().replace(/\\/g, "/");
|
|
220
|
+
if (!pattern || pattern.startsWith("/") || pattern.includes("\0"))
|
|
221
|
+
return null;
|
|
222
|
+
if (pattern.split("/").some((segment) => segment === "..")) return null;
|
|
223
|
+
if (!pattern.includes("<index>") && !pattern.includes("{index}")) return null;
|
|
224
|
+
if (!path.basename(pattern)) return null;
|
|
225
|
+
return pattern;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function savePathFromPattern({ pattern, index, mimeType, params }) {
|
|
229
|
+
const normalizedPattern = normalizeSavePathPattern(pattern);
|
|
230
|
+
if (!normalizedPattern) return null;
|
|
231
|
+
const extension = mimeToExtension(
|
|
232
|
+
mimeType || mimeFromOutputFormat(params?.outputFormat),
|
|
233
|
+
);
|
|
234
|
+
return normalizedPattern
|
|
235
|
+
.replace(/<index>/g, String(index))
|
|
236
|
+
.replace(/\{index\}/g, String(index))
|
|
237
|
+
.replace(/<ext>/g, extension)
|
|
238
|
+
.replace(/\{ext\}/g, extension);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function availableSavePathFromPattern({
|
|
242
|
+
pattern,
|
|
243
|
+
mimeType,
|
|
244
|
+
params,
|
|
245
|
+
config,
|
|
246
|
+
workspacePath,
|
|
247
|
+
}) {
|
|
248
|
+
for (let index = 1; index <= 999; index += 1) {
|
|
249
|
+
const savePath = savePathFromPattern({ pattern, index, mimeType, params });
|
|
250
|
+
if (!savePath) return null;
|
|
251
|
+
const resolved = resolveValidatedSavePath({
|
|
252
|
+
savePath,
|
|
253
|
+
config,
|
|
254
|
+
workspacePath,
|
|
255
|
+
});
|
|
256
|
+
if (
|
|
257
|
+
resolved &&
|
|
258
|
+
!fsSync.existsSync(/* turbopackIgnore: true */ resolved.absolutePath)
|
|
259
|
+
)
|
|
260
|
+
return savePath;
|
|
261
|
+
}
|
|
262
|
+
throw new Error("No available image savePathPattern slot.");
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function sniffMime(buffer, fallback) {
|
|
266
|
+
if (
|
|
267
|
+
buffer.length >= 8 &&
|
|
268
|
+
buffer[0] === 0x89 &&
|
|
269
|
+
buffer[1] === 0x50 &&
|
|
270
|
+
buffer[2] === 0x4e &&
|
|
271
|
+
buffer[3] === 0x47
|
|
272
|
+
) {
|
|
273
|
+
return "image/png";
|
|
274
|
+
}
|
|
275
|
+
if (buffer.length >= 3 && buffer[0] === 0xff && buffer[1] === 0xd8) {
|
|
276
|
+
return "image/jpeg";
|
|
277
|
+
}
|
|
278
|
+
if (
|
|
279
|
+
buffer.length >= 12 &&
|
|
280
|
+
buffer.subarray(0, 4).toString("ascii") === "RIFF" &&
|
|
281
|
+
buffer.subarray(8, 12).toString("ascii") === "WEBP"
|
|
282
|
+
) {
|
|
283
|
+
return "image/webp";
|
|
284
|
+
}
|
|
285
|
+
return fallback ?? "application/octet-stream";
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function assertSupportedImage(buffer, mimeType, maxBytes) {
|
|
289
|
+
if (buffer.byteLength > maxBytes) {
|
|
290
|
+
throw new Error(
|
|
291
|
+
`Input image is too large (${buffer.byteLength} bytes, max ${maxBytes}).`,
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
if (!SUPPORTED_MIME_TYPES.has(mimeType)) {
|
|
295
|
+
throw new Error(`Unsupported input image MIME type: ${mimeType}.`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function isPathInside(parent, candidate) {
|
|
300
|
+
const relative = path.relative(parent, candidate);
|
|
301
|
+
return (
|
|
302
|
+
Boolean(relative) &&
|
|
303
|
+
!relative.startsWith("..") &&
|
|
304
|
+
!path.isAbsolute(relative)
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function isPathInsideOrEqual(parent, candidate) {
|
|
309
|
+
const relative = path.relative(parent, candidate);
|
|
310
|
+
return (
|
|
311
|
+
relative === "" ||
|
|
312
|
+
(!relative.startsWith("..") && !path.isAbsolute(relative))
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function normalizeSavePath(savePath) {
|
|
317
|
+
if (typeof savePath !== "string") return null;
|
|
318
|
+
const normalized = savePath.trim().replace(/\\/g, "/");
|
|
319
|
+
return normalized || null;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function resolveValidatedSavePath({ savePath, config, workspacePath }) {
|
|
323
|
+
const normalized = normalizeSavePath(savePath);
|
|
324
|
+
if (!normalized) return null;
|
|
325
|
+
if (!config.allowSavePath) {
|
|
326
|
+
throw new Error("Custom image savePath is disabled in config.");
|
|
327
|
+
}
|
|
328
|
+
if (path.isAbsolute(normalized)) {
|
|
329
|
+
throw new Error("Image savePath must be relative to the workspace.");
|
|
330
|
+
}
|
|
331
|
+
if (normalized.split("/").some((part) => part === "..")) {
|
|
332
|
+
throw new Error("Image savePath cannot contain '..' path segments.");
|
|
333
|
+
}
|
|
334
|
+
if (normalized.endsWith("/")) {
|
|
335
|
+
throw new Error("Image savePath must include a file name.");
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const workspace = normalizeWorkspacePath(workspacePath);
|
|
339
|
+
const baseDir = path.resolve(config.savePathBaseDir);
|
|
340
|
+
const absolutePath = path.resolve(baseDir, normalized);
|
|
341
|
+
if (!isPathInsideOrEqual(workspace, baseDir)) {
|
|
342
|
+
throw new Error(
|
|
343
|
+
"Configured image savePathBaseDir is outside the workspace.",
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
if (!isPathInsideOrEqual(workspace, absolutePath)) {
|
|
347
|
+
throw new Error("Image savePath must resolve inside the workspace.");
|
|
348
|
+
}
|
|
349
|
+
if (!isPathInsideOrEqual(baseDir, absolutePath)) {
|
|
350
|
+
throw new Error("Image savePath must resolve inside savePathBaseDir.");
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return {
|
|
354
|
+
relativePath: path.relative(workspace, absolutePath).replace(/\\/g, "/"),
|
|
355
|
+
absolutePath,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function fileNameFromSavePath(savePath, fallback) {
|
|
360
|
+
if (!savePath) return fallback;
|
|
361
|
+
const baseName = path.basename(savePath);
|
|
362
|
+
return baseName && baseName !== "." ? baseName : fallback;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function parseDataUrl(value) {
|
|
366
|
+
const match = /^data:([^;,]+);base64,([\s\S]+)$/.exec(value);
|
|
367
|
+
if (!match) throw new Error("Invalid data URL image input.");
|
|
368
|
+
return {
|
|
369
|
+
mimeType: match[1].toLowerCase(),
|
|
370
|
+
buffer: Buffer.from(match[2], "base64"),
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
async function loadPathImage(source, config, workspacePath) {
|
|
375
|
+
const imagePath = path.resolve(
|
|
376
|
+
/* turbopackIgnore: true */ workspacePath,
|
|
377
|
+
source.value,
|
|
378
|
+
);
|
|
379
|
+
const outputPath = path.resolve(/* turbopackIgnore: true */ config.outputDir);
|
|
380
|
+
if (
|
|
381
|
+
!isPathInside(workspacePath, imagePath) &&
|
|
382
|
+
!isPathInside(outputPath, imagePath)
|
|
383
|
+
) {
|
|
384
|
+
throw new Error(
|
|
385
|
+
`Input image path is outside the workspace and image-generation output directory: ${source.value}`,
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
const buffer = await fs.readFile(imagePath);
|
|
389
|
+
const mimeType = sniffMime(buffer, source.mimeType);
|
|
390
|
+
assertSupportedImage(buffer, mimeType, config.maxInputBytes);
|
|
391
|
+
return {
|
|
392
|
+
buffer,
|
|
393
|
+
mimeType,
|
|
394
|
+
filename:
|
|
395
|
+
source.filename ||
|
|
396
|
+
path.basename(imagePath) ||
|
|
397
|
+
`input.${mimeToExtension(mimeType)}`,
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
async function loadUrlImage(source, config) {
|
|
402
|
+
const url = new URL(source.value);
|
|
403
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
404
|
+
throw new Error("Only http(s) image URLs are supported.");
|
|
405
|
+
}
|
|
406
|
+
const response = await fetch(url, {
|
|
407
|
+
signal: AbortSignal.timeout(config.downloadTimeoutMs),
|
|
408
|
+
});
|
|
409
|
+
if (!response.ok) {
|
|
410
|
+
throw new Error(`Failed to download image URL: ${response.status}.`);
|
|
411
|
+
}
|
|
412
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
413
|
+
const mimeType = sniffMime(
|
|
414
|
+
buffer,
|
|
415
|
+
response.headers.get("content-type")?.split(";")[0] ?? source.mimeType,
|
|
416
|
+
);
|
|
417
|
+
assertSupportedImage(buffer, mimeType, config.maxInputBytes);
|
|
418
|
+
return {
|
|
419
|
+
buffer,
|
|
420
|
+
mimeType,
|
|
421
|
+
filename:
|
|
422
|
+
source.filename ||
|
|
423
|
+
path.basename(url.pathname) ||
|
|
424
|
+
`input.${mimeToExtension(mimeType)}`,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
async function loadInlineImage(source, config) {
|
|
429
|
+
const parsed =
|
|
430
|
+
source.type === "data_url"
|
|
431
|
+
? parseDataUrl(source.value)
|
|
432
|
+
: {
|
|
433
|
+
mimeType: source.mimeType ?? "application/octet-stream",
|
|
434
|
+
buffer: Buffer.from(source.value, "base64"),
|
|
435
|
+
};
|
|
436
|
+
const mimeType = sniffMime(parsed.buffer, parsed.mimeType);
|
|
437
|
+
assertSupportedImage(parsed.buffer, mimeType, config.maxInputBytes);
|
|
438
|
+
return {
|
|
439
|
+
buffer: parsed.buffer,
|
|
440
|
+
mimeType,
|
|
441
|
+
filename: source.filename || `input.${mimeToExtension(mimeType)}`,
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function normalizeParams(input, config) {
|
|
446
|
+
return {
|
|
447
|
+
model: input.model || config.model,
|
|
448
|
+
...(typeof input.size === "string" && input.size.trim()
|
|
449
|
+
? { size: input.size.trim() }
|
|
450
|
+
: {}),
|
|
451
|
+
...(typeof input.quality === "string" && input.quality.trim()
|
|
452
|
+
? { quality: input.quality.trim() }
|
|
453
|
+
: {}),
|
|
454
|
+
...(typeof input.outputFormat === "string" && input.outputFormat.trim()
|
|
455
|
+
? { outputFormat: input.outputFormat.trim() }
|
|
456
|
+
: {}),
|
|
457
|
+
...(typeof input.background === "string" && input.background.trim()
|
|
458
|
+
? { background: input.background.trim() }
|
|
459
|
+
: {}),
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function appendFormParams(formData, params, prompt) {
|
|
464
|
+
formData.set("model", params.model);
|
|
465
|
+
formData.set("prompt", prompt);
|
|
466
|
+
formData.set("n", "1");
|
|
467
|
+
if (params.size) formData.set("size", params.size);
|
|
468
|
+
if (params.quality) formData.set("quality", params.quality);
|
|
469
|
+
if (params.outputFormat) formData.set("output_format", params.outputFormat);
|
|
470
|
+
if (params.background) formData.set("background", params.background);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function buildImageApiPrompt(prompt, config) {
|
|
474
|
+
const normalizedPrompt = String(prompt ?? "");
|
|
475
|
+
if (!config.preservePrompt) return normalizedPrompt;
|
|
476
|
+
if (normalizedPrompt.startsWith(PROMPT_REWRITE_GUARD_PREFIX)) {
|
|
477
|
+
return normalizedPrompt;
|
|
478
|
+
}
|
|
479
|
+
return `${PROMPT_REWRITE_GUARD_PREFIX}\n${normalizedPrompt}`;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function mimeFromOutputFormat(outputFormat) {
|
|
483
|
+
if (outputFormat === "jpeg") return "image/jpeg";
|
|
484
|
+
if (outputFormat === "webp") return "image/webp";
|
|
485
|
+
return "image/png";
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function summarizeRawResponse(value) {
|
|
489
|
+
if (!isRecord(value)) return undefined;
|
|
490
|
+
const data = Array.isArray(value.data)
|
|
491
|
+
? value.data.map((item) => {
|
|
492
|
+
if (!isRecord(item)) return item;
|
|
493
|
+
return {
|
|
494
|
+
...item,
|
|
495
|
+
b64_json:
|
|
496
|
+
typeof item.b64_json === "string" ? "[base64]" : item.b64_json,
|
|
497
|
+
};
|
|
498
|
+
})
|
|
499
|
+
: undefined;
|
|
500
|
+
return {
|
|
501
|
+
...(typeof value.id === "string" ? { id: value.id } : {}),
|
|
502
|
+
...(typeof value.created === "number" ? { created: value.created } : {}),
|
|
503
|
+
...(data ? { data } : {}),
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
async function fetchImageUrl(url, config) {
|
|
508
|
+
const response = await fetch(url, {
|
|
509
|
+
signal: AbortSignal.timeout(config.downloadTimeoutMs),
|
|
510
|
+
});
|
|
511
|
+
if (!response.ok) throw new Error("Failed to fetch generated image URL.");
|
|
512
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
513
|
+
const mimeType = sniffMime(
|
|
514
|
+
buffer,
|
|
515
|
+
response.headers.get("content-type")?.split(";")[0] ?? undefined,
|
|
516
|
+
);
|
|
517
|
+
return { buffer, mimeType };
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
async function requestImageApi({ config, input, params, inputImages }) {
|
|
521
|
+
const endpoint =
|
|
522
|
+
inputImages.length > 0
|
|
523
|
+
? `${config.baseUrl}/images/edits`
|
|
524
|
+
: `${config.baseUrl}/images/generations`;
|
|
525
|
+
const headers = new Headers({
|
|
526
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
527
|
+
});
|
|
528
|
+
const prompt = buildImageApiPrompt(input.prompt, config);
|
|
529
|
+
let body;
|
|
530
|
+
|
|
531
|
+
if (inputImages.length > 0) {
|
|
532
|
+
const formData = new FormData();
|
|
533
|
+
appendFormParams(formData, params, prompt);
|
|
534
|
+
for (const image of inputImages) {
|
|
535
|
+
formData.append(
|
|
536
|
+
"image[]",
|
|
537
|
+
new Blob([new Uint8Array(image.buffer)], { type: image.mimeType }),
|
|
538
|
+
image.filename,
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
body = formData;
|
|
542
|
+
} else {
|
|
543
|
+
headers.set("content-type", "application/json");
|
|
544
|
+
body = JSON.stringify({
|
|
545
|
+
model: params.model,
|
|
546
|
+
prompt,
|
|
547
|
+
n: 1,
|
|
548
|
+
...(params.size ? { size: params.size } : {}),
|
|
549
|
+
...(params.quality ? { quality: params.quality } : {}),
|
|
550
|
+
...(params.outputFormat ? { output_format: params.outputFormat } : {}),
|
|
551
|
+
...(params.background ? { background: params.background } : {}),
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
const response = await fetch(endpoint, {
|
|
556
|
+
method: "POST",
|
|
557
|
+
headers,
|
|
558
|
+
body,
|
|
559
|
+
signal: AbortSignal.timeout(config.requestTimeoutMs),
|
|
560
|
+
});
|
|
561
|
+
const responseText = await response.text();
|
|
562
|
+
let payload = null;
|
|
563
|
+
try {
|
|
564
|
+
payload = responseText ? JSON.parse(responseText) : null;
|
|
565
|
+
} catch {
|
|
566
|
+
payload = responseText;
|
|
567
|
+
}
|
|
568
|
+
if (!response.ok) {
|
|
569
|
+
const message =
|
|
570
|
+
isRecord(payload) && isRecord(payload.error)
|
|
571
|
+
? JSON.stringify(payload.error)
|
|
572
|
+
: responseText || `${response.status} ${response.statusText}`;
|
|
573
|
+
throw new Error(`Image API request failed: ${message}`);
|
|
574
|
+
}
|
|
575
|
+
return payload;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
async function readIndex(outputDir) {
|
|
579
|
+
try {
|
|
580
|
+
const raw = await fs.readFile(
|
|
581
|
+
/* turbopackIgnore: true */ path.join(outputDir, INDEX_FILE),
|
|
582
|
+
"utf8",
|
|
583
|
+
);
|
|
584
|
+
const parsed = JSON.parse(raw);
|
|
585
|
+
return {
|
|
586
|
+
images: isRecord(parsed?.images) ? parsed.images : {},
|
|
587
|
+
records: Array.isArray(parsed?.records) ? parsed.records : [],
|
|
588
|
+
};
|
|
589
|
+
} catch (error) {
|
|
590
|
+
if (error.code === "ENOENT") return { images: {}, records: [] };
|
|
591
|
+
throw error;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
async function writeIndex(outputDir, index) {
|
|
596
|
+
await fs.mkdir(/* turbopackIgnore: true */ outputDir, { recursive: true });
|
|
597
|
+
const tempPath = path.join(outputDir, `${INDEX_FILE}.tmp`);
|
|
598
|
+
await fs.writeFile(
|
|
599
|
+
/* turbopackIgnore: true */ tempPath,
|
|
600
|
+
JSON.stringify(index, null, 2),
|
|
601
|
+
"utf8",
|
|
602
|
+
);
|
|
603
|
+
await fs.rename(
|
|
604
|
+
/* turbopackIgnore: true */ tempPath,
|
|
605
|
+
/* turbopackIgnore: true */ path.join(outputDir, INDEX_FILE),
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function storedImageUrl(id, workspacePath) {
|
|
610
|
+
const params = new URLSearchParams();
|
|
611
|
+
if (workspacePath) params.set("workspacePath", workspacePath);
|
|
612
|
+
const query = params.toString();
|
|
613
|
+
return `/api/tools/image-generation/files/${id}${query ? `?${query}` : ""}`;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function localImageUrl(filePath) {
|
|
617
|
+
const params = new URLSearchParams({ path: filePath });
|
|
618
|
+
return `/api/local-files/image?${params.toString()}`;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
function hydrateImage(image, workspacePath) {
|
|
622
|
+
if (!isRecord(image) || typeof image.id !== "string") return null;
|
|
623
|
+
if (
|
|
624
|
+
typeof image.savedPath === "string" &&
|
|
625
|
+
typeof image.absolutePath === "string"
|
|
626
|
+
) {
|
|
627
|
+
return {
|
|
628
|
+
...image,
|
|
629
|
+
url: localImageUrl(image.absolutePath),
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
return {
|
|
633
|
+
...image,
|
|
634
|
+
url: storedImageUrl(image.id, workspacePath),
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function hydrateRecord(record, fallbackWorkspacePath) {
|
|
639
|
+
if (!isRecord(record) || typeof record.id !== "string") return null;
|
|
640
|
+
const workspacePath =
|
|
641
|
+
typeof record.workspacePath === "string" && record.workspacePath.trim()
|
|
642
|
+
? record.workspacePath
|
|
643
|
+
: fallbackWorkspacePath;
|
|
644
|
+
return {
|
|
645
|
+
...record,
|
|
646
|
+
threadId: typeof record.threadId === "string" ? record.threadId : null,
|
|
647
|
+
workspacePath,
|
|
648
|
+
images: Array.isArray(record.images)
|
|
649
|
+
? record.images
|
|
650
|
+
.map((image) => hydrateImage(image, workspacePath))
|
|
651
|
+
.filter(Boolean)
|
|
652
|
+
: [],
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
export async function saveGeneratedImage({
|
|
657
|
+
config,
|
|
658
|
+
data,
|
|
659
|
+
mimeType,
|
|
660
|
+
revisedPrompt,
|
|
661
|
+
actualParams,
|
|
662
|
+
width,
|
|
663
|
+
height,
|
|
664
|
+
workspacePath,
|
|
665
|
+
savePath,
|
|
666
|
+
}) {
|
|
667
|
+
if (data.byteLength > config.maxOutputBytes) {
|
|
668
|
+
throw new Error(
|
|
669
|
+
`Generated image is too large (${data.byteLength} bytes, max ${config.maxOutputBytes}).`,
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
const resolvedSavePath = resolveValidatedSavePath({
|
|
673
|
+
savePath,
|
|
674
|
+
config,
|
|
675
|
+
workspacePath,
|
|
676
|
+
});
|
|
677
|
+
await fs.mkdir(/* turbopackIgnore: true */ config.outputDir, {
|
|
678
|
+
recursive: true,
|
|
679
|
+
});
|
|
680
|
+
const id = createId();
|
|
681
|
+
const fileName = `${id}.${mimeToExtension(mimeType)}`;
|
|
682
|
+
const absolutePath =
|
|
683
|
+
resolvedSavePath?.absolutePath ?? path.join(config.outputDir, fileName);
|
|
684
|
+
if (resolvedSavePath) {
|
|
685
|
+
await fs.mkdir(
|
|
686
|
+
/* turbopackIgnore: true */ path.dirname(resolvedSavePath.absolutePath),
|
|
687
|
+
{
|
|
688
|
+
recursive: true,
|
|
689
|
+
},
|
|
690
|
+
);
|
|
691
|
+
await fs.writeFile(
|
|
692
|
+
/* turbopackIgnore: true */ resolvedSavePath.absolutePath,
|
|
693
|
+
data,
|
|
694
|
+
{ flag: "wx" },
|
|
695
|
+
);
|
|
696
|
+
} else {
|
|
697
|
+
await fs.writeFile(/* turbopackIgnore: true */ absolutePath, data);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
const image = {
|
|
701
|
+
id,
|
|
702
|
+
fileName: fileNameFromSavePath(resolvedSavePath?.relativePath, fileName),
|
|
703
|
+
mimeType,
|
|
704
|
+
sizeBytes: data.byteLength,
|
|
705
|
+
url: resolvedSavePath
|
|
706
|
+
? localImageUrl(resolvedSavePath.absolutePath)
|
|
707
|
+
: storedImageUrl(id, workspacePath),
|
|
708
|
+
...(resolvedSavePath ? { savedPath: resolvedSavePath.relativePath } : {}),
|
|
709
|
+
...(width ? { width } : {}),
|
|
710
|
+
...(height ? { height } : {}),
|
|
711
|
+
...(revisedPrompt ? { revisedPrompt } : {}),
|
|
712
|
+
...(actualParams ? { actualParams } : {}),
|
|
713
|
+
};
|
|
714
|
+
const index = await readIndex(config.outputDir);
|
|
715
|
+
index.images[id] = {
|
|
716
|
+
...image,
|
|
717
|
+
createdAt: nowIso(),
|
|
718
|
+
absolutePath,
|
|
719
|
+
};
|
|
720
|
+
await writeIndex(config.outputDir, index);
|
|
721
|
+
return image;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
async function saveImageGenerationRecord(config, record) {
|
|
725
|
+
const index = await readIndex(config.outputDir);
|
|
726
|
+
index.records = [
|
|
727
|
+
record,
|
|
728
|
+
...index.records.filter((entry) => entry?.id !== record.id),
|
|
729
|
+
];
|
|
730
|
+
await writeIndex(config.outputDir, index);
|
|
731
|
+
return record;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
function outputToRecord({ output, id, workspacePath, threadId, source }) {
|
|
735
|
+
const timestamp = nowIso();
|
|
736
|
+
return {
|
|
737
|
+
id,
|
|
738
|
+
requestId: output.requestId,
|
|
739
|
+
threadId: threadId || null,
|
|
740
|
+
workspacePath,
|
|
741
|
+
source,
|
|
742
|
+
status: output.status,
|
|
743
|
+
provider: output.provider,
|
|
744
|
+
prompt: output.prompt,
|
|
745
|
+
...(output.revisedPrompt ? { revisedPrompt: output.revisedPrompt } : {}),
|
|
746
|
+
requestedParams: output.requestedParams,
|
|
747
|
+
actualParams: output.actualParams,
|
|
748
|
+
images: output.images,
|
|
749
|
+
durationMs: output.durationMs,
|
|
750
|
+
...(output.error ? { error: output.error } : {}),
|
|
751
|
+
createdAt: timestamp,
|
|
752
|
+
updatedAt: timestamp,
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
function failedOutput({ recordId, config, input, params, startedAt, error }) {
|
|
757
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
758
|
+
return {
|
|
759
|
+
status: "failed",
|
|
760
|
+
recordId,
|
|
761
|
+
requestId: `img_error_${Date.now().toString(36)}`,
|
|
762
|
+
provider: config?.provider ?? "unknown",
|
|
763
|
+
prompt: typeof input?.prompt === "string" ? input.prompt : "",
|
|
764
|
+
requestedParams: params ?? {},
|
|
765
|
+
actualParams: {},
|
|
766
|
+
images: [],
|
|
767
|
+
durationMs: Date.now() - startedAt,
|
|
768
|
+
error: message,
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
function normalizeSource(value) {
|
|
773
|
+
return value === "mcp" ? "mcp" : "api";
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
async function generateImageOrThrow({ input, config, workspacePath }) {
|
|
777
|
+
if (
|
|
778
|
+
!isRecord(input) ||
|
|
779
|
+
typeof input.prompt !== "string" ||
|
|
780
|
+
!input.prompt.trim()
|
|
781
|
+
) {
|
|
782
|
+
throw new Error("Image generation prompt is required.");
|
|
783
|
+
}
|
|
784
|
+
if (!config.apiKey) {
|
|
785
|
+
throw new Error(
|
|
786
|
+
'Image generation API key is missing. Pass builtinToolSettings["image-generation"].apiKey or configure tools/image-generation/config.local.json.',
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
if (!config.model) {
|
|
790
|
+
throw new Error(
|
|
791
|
+
'Image generation model is missing. Pass builtinToolSettings["image-generation"].model or configure tools/image-generation/config.local.json.',
|
|
792
|
+
);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
const params = normalizeParams(input, config);
|
|
796
|
+
const inputImages = await Promise.all(
|
|
797
|
+
(input.images ?? []).map((source) =>
|
|
798
|
+
source.type === "path"
|
|
799
|
+
? loadPathImage(source, config, workspacePath)
|
|
800
|
+
: source.type === "url"
|
|
801
|
+
? loadUrlImage(source, config)
|
|
802
|
+
: loadInlineImage(source, config),
|
|
803
|
+
),
|
|
804
|
+
);
|
|
805
|
+
const payload = await requestImageApi({ config, input, params, inputImages });
|
|
806
|
+
const items =
|
|
807
|
+
isRecord(payload) && Array.isArray(payload.data) ? payload.data : [];
|
|
808
|
+
let image = null;
|
|
809
|
+
for (const item of items) {
|
|
810
|
+
if (!isRecord(item)) continue;
|
|
811
|
+
const revisedPrompt =
|
|
812
|
+
typeof item.revised_prompt === "string" ? item.revised_prompt : undefined;
|
|
813
|
+
let buffer = null;
|
|
814
|
+
let mimeType = mimeFromOutputFormat(params.outputFormat);
|
|
815
|
+
if (typeof item.b64_json === "string") {
|
|
816
|
+
buffer = Buffer.from(item.b64_json, "base64");
|
|
817
|
+
mimeType = sniffMime(buffer, mimeType);
|
|
818
|
+
} else if (typeof item.url === "string") {
|
|
819
|
+
const fetched = await fetchImageUrl(item.url, config);
|
|
820
|
+
buffer = fetched.buffer;
|
|
821
|
+
mimeType = fetched.mimeType;
|
|
822
|
+
}
|
|
823
|
+
if (!buffer) continue;
|
|
824
|
+
const savePath =
|
|
825
|
+
typeof input.savePath === "string" && input.savePath.trim()
|
|
826
|
+
? input.savePath
|
|
827
|
+
: availableSavePathFromPattern({
|
|
828
|
+
pattern: config.savePathPattern,
|
|
829
|
+
mimeType,
|
|
830
|
+
params,
|
|
831
|
+
config,
|
|
832
|
+
workspacePath,
|
|
833
|
+
});
|
|
834
|
+
image = await saveGeneratedImage({
|
|
835
|
+
config,
|
|
836
|
+
data: buffer,
|
|
837
|
+
mimeType,
|
|
838
|
+
revisedPrompt,
|
|
839
|
+
actualParams: params,
|
|
840
|
+
workspacePath,
|
|
841
|
+
savePath,
|
|
842
|
+
});
|
|
843
|
+
break;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
if (!image) {
|
|
847
|
+
throw new Error("Image API response did not include any images.");
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
return { image, params, payload };
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
export async function generateImage(input, options = {}) {
|
|
854
|
+
const workspacePath = normalizeWorkspacePath(options.workspacePath);
|
|
855
|
+
const threadId =
|
|
856
|
+
typeof options.threadId === "string" && options.threadId.trim()
|
|
857
|
+
? options.threadId.trim()
|
|
858
|
+
: null;
|
|
859
|
+
const source = normalizeSource(options.source);
|
|
860
|
+
const recordId = createId();
|
|
861
|
+
const startedAt = Date.now();
|
|
862
|
+
const config = loadImageGenerationConfig({
|
|
863
|
+
workspacePath,
|
|
864
|
+
threadId,
|
|
865
|
+
runtimeSettings: options.runtimeSettings,
|
|
866
|
+
});
|
|
867
|
+
let params = null;
|
|
868
|
+
|
|
869
|
+
try {
|
|
870
|
+
if (isRecord(input)) params = normalizeParams(input, config);
|
|
871
|
+
const result = await generateImageOrThrow({ input, config, workspacePath });
|
|
872
|
+
params = result.params;
|
|
873
|
+
const revisedPrompt = result.image.revisedPrompt;
|
|
874
|
+
const output = {
|
|
875
|
+
status: "completed",
|
|
876
|
+
recordId,
|
|
877
|
+
requestId:
|
|
878
|
+
isRecord(result.payload) && typeof result.payload.id === "string"
|
|
879
|
+
? result.payload.id
|
|
880
|
+
: `img_${Date.now().toString(36)}`,
|
|
881
|
+
provider: config.provider,
|
|
882
|
+
prompt: input.prompt,
|
|
883
|
+
...(revisedPrompt ? { revisedPrompt } : {}),
|
|
884
|
+
requestedParams: params,
|
|
885
|
+
actualParams: params,
|
|
886
|
+
images: [result.image],
|
|
887
|
+
durationMs: Date.now() - startedAt,
|
|
888
|
+
rawResponseSummary: summarizeRawResponse(result.payload),
|
|
889
|
+
};
|
|
890
|
+
const record = outputToRecord({
|
|
891
|
+
output,
|
|
892
|
+
id: recordId,
|
|
893
|
+
workspacePath,
|
|
894
|
+
threadId,
|
|
895
|
+
source,
|
|
896
|
+
});
|
|
897
|
+
await saveImageGenerationRecord(config, record);
|
|
898
|
+
return output;
|
|
899
|
+
} catch (error) {
|
|
900
|
+
const output = failedOutput({
|
|
901
|
+
recordId,
|
|
902
|
+
config,
|
|
903
|
+
input,
|
|
904
|
+
params,
|
|
905
|
+
startedAt,
|
|
906
|
+
error,
|
|
907
|
+
});
|
|
908
|
+
const record = outputToRecord({
|
|
909
|
+
output,
|
|
910
|
+
id: recordId,
|
|
911
|
+
workspacePath,
|
|
912
|
+
threadId,
|
|
913
|
+
source,
|
|
914
|
+
});
|
|
915
|
+
await saveImageGenerationRecord(config, record);
|
|
916
|
+
return output;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
export async function listImageGenerationRecords(options = {}) {
|
|
921
|
+
const workspacePath = normalizeWorkspacePath(options.workspacePath);
|
|
922
|
+
const config = loadImageGenerationConfig({ workspacePath });
|
|
923
|
+
const threadId =
|
|
924
|
+
typeof options.threadId === "string" && options.threadId.trim()
|
|
925
|
+
? options.threadId.trim()
|
|
926
|
+
: null;
|
|
927
|
+
const limit = Number.isFinite(options.limit)
|
|
928
|
+
? Math.max(1, Math.floor(options.limit))
|
|
929
|
+
: 20;
|
|
930
|
+
const index = await readIndex(config.outputDir);
|
|
931
|
+
return index.records
|
|
932
|
+
.map((record) => hydrateRecord(record, workspacePath))
|
|
933
|
+
.filter(Boolean)
|
|
934
|
+
.filter((record) => !threadId || record.threadId === threadId)
|
|
935
|
+
.sort((left, right) =>
|
|
936
|
+
String(right.updatedAt ?? right.createdAt ?? "").localeCompare(
|
|
937
|
+
String(left.updatedAt ?? left.createdAt ?? ""),
|
|
938
|
+
),
|
|
939
|
+
)
|
|
940
|
+
.slice(0, limit);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
export async function getStoredImage(options, id) {
|
|
944
|
+
const workspacePath = normalizeWorkspacePath(options?.workspacePath);
|
|
945
|
+
if (!/^[a-f0-9]{32}$/.test(id)) return null;
|
|
946
|
+
const config = loadImageGenerationConfig({ workspacePath });
|
|
947
|
+
const index = await readIndex(config.outputDir);
|
|
948
|
+
const image = index.images[id];
|
|
949
|
+
if (!image) return null;
|
|
950
|
+
const absolutePath =
|
|
951
|
+
typeof image.absolutePath === "string"
|
|
952
|
+
? image.absolutePath
|
|
953
|
+
: path.join(config.outputDir, image.fileName);
|
|
954
|
+
const resolvedOutputDir = path.resolve(config.outputDir);
|
|
955
|
+
const resolvedPath = path.resolve(absolutePath);
|
|
956
|
+
if (typeof image.savedPath === "string") {
|
|
957
|
+
const workspace = normalizeWorkspacePath(workspacePath);
|
|
958
|
+
if (!isPathInsideOrEqual(workspace, resolvedPath)) {
|
|
959
|
+
return null;
|
|
960
|
+
}
|
|
961
|
+
return {
|
|
962
|
+
...image,
|
|
963
|
+
url: localImageUrl(resolvedPath),
|
|
964
|
+
absolutePath: resolvedPath,
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
if (
|
|
968
|
+
resolvedPath !== resolvedOutputDir &&
|
|
969
|
+
!resolvedPath.startsWith(`${resolvedOutputDir}${path.sep}`)
|
|
970
|
+
) {
|
|
971
|
+
return null;
|
|
972
|
+
}
|
|
973
|
+
return {
|
|
974
|
+
...image,
|
|
975
|
+
url: storedImageUrl(id, workspacePath),
|
|
976
|
+
absolutePath: resolvedPath,
|
|
977
|
+
};
|
|
978
|
+
}
|