vibepulse 0.2.2 → 0.3.1-beta
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/app-path-routes-manifest.json +1 -0
- package/.next/build-manifest.json +2 -2
- package/.next/cache/.previewinfo +1 -1
- package/.next/cache/.rscinfo +1 -1
- package/.next/cache/.tsbuildinfo +1 -1
- package/.next/cache/config.json +3 -3
- package/.next/fallback-build-manifest.json +2 -2
- package/.next/prerender-manifest.json +3 -3
- package/.next/routes-manifest.json +8 -0
- package/.next/server/app/_global-error/page.js +1 -1
- package/.next/server/app/_global-error/page.js.nft.json +1 -1
- 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.js +1 -1
- package/.next/server/app/_not-found/page.js.nft.json +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 +2 -2
- package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- 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/api/node/events/route.js +2 -2
- package/.next/server/app/api/node/events/route.js.nft.json +1 -1
- package/.next/server/app/api/node/sessions/[id]/delete/route.js +2 -2
- package/.next/server/app/api/node/sessions/[id]/delete/route.js.nft.json +1 -1
- package/.next/server/app/api/node/sessions/[id]/open-editor/route.js +2 -2
- package/.next/server/app/api/node/sessions/[id]/open-editor/route.js.nft.json +1 -1
- package/.next/server/app/api/node/sessions/route.js +6 -4
- package/.next/server/app/api/node/sessions/route.js.nft.json +1 -1
- package/.next/server/app/api/nodes/route.js +2 -2
- package/.next/server/app/api/nodes/route.js.nft.json +1 -1
- package/.next/server/app/api/opencode-config/route.js +2 -2
- package/.next/server/app/api/opencode-config/route.js.nft.json +1 -1
- package/.next/server/app/api/opencode-config/status/route.js +2 -2
- package/.next/server/app/api/opencode-config/status/route.js.nft.json +1 -1
- package/.next/server/app/api/opencode-events/route.js +3 -3
- package/.next/server/app/api/opencode-events/route.js.nft.json +1 -1
- package/.next/server/app/api/profiles/[id]/apply/route.js +2 -2
- package/.next/server/app/api/profiles/[id]/apply/route.js.nft.json +1 -1
- package/.next/server/app/api/profiles/[id]/export/route.js +2 -2
- package/.next/server/app/api/profiles/[id]/export/route.js.nft.json +1 -1
- package/.next/server/app/api/profiles/[id]/route.js +2 -2
- package/.next/server/app/api/profiles/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/profiles/import/route.js +2 -2
- package/.next/server/app/api/profiles/import/route.js.nft.json +1 -1
- package/.next/server/app/api/profiles/route.js +2 -2
- package/.next/server/app/api/profiles/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/archive/route.js +3 -2
- package/.next/server/app/api/sessions/[id]/archive/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/delete/route.js +4 -3
- package/.next/server/app/api/sessions/[id]/delete/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/open-editor/route.js +2 -2
- package/.next/server/app/api/sessions/[id]/open-editor/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/restore/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/sessions/[id]/restore/route/build-manifest.json +11 -0
- package/.next/server/app/api/sessions/[id]/restore/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/sessions/[id]/restore/route.js +8 -0
- package/.next/server/app/api/sessions/[id]/restore/route.js.map +5 -0
- package/.next/server/app/api/sessions/[id]/restore/route.js.nft.json +1 -0
- package/.next/server/app/api/sessions/[id]/restore/route_client-reference-manifest.js +2 -0
- package/.next/server/app/api/sessions/[id]/route.js +2 -2
- package/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/route.js +5 -3
- package/.next/server/app/api/sessions/route.js.nft.json +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +3 -3
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +1 -0
- package/.next/server/chunks/[root-of-the-server]__005eb0c5._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__005eb0c5._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__09e90d57._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__09e90d57._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__18dd0ce9._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__18dd0ce9._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__19468536._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__19468536._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__1b87ec42._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__1b87ec42._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__2b912935._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__2b912935._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__303d3bac._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__303d3bac._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__3fac2b91._.js +5 -0
- package/.next/server/chunks/[root-of-the-server]__3fac2b91._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__43440b8d._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__43440b8d._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__438f8bbe._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__438f8bbe._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__4a0bfb55._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__4a0bfb55._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__534c3949._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__534c3949._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__59160266._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__59160266._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__6f812da0._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__6f812da0._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__71aac504._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__71aac504._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__907a8bf2._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__907a8bf2._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__92089220._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__92089220._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__9b7bc2d0._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__9b7bc2d0._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__b2640944._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__b2640944._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__c2267cf1._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__c2267cf1._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__d7f7e6dd._.js +3 -0
- package/.next/server/chunks/{[root-of-the-server]__6924c09d._.js.map → [root-of-the-server]__d7f7e6dd._.js.map} +1 -1
- package/.next/server/chunks/[root-of-the-server]__d8e61048._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__d8e61048._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__f6d0d488._.js +3 -0
- package/.next/server/chunks/{[root-of-the-server]__192ed2f4._.js.map → [root-of-the-server]__f6d0d488._.js.map} +1 -1
- package/.next/server/chunks/[root-of-the-server]__fa559e1e._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__fa559e1e._.js.map +1 -0
- package/.next/server/chunks/_next-internal_server_app_api_sessions_[id]_restore_route_actions_af7d6b6c.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_sessions_[id]_restore_route_actions_af7d6b6c.js.map +1 -0
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_7e181e75.js +1 -1
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_7e181e75.js.map +1 -1
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_aca45402.js +1 -1
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_aca45402.js.map +1 -1
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_b054aff3.js +1 -1
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_b054aff3.js.map +1 -1
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_fa835ac3.js +2 -2
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_fa835ac3.js.map +1 -1
- package/.next/server/chunks/src_lib_opencodeConfig_ts_8e209941._.js +3 -0
- package/.next/server/chunks/src_lib_opencodeConfig_ts_8e209941._.js.map +1 -0
- package/.next/server/chunks/src_lib_session-providers_claudeCode_ts_0f9590ed._.js +3 -0
- package/.next/server/chunks/src_lib_session-providers_claudeCode_ts_0f9590ed._.js.map +1 -0
- package/.next/server/chunks/ssr/{[root-of-the-server]__631e12d0._.js → [root-of-the-server]__c91a8380._.js} +2 -2
- package/.next/server/chunks/ssr/{[root-of-the-server]__631e12d0._.js.map → [root-of-the-server]__c91a8380._.js.map} +1 -1
- package/.next/server/chunks/ssr/src_app_page_tsx_a7111f3e._.js +3 -3
- package/.next/server/chunks/ssr/src_app_page_tsx_a7111f3e._.js.map +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +2 -2
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +1 -0
- package/.next/standalone/.next/build-manifest.json +2 -2
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/routes-manifest.json +8 -0
- package/.next/standalone/.next/server/app/_global-error/page.js +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +2 -2
- package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/node/events/route.js +2 -2
- package/.next/standalone/.next/server/app/api/node/events/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/delete/route.js +2 -2
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/delete/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/open-editor/route.js +2 -2
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/open-editor/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/node/sessions/route.js +6 -4
- package/.next/standalone/.next/server/app/api/node/sessions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/nodes/route.js +2 -2
- package/.next/standalone/.next/server/app/api/nodes/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/opencode-config/route.js +2 -2
- package/.next/standalone/.next/server/app/api/opencode-config/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/opencode-config/status/route.js +2 -2
- package/.next/standalone/.next/server/app/api/opencode-config/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/opencode-events/route.js +3 -3
- package/.next/standalone/.next/server/app/api/opencode-events/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/profiles/[id]/apply/route.js +2 -2
- package/.next/standalone/.next/server/app/api/profiles/[id]/apply/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/profiles/[id]/export/route.js +2 -2
- package/.next/standalone/.next/server/app/api/profiles/[id]/export/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/profiles/[id]/route.js +2 -2
- package/.next/standalone/.next/server/app/api/profiles/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/profiles/import/route.js +2 -2
- package/.next/standalone/.next/server/app/api/profiles/import/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/profiles/route.js +2 -2
- package/.next/standalone/.next/server/app/api/profiles/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/archive/route.js +3 -2
- package/.next/standalone/.next/server/app/api/sessions/[id]/archive/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/delete/route.js +4 -3
- package/.next/standalone/.next/server/app/api/sessions/[id]/delete/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/open-editor/route.js +2 -2
- package/.next/standalone/.next/server/app/api/sessions/[id]/open-editor/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route.js +8 -0
- package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/sessions/[id]/route.js +2 -2
- package/.next/standalone/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/route.js +5 -3
- package/.next/standalone/.next/server/app/api/sessions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +3 -3
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__005eb0c5._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__09e90d57._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__18dd0ce9._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__19468536._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1b87ec42._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__2b912935._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__303d3bac._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__3fac2b91._.js +5 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__43440b8d._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__438f8bbe._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__4a0bfb55._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__534c3949._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__59160266._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__6f812da0._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__71aac504._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__907a8bf2._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__92089220._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__9b7bc2d0._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__b2640944._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__c2267cf1._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d7f7e6dd._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d8e61048._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__f6d0d488._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__fa559e1e._.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_sessions_[id]_restore_route_actions_af7d6b6c.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_7e181e75.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_aca45402.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_b054aff3.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_fa835ac3.js +2 -2
- package/.next/standalone/.next/server/chunks/src_lib_opencodeConfig_ts_8e209941._.js +3 -0
- package/.next/standalone/.next/server/chunks/src_lib_session-providers_claudeCode_ts_0f9590ed._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__631e12d0._.js → [root-of-the-server]__c91a8380._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/src_app_page_tsx_a7111f3e._.js +3 -3
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +2 -2
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/static/chunks/9e790b67c80f853c.js +13 -0
- package/.next/standalone/.next/static/chunks/c3dc8cd80979c971.css +3 -0
- package/.next/standalone/AGENTS.md +4 -0
- package/.next/standalone/README.md +54 -5
- package/.next/standalone/check-hsql.mjs +1 -1
- package/.next/standalone/docs/session-status-detection.md +36 -0
- package/.next/standalone/docs/superpowers/specs/2026-04-09-claude-capability-alignment-design.md +39 -0
- package/.next/standalone/eslint.config.mjs +1 -0
- package/.next/standalone/package-lock.json +74 -13
- package/.next/standalone/package.json +2 -2
- package/.next/standalone/src/app/api/node/events/route.ts +3 -3
- package/.next/standalone/src/app/api/node/sessions/[id]/archive/route.test.ts +60 -1
- package/.next/standalone/src/app/api/node/sessions/[id]/archive/route.ts +77 -22
- package/.next/standalone/src/app/api/node/sessions/[id]/delete/route.ts +6 -5
- package/.next/standalone/src/app/api/node/sessions/[id]/open-editor/route.ts +6 -5
- package/.next/standalone/src/app/api/node/sessions/route.test.ts +282 -0
- package/.next/standalone/src/app/api/node/sessions/route.ts +156 -30
- package/.next/standalone/src/app/api/opencode-config/route.test.ts +613 -0
- package/.next/standalone/src/app/api/opencode-config/route.ts +336 -185
- package/.next/standalone/src/app/api/opencode-events/route.test.ts +77 -1
- package/.next/standalone/src/app/api/opencode-events/route.ts +3 -3
- package/.next/standalone/src/app/api/opencode-models/route.test.ts +19 -0
- package/.next/standalone/src/app/api/opencode-models/route.ts +4 -1
- package/.next/standalone/src/app/api/profiles/[id]/apply/route.test.ts +227 -0
- package/.next/standalone/src/app/api/profiles/[id]/apply/route.ts +13 -9
- package/.next/standalone/src/app/api/sessions/[id]/archive/route.test.ts +126 -0
- package/.next/standalone/src/app/api/sessions/[id]/archive/route.ts +47 -12
- package/.next/standalone/src/app/api/sessions/[id]/delete/route.test.ts +140 -0
- package/.next/standalone/src/app/api/sessions/[id]/delete/route.ts +51 -16
- package/.next/standalone/src/app/api/sessions/[id]/open-editor/route.test.ts +74 -0
- package/.next/standalone/src/app/api/sessions/[id]/open-editor/route.ts +22 -2
- package/.next/standalone/src/app/api/sessions/[id]/restore/route.test.ts +186 -0
- package/.next/standalone/src/app/api/sessions/[id]/restore/route.ts +184 -0
- package/.next/standalone/src/app/api/sessions/[id]/route.ts +3 -3
- package/.next/standalone/src/app/api/sessions/route.test.ts +1955 -100
- package/.next/standalone/src/app/api/sessions/route.ts +361 -986
- package/.next/standalone/src/components/KanbanBoard.test.tsx +307 -1
- package/.next/standalone/src/components/KanbanBoard.tsx +106 -19
- package/.next/standalone/src/components/ProjectCard.test.tsx +420 -6
- package/.next/standalone/src/components/ProjectCard.tsx +238 -86
- package/.next/standalone/src/components/SessionCard.test.tsx +259 -8
- package/.next/standalone/src/components/SessionCard.tsx +182 -76
- package/.next/standalone/src/components/opencode-config/AgentConfigForm.test.tsx +141 -1
- package/.next/standalone/src/components/opencode-config/AgentConfigForm.tsx +99 -7
- package/.next/standalone/src/components/opencode-config/GeneralSettingsForm.test.tsx +11 -0
- package/.next/standalone/src/components/opencode-config/GeneralSettingsForm.tsx +41 -2
- package/.next/standalone/src/components/opencode-config/categories/CategoriesManager.tsx +3 -1
- package/.next/standalone/src/components/opencode-config/categories/CategoryConfigForm.test.tsx +106 -8
- package/.next/standalone/src/components/opencode-config/categories/CategoryConfigForm.tsx +82 -5
- package/.next/standalone/src/hooks/useHostSources.test.ts +0 -41
- package/.next/standalone/src/hooks/useOpencodeSync.test.ts +321 -1
- package/.next/standalone/src/hooks/useOpencodeSync.ts +16 -12
- package/.next/standalone/src/lib/claudeSessionOverrides.test.ts +75 -0
- package/.next/standalone/src/lib/claudeSessionOverrides.ts +169 -0
- package/.next/standalone/src/lib/fixtures/opencode-config/oh-my-openagent.v4.jsonc +70 -0
- package/.next/standalone/src/lib/fixtures/opencode-config/oh-my-openagent.v4.secret-like.jsonc +21 -0
- package/.next/standalone/src/lib/fixtures/opencode-config/oh-my-opencode.v3.jsonc +17 -0
- package/.next/standalone/src/lib/opencodeConfig.test.ts +430 -3
- package/.next/standalone/src/lib/opencodeConfig.ts +157 -4
- package/.next/standalone/src/lib/opencodeDiscovery.test.ts +241 -0
- package/.next/standalone/src/lib/opencodeDiscovery.ts +164 -9
- package/.next/standalone/src/lib/profiles/share.test.ts +92 -0
- package/.next/standalone/src/lib/profiles/share.ts +1 -0
- package/.next/standalone/src/lib/profiles/storage.test.ts +77 -1
- package/.next/standalone/src/lib/profiles/storage.ts +10 -9
- package/.next/standalone/src/lib/session-providers/claudeCode.test.ts +2288 -0
- package/.next/standalone/src/lib/session-providers/claudeCode.ts +1083 -0
- package/.next/standalone/src/lib/session-providers/localAggregator.test.ts +322 -0
- package/.next/standalone/src/lib/session-providers/localAggregator.ts +302 -0
- package/.next/standalone/src/lib/session-providers/opencodeProvider.test.ts +170 -0
- package/.next/standalone/src/lib/session-providers/opencodeProvider.ts +721 -0
- package/.next/standalone/src/lib/session-providers/opencodeSdkCompat.ts +92 -0
- package/.next/standalone/src/lib/session-providers/providerIds.test.ts +337 -0
- package/.next/standalone/src/lib/session-providers/providerIds.ts +176 -0
- package/.next/standalone/src/lib/session-providers/types.ts +131 -0
- package/.next/standalone/src/lib/transform.test.ts +253 -0
- package/.next/standalone/src/lib/transform.ts +96 -37
- package/.next/standalone/src/types/index.ts +23 -17
- package/.next/standalone/src/types/opencodeConfig.ts +55 -0
- package/.next/static/chunks/9e790b67c80f853c.js +13 -0
- package/.next/static/chunks/c3dc8cd80979c971.css +3 -0
- package/.next/trace +1 -1
- package/.next/trace-build +1 -1
- package/.next/types/routes.d.ts +2 -1
- package/.next/types/validator.ts +9 -0
- package/README.md +54 -5
- package/package.json +2 -2
- package/.next/server/chunks/[root-of-the-server]__1211da38._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1211da38._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__192ed2f4._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__2b526e7a._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__2b526e7a._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__2f981540._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__2f981540._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__3745b314._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__3745b314._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__56690af0._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__56690af0._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__56f5f249._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__56f5f249._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__59175de4._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__59175de4._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__64fffc02._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__64fffc02._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__6924c09d._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__6c428a24._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__6c428a24._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__73a00b88._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__73a00b88._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__7e757f50._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__7e757f50._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__89c5eeab._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__89c5eeab._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__8da6c5a8._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__8da6c5a8._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__b796d06c._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__b796d06c._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__c2ce5c0f._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__c2ce5c0f._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__db285678._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__db285678._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__e00a9200._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__e00a9200._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__e5df5e5f._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__e5df5e5f._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__edbc8d9e._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__edbc8d9e._.js.map +0 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1211da38._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__192ed2f4._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__2b526e7a._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__2f981540._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__3745b314._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__56690af0._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__56f5f249._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__59175de4._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__64fffc02._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__6924c09d._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__6c428a24._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__73a00b88._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__7e757f50._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__89c5eeab._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__8da6c5a8._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__b796d06c._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__c2ce5c0f._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__db285678._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__e00a9200._.js +0 -5
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__e5df5e5f._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__edbc8d9e._.js +0 -3
- package/.next/standalone/.next/static/chunks/65d5354ba0add961.js +0 -13
- package/.next/standalone/.next/static/chunks/f42202943f6742e5.css +0 -3
- package/.next/static/chunks/65d5354ba0add961.js +0 -13
- package/.next/static/chunks/f42202943f6742e5.css +0 -3
- /package/.next/standalone/.next/static/{5kq9DtuBFVxu4jsgmL5Q- → 0WaQ6UjiNBgvh531pJVh0}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{5kq9DtuBFVxu4jsgmL5Q- → 0WaQ6UjiNBgvh531pJVh0}/_clientMiddlewareManifest.json +0 -0
- /package/.next/standalone/.next/static/{5kq9DtuBFVxu4jsgmL5Q- → 0WaQ6UjiNBgvh531pJVh0}/_ssgManifest.js +0 -0
- /package/.next/static/{5kq9DtuBFVxu4jsgmL5Q- → 0WaQ6UjiNBgvh531pJVh0}/_buildManifest.js +0 -0
- /package/.next/static/{5kq9DtuBFVxu4jsgmL5Q- → 0WaQ6UjiNBgvh531pJVh0}/_clientMiddlewareManifest.json +0 -0
- /package/.next/static/{5kq9DtuBFVxu4jsgmL5Q- → 0WaQ6UjiNBgvh531pJVh0}/_ssgManifest.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { readFile, writeFile } from 'fs/promises';
|
|
1
|
+
import { copyFile, readFile, rm, writeFile } from 'fs/promises';
|
|
2
2
|
import { existsSync, mkdirSync } from 'fs';
|
|
3
|
-
import { join } from 'path';
|
|
3
|
+
import { dirname, join, parse as parsePath } from 'path';
|
|
4
4
|
import { homedir } from 'os';
|
|
5
5
|
import { parse, stringify } from 'comment-json';
|
|
6
6
|
import type { OhMyOpenAgentConfig, OpenEditorTargetMode, VibePulseConfig } from '@/types/opencodeConfig';
|
|
@@ -8,11 +8,44 @@ import type { OhMyOpenAgentConfig, OpenEditorTargetMode, VibePulseConfig } from
|
|
|
8
8
|
export const CONFIG_DIR = join(homedir(), '.config', 'opencode');
|
|
9
9
|
export const CONFIG_PATH = join(CONFIG_DIR, 'oh-my-openagent.jsonc');
|
|
10
10
|
export const LEGACY_CONFIG_PATH = join(CONFIG_DIR, 'oh-my-opencode.jsonc');
|
|
11
|
-
export const
|
|
11
|
+
export const PROJECT_CONFIG_DIR = '.opencode';
|
|
12
|
+
export const PROJECT_CONFIG_JSONC = 'oh-my-openagent.jsonc';
|
|
13
|
+
export const PROJECT_CONFIG_JSON = 'oh-my-openagent.json';
|
|
14
|
+
export const LEGACY_PROJECT_CONFIG_JSONC = 'oh-my-opencode.jsonc';
|
|
15
|
+
export const LEGACY_PROJECT_CONFIG_JSON = 'oh-my-opencode.json';
|
|
16
|
+
export const OH_MY_OPENAGENT_CONFIG_SCHEMA = 'https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/master/assets/oh-my-openagent.schema.json';
|
|
12
17
|
export const DEFAULT_OPEN_EDITOR_TARGET_MODE: OpenEditorTargetMode = 'remote';
|
|
18
|
+
const PROJECT_CONFIG_FILENAMES = [
|
|
19
|
+
PROJECT_CONFIG_JSONC,
|
|
20
|
+
PROJECT_CONFIG_JSON,
|
|
21
|
+
LEGACY_PROJECT_CONFIG_JSONC,
|
|
22
|
+
LEGACY_PROJECT_CONFIG_JSON,
|
|
23
|
+
];
|
|
13
24
|
|
|
14
25
|
export type OpenCodeConfig = OhMyOpenAgentConfig;
|
|
15
26
|
|
|
27
|
+
export interface EffectiveConfigOptions {
|
|
28
|
+
userConfigPath?: string;
|
|
29
|
+
projectStartDir?: string;
|
|
30
|
+
projectConfigPath?: string | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface LegacyMigrationOptions {
|
|
34
|
+
legacyPath?: string;
|
|
35
|
+
canonicalPath?: string;
|
|
36
|
+
backupPath?: string;
|
|
37
|
+
now?: () => Date;
|
|
38
|
+
writeCanonicalConfig?: (config: OpenCodeConfig, canonicalPath: string) => Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface LegacyMigrationResult {
|
|
42
|
+
migrated: boolean;
|
|
43
|
+
legacyPath: string;
|
|
44
|
+
canonicalPath: string;
|
|
45
|
+
backupPath?: string;
|
|
46
|
+
reason?: 'legacy-missing' | 'canonical-exists';
|
|
47
|
+
}
|
|
48
|
+
|
|
16
49
|
export function normalizeOpenEditorTargetMode(value: unknown): OpenEditorTargetMode {
|
|
17
50
|
return value === 'hub' ? 'hub' : DEFAULT_OPEN_EDITOR_TARGET_MODE;
|
|
18
51
|
}
|
|
@@ -28,6 +61,22 @@ export function normalizeVibePulseConfig(value: unknown): VibePulseConfig {
|
|
|
28
61
|
};
|
|
29
62
|
}
|
|
30
63
|
|
|
64
|
+
export function getCanonicalUserConfigPath(): string {
|
|
65
|
+
return CONFIG_PATH;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function getLegacyUserConfigPath(): string {
|
|
69
|
+
return LEGACY_CONFIG_PATH;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function detectLegacyConfig(legacyPath: string = LEGACY_CONFIG_PATH): boolean {
|
|
73
|
+
try {
|
|
74
|
+
return existsSync(legacyPath);
|
|
75
|
+
} catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
31
80
|
export function resolveConfigPath(configPath: string = CONFIG_PATH): string {
|
|
32
81
|
if (configPath !== CONFIG_PATH) {
|
|
33
82
|
return configPath;
|
|
@@ -52,6 +101,47 @@ export function detectConfig(configPath: string = CONFIG_PATH): boolean {
|
|
|
52
101
|
}
|
|
53
102
|
}
|
|
54
103
|
|
|
104
|
+
export function findProjectConfigPath(startDir: string = process.cwd()): string | null {
|
|
105
|
+
let currentDir = startDir;
|
|
106
|
+
|
|
107
|
+
while (true) {
|
|
108
|
+
for (const filename of PROJECT_CONFIG_FILENAMES) {
|
|
109
|
+
const configPath = join(currentDir, PROJECT_CONFIG_DIR, filename);
|
|
110
|
+
if (existsSync(configPath)) {
|
|
111
|
+
return configPath;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const parentDir = dirname(currentDir);
|
|
116
|
+
if (parentDir === currentDir) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
currentDir = parentDir;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
125
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function mergeConfig(base: OpenCodeConfig, overlay: OpenCodeConfig): OpenCodeConfig {
|
|
129
|
+
const merged: Record<string, unknown> = { ...base };
|
|
130
|
+
|
|
131
|
+
for (const [key, overlayValue] of Object.entries(overlay)) {
|
|
132
|
+
const baseValue = merged[key];
|
|
133
|
+
|
|
134
|
+
if (isPlainObject(baseValue) && isPlainObject(overlayValue)) {
|
|
135
|
+
merged[key] = mergeConfig(baseValue as OpenCodeConfig, overlayValue as OpenCodeConfig);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
merged[key] = overlayValue;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return merged as OpenCodeConfig;
|
|
143
|
+
}
|
|
144
|
+
|
|
55
145
|
export async function readConfig(configPath: string = CONFIG_PATH): Promise<OpenCodeConfig> {
|
|
56
146
|
try {
|
|
57
147
|
const content = await readFile(resolveConfigPath(configPath), 'utf-8');
|
|
@@ -62,12 +152,35 @@ export async function readConfig(configPath: string = CONFIG_PATH): Promise<Open
|
|
|
62
152
|
}
|
|
63
153
|
}
|
|
64
154
|
|
|
155
|
+
async function readLegacyConfigForMigration(legacyPath: string): Promise<OpenCodeConfig> {
|
|
156
|
+
try {
|
|
157
|
+
const content = await readFile(legacyPath, 'utf-8');
|
|
158
|
+
return parse(content, null, false) as OpenCodeConfig;
|
|
159
|
+
} catch (error) {
|
|
160
|
+
throw new Error(`Failed to read legacy config: ${error}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export async function readEffectiveConfig(options: EffectiveConfigOptions = {}): Promise<OpenCodeConfig> {
|
|
165
|
+
const userConfig = await readConfig(options.userConfigPath);
|
|
166
|
+
const projectConfigPath = options.projectConfigPath === undefined
|
|
167
|
+
? findProjectConfigPath(options.projectStartDir)
|
|
168
|
+
: options.projectConfigPath;
|
|
169
|
+
|
|
170
|
+
if (!projectConfigPath) {
|
|
171
|
+
return userConfig;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const projectConfig = await readConfig(projectConfigPath);
|
|
175
|
+
return mergeConfig(userConfig, projectConfig);
|
|
176
|
+
}
|
|
177
|
+
|
|
65
178
|
export async function writeConfig(
|
|
66
179
|
config: OpenCodeConfig,
|
|
67
180
|
configPath: string = CONFIG_PATH
|
|
68
181
|
): Promise<void> {
|
|
69
182
|
try {
|
|
70
|
-
const configDir =
|
|
183
|
+
const configDir = dirname(configPath);
|
|
71
184
|
if (!existsSync(configDir)) {
|
|
72
185
|
mkdirSync(configDir, { recursive: true });
|
|
73
186
|
}
|
|
@@ -86,3 +199,43 @@ export async function writeConfig(
|
|
|
86
199
|
throw new Error(`Failed to write config: ${error}`);
|
|
87
200
|
}
|
|
88
201
|
}
|
|
202
|
+
|
|
203
|
+
function formatBackupTimestamp(date: Date): string {
|
|
204
|
+
return date.toISOString().replace(/[:.]/g, '-');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function defaultBackupPath(legacyPath: string, date: Date): string {
|
|
208
|
+
const parsed = parsePath(legacyPath);
|
|
209
|
+
return join(parsed.dir, `${parsed.base}.backup-${formatBackupTimestamp(date)}`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export async function migrateLegacyConfig(options: LegacyMigrationOptions = {}): Promise<LegacyMigrationResult> {
|
|
213
|
+
const legacyPath = options.legacyPath ?? LEGACY_CONFIG_PATH;
|
|
214
|
+
const canonicalPath = options.canonicalPath ?? CONFIG_PATH;
|
|
215
|
+
|
|
216
|
+
if (!existsSync(legacyPath)) {
|
|
217
|
+
return { migrated: false, legacyPath, canonicalPath, reason: 'legacy-missing' };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (existsSync(canonicalPath)) {
|
|
221
|
+
return { migrated: false, legacyPath, canonicalPath, reason: 'canonical-exists' };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const backupPath = options.backupPath ?? defaultBackupPath(legacyPath, options.now?.() ?? new Date());
|
|
225
|
+
const canonicalDir = dirname(canonicalPath);
|
|
226
|
+
if (!existsSync(canonicalDir)) {
|
|
227
|
+
mkdirSync(canonicalDir, { recursive: true });
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
await copyFile(legacyPath, backupPath);
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
const legacyConfig = await readLegacyConfigForMigration(legacyPath);
|
|
234
|
+
const writeCanonicalConfig = options.writeCanonicalConfig ?? writeConfig;
|
|
235
|
+
await writeCanonicalConfig(legacyConfig, canonicalPath);
|
|
236
|
+
return { migrated: true, legacyPath, canonicalPath, backupPath };
|
|
237
|
+
} catch (error) {
|
|
238
|
+
await rm(canonicalPath, { force: true });
|
|
239
|
+
throw error;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
type ExecSyncMock = ReturnType<typeof vi.fn>;
|
|
4
|
+
type DiscoveryModule = typeof import('./opencodeDiscovery');
|
|
5
|
+
|
|
6
|
+
let execSyncMock: ExecSyncMock;
|
|
7
|
+
const originalDiscoveryTimeout = process.env.OPENCODE_DISCOVERY_TIMEOUT_MS;
|
|
8
|
+
|
|
9
|
+
async function loadDiscovery(): Promise<DiscoveryModule> {
|
|
10
|
+
vi.resetModules();
|
|
11
|
+
execSyncMock = vi.fn();
|
|
12
|
+
vi.doMock('child_process', () => ({
|
|
13
|
+
execSync: execSyncMock,
|
|
14
|
+
default: {
|
|
15
|
+
execSync: execSyncMock,
|
|
16
|
+
},
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
return import('./opencodeDiscovery');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function commandText(command: unknown): string {
|
|
23
|
+
return typeof command === 'string' ? command : '';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function setupCommandOutputs({
|
|
27
|
+
lsof = '',
|
|
28
|
+
psCommand = '',
|
|
29
|
+
probePorts = {},
|
|
30
|
+
timeoutCommands = [],
|
|
31
|
+
}: {
|
|
32
|
+
lsof?: string;
|
|
33
|
+
psCommand?: string;
|
|
34
|
+
probePorts?: Record<number, { health?: boolean; doc?: boolean; healthBody?: string; docBody?: string }>;
|
|
35
|
+
timeoutCommands?: string[];
|
|
36
|
+
}): void {
|
|
37
|
+
execSyncMock.mockImplementation((command: unknown) => {
|
|
38
|
+
const text = commandText(command);
|
|
39
|
+
|
|
40
|
+
if (timeoutCommands.some((pattern) => text.includes(pattern))) {
|
|
41
|
+
const error = new Error('Command timed out') as Error & { code: string };
|
|
42
|
+
error.code = 'ETIMEDOUT';
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (text.includes('lsof -nP -iTCP -sTCP:LISTEN')) {
|
|
47
|
+
return lsof;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (text.includes('ps -axo command')) {
|
|
51
|
+
return psCommand;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (text.includes('/global/health') || text.includes('/doc')) {
|
|
55
|
+
const match = text.match(/127\.0\.0\.1:(\d+)\//);
|
|
56
|
+
const port = match ? Number(match[1]) : NaN;
|
|
57
|
+
const probe = probePorts[port];
|
|
58
|
+
const isHealth = text.includes('/global/health');
|
|
59
|
+
const ok = isHealth ? probe?.health : probe?.doc;
|
|
60
|
+
if (ok) {
|
|
61
|
+
return isHealth
|
|
62
|
+
? (probe?.healthBody ?? '{"health":"ok","version":"1.14.48"}')
|
|
63
|
+
: (probe?.docBody ?? '{"openapi":"3.0.0","info":{"title":"OpenCode API","description":"OpenCode docs"}}');
|
|
64
|
+
}
|
|
65
|
+
throw new Error(`probe failed for ${port}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return '';
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function probeUrls(): string[] {
|
|
73
|
+
return execSyncMock.mock.calls
|
|
74
|
+
.map((call) => commandText(call[0]))
|
|
75
|
+
.filter((command) => command.includes('/global/health') || command.includes('/doc'));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
describe('opencodeDiscovery', () => {
|
|
79
|
+
beforeEach(() => {
|
|
80
|
+
process.env.OPENCODE_DISCOVERY_TIMEOUT_MS = '5000';
|
|
81
|
+
vi.unstubAllGlobals();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
afterEach(() => {
|
|
85
|
+
vi.doUnmock('child_process');
|
|
86
|
+
vi.unstubAllGlobals();
|
|
87
|
+
if (originalDiscoveryTimeout === undefined) {
|
|
88
|
+
delete process.env.OPENCODE_DISCOVERY_TIMEOUT_MS;
|
|
89
|
+
} else {
|
|
90
|
+
process.env.OPENCODE_DISCOVERY_TIMEOUT_MS = originalDiscoveryTimeout;
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('includes explicit --port values from both space and equals process args', async () => {
|
|
95
|
+
const discovery = await loadDiscovery();
|
|
96
|
+
setupCommandOutputs({
|
|
97
|
+
psCommand: '/usr/local/bin/opencode serve --port 3456\nnode /bin/opencode --port=4567\n',
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const result = discovery.discoverOpencodePortsWithMeta();
|
|
101
|
+
|
|
102
|
+
expect(result).toEqual({ ports: [3456, 4567], timedOut: false });
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('includes lsof OpenCode listening ports with explicit ports', async () => {
|
|
106
|
+
const discovery = await loadDiscovery();
|
|
107
|
+
setupCommandOutputs({
|
|
108
|
+
lsof: 'COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME\nopencode 123 user 10u IPv4 0x1 0t0 TCP 127.0.0.1:7777 (LISTEN)\nnode 555 user 12u IPv4 0x2 0t0 TCP 127.0.0.1:9999 (LISTEN)\n',
|
|
109
|
+
psCommand: 'opencode --port=3456\n',
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const result = discovery.discoverOpencodePortsWithMeta();
|
|
113
|
+
|
|
114
|
+
expect(result).toEqual({ ports: [3456, 7777], timedOut: false });
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('probes default 4096 when OpenCode is started with --port 0', async () => {
|
|
118
|
+
const discovery = await loadDiscovery();
|
|
119
|
+
setupCommandOutputs({
|
|
120
|
+
psCommand: 'opencode serve --port 0\n',
|
|
121
|
+
probePorts: { 4096: { health: true, healthBody: '{"health":"ok","version":"1.14.48"}' } },
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const result = discovery.discoverOpencodePortsWithMeta();
|
|
125
|
+
|
|
126
|
+
expect(result).toEqual({ ports: [4096], timedOut: false });
|
|
127
|
+
expect(probeUrls()).toEqual(
|
|
128
|
+
expect.arrayContaining([expect.stringContaining('http://127.0.0.1:4096/global/health')])
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('accepts documented healthy health response for the default candidate', async () => {
|
|
133
|
+
const discovery = await loadDiscovery();
|
|
134
|
+
setupCommandOutputs({
|
|
135
|
+
psCommand: 'opencode serve\n',
|
|
136
|
+
probePorts: { 4096: { health: true, healthBody: '{"healthy":true,"version":"1.14.48"}' } },
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const result = discovery.discoverOpencodePortsWithMeta();
|
|
140
|
+
|
|
141
|
+
expect(result).toEqual({ ports: [4096], timedOut: false });
|
|
142
|
+
expect(probeUrls()).toEqual([
|
|
143
|
+
expect.stringContaining('http://127.0.0.1:4096/global/health'),
|
|
144
|
+
]);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('rejects unhealthy or versionless health responses unless /doc is valid', async () => {
|
|
148
|
+
const discovery = await loadDiscovery();
|
|
149
|
+
setupCommandOutputs({
|
|
150
|
+
psCommand: 'opencode serve\n',
|
|
151
|
+
probePorts: {
|
|
152
|
+
4096: { health: true, healthBody: '{"healthy":false,"version":"1.14.48"}', doc: false },
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
expect(discovery.discoverOpencodePortsWithMeta()).toEqual({ ports: [], timedOut: false });
|
|
157
|
+
expect(probeUrls()).toEqual([
|
|
158
|
+
expect.stringContaining('http://127.0.0.1:4096/global/health'),
|
|
159
|
+
expect.stringContaining('http://127.0.0.1:4096/doc'),
|
|
160
|
+
]);
|
|
161
|
+
|
|
162
|
+
setupCommandOutputs({
|
|
163
|
+
psCommand: 'opencode serve\n',
|
|
164
|
+
probePorts: {
|
|
165
|
+
4096: { health: true, healthBody: '{"healthy":true}', doc: false },
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
expect(discovery.discoverOpencodePortsWithMeta()).toEqual({ ports: [], timedOut: false });
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('accepts default candidate when /doc succeeds after health fails', async () => {
|
|
173
|
+
const discovery = await loadDiscovery();
|
|
174
|
+
setupCommandOutputs({
|
|
175
|
+
psCommand: 'opencode serve\n',
|
|
176
|
+
probePorts: {
|
|
177
|
+
4096: {
|
|
178
|
+
health: false,
|
|
179
|
+
doc: true,
|
|
180
|
+
docBody: '{"openapi":"3.0.0","info":{"title":"OpenCode API","description":"OpenCode docs"}}',
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const result = discovery.discoverOpencodePortsWithMeta();
|
|
186
|
+
|
|
187
|
+
expect(result).toEqual({ ports: [4096], timedOut: false });
|
|
188
|
+
expect(probeUrls()).toEqual([
|
|
189
|
+
expect.stringContaining('http://127.0.0.1:4096/global/health'),
|
|
190
|
+
expect.stringContaining('http://127.0.0.1:4096/doc'),
|
|
191
|
+
]);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('rejects generic /doc swagger content without an OpenCode signature', async () => {
|
|
195
|
+
const discovery = await loadDiscovery();
|
|
196
|
+
setupCommandOutputs({
|
|
197
|
+
psCommand: 'opencode serve\n',
|
|
198
|
+
probePorts: {
|
|
199
|
+
4096: {
|
|
200
|
+
health: false,
|
|
201
|
+
doc: true,
|
|
202
|
+
docBody: '{"openapi":"3.0.0","info":{"title":"Generic API","description":"Generic docs"}}',
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const result = discovery.discoverOpencodePortsWithMeta();
|
|
208
|
+
|
|
209
|
+
expect(result).toEqual({ ports: [], timedOut: false });
|
|
210
|
+
expect(probeUrls()).toEqual([
|
|
211
|
+
expect.stringContaining('http://127.0.0.1:4096/global/health'),
|
|
212
|
+
expect.stringContaining('http://127.0.0.1:4096/doc'),
|
|
213
|
+
]);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('retains valid known ports and prunes stale known ports after probe failure', async () => {
|
|
217
|
+
const discovery = await loadDiscovery();
|
|
218
|
+
setupCommandOutputs({ psCommand: 'opencode --port=6123\n' });
|
|
219
|
+
|
|
220
|
+
expect(discovery.discoverOpencodePortsWithMeta().ports).toEqual([6123]);
|
|
221
|
+
|
|
222
|
+
setupCommandOutputs({ probePorts: { 6123: { health: true } } });
|
|
223
|
+
expect(discovery.discoverOpencodePortsWithMeta().ports).toEqual([6123]);
|
|
224
|
+
|
|
225
|
+
setupCommandOutputs({ probePorts: { 6123: { health: false, doc: false } } });
|
|
226
|
+
expect(discovery.discoverOpencodePortsWithMeta().ports).toEqual([]);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('keeps discovery bounded and reports command timeout', async () => {
|
|
230
|
+
const discovery = await loadDiscovery();
|
|
231
|
+
setupCommandOutputs({
|
|
232
|
+
timeoutCommands: ['lsof -nP -iTCP -sTCP:LISTEN'],
|
|
233
|
+
psCommand: 'opencode --port=4567\n',
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
const result = discovery.discoverOpencodePortsWithMeta();
|
|
237
|
+
|
|
238
|
+
expect(result).toEqual({ ports: [4567], timedOut: true });
|
|
239
|
+
expect(probeUrls()).toEqual([]);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { execSync } from 'child_process';
|
|
2
2
|
|
|
3
3
|
const DEFAULT_DISCOVERY_COMMAND_TIMEOUT_MS = 5000;
|
|
4
|
+
const DEFAULT_OPENCODE_PORT = 4096;
|
|
5
|
+
const OPENCODE_PROBE_ENDPOINTS = ['/global/health', '/doc'] as const;
|
|
4
6
|
const knownPorts = new Set<number>();
|
|
5
7
|
|
|
6
8
|
export type OpencodeProcessCwd = {
|
|
@@ -24,6 +26,58 @@ type DiscoveryState = {
|
|
|
24
26
|
deadlineMs: number;
|
|
25
27
|
};
|
|
26
28
|
|
|
29
|
+
type ProcessPortDiscovery = {
|
|
30
|
+
ports: number[];
|
|
31
|
+
shouldProbeDefaultPort: boolean;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
type ProbeResult = 'ok' | 'failed' | 'timedOut';
|
|
35
|
+
|
|
36
|
+
function parseJsonResponse(body: string): Record<string, unknown> | null {
|
|
37
|
+
try {
|
|
38
|
+
const parsed = JSON.parse(body) as unknown;
|
|
39
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return parsed as Record<string, unknown>;
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isOpenCodeHealthResponse(body: string): boolean {
|
|
50
|
+
const parsed = parseJsonResponse(body);
|
|
51
|
+
if (!parsed) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const health = parsed.health;
|
|
56
|
+
const healthy = parsed.healthy;
|
|
57
|
+
const version = parsed.version;
|
|
58
|
+
return (
|
|
59
|
+
(health === 'ok' || healthy === true) &&
|
|
60
|
+
typeof version === 'string' &&
|
|
61
|
+
/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(version)
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function isOpenCodeDocsResponse(body: string): boolean {
|
|
66
|
+
const parsed = parseJsonResponse(body);
|
|
67
|
+
if (!parsed || typeof parsed.openapi !== 'string' || !/^3(?:\.\d+)?/.test(parsed.openapi)) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const info = parsed.info;
|
|
72
|
+
const infoRecord = info && typeof info === 'object' && !Array.isArray(info) ? info as Record<string, unknown> : null;
|
|
73
|
+
const title = typeof infoRecord?.title === 'string' ? infoRecord.title : '';
|
|
74
|
+
const description = typeof infoRecord?.description === 'string' ? infoRecord.description : '';
|
|
75
|
+
const rootTitle = typeof parsed.title === 'string' ? parsed.title : '';
|
|
76
|
+
const rootDescription = typeof parsed.description === 'string' ? parsed.description : '';
|
|
77
|
+
|
|
78
|
+
return [title, description, rootTitle, rootDescription].some((value) => /opencode/i.test(value));
|
|
79
|
+
}
|
|
80
|
+
|
|
27
81
|
function getDiscoveryCommandTimeoutMs(): number {
|
|
28
82
|
const parsedTimeout = Number(process.env.OPENCODE_DISCOVERY_TIMEOUT_MS);
|
|
29
83
|
return Number.isFinite(parsedTimeout) && parsedTimeout > 0
|
|
@@ -110,11 +164,31 @@ function getPortsFromLsof(state: DiscoveryState): number[] {
|
|
|
110
164
|
}
|
|
111
165
|
}
|
|
112
166
|
|
|
113
|
-
function
|
|
167
|
+
function isOpencodeCommand(command: string): boolean {
|
|
168
|
+
return /\bopencode\b/.test(command);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function extractPortFlags(command: string): number[] {
|
|
172
|
+
const ports: number[] = [];
|
|
173
|
+
const matches = command.matchAll(/--port(?:=(\d+)|\s+(\d+))\b/g);
|
|
174
|
+
|
|
175
|
+
for (const match of matches) {
|
|
176
|
+
const parsedPort = parseInt(match[1] ?? match[2] ?? '', 10);
|
|
177
|
+
if (Number.isFinite(parsedPort)) {
|
|
178
|
+
ports.push(parsedPort);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return ports;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function getPortsFromProcessArgs(state: DiscoveryState): ProcessPortDiscovery {
|
|
186
|
+
const result: ProcessPortDiscovery = { ports: [], shouldProbeDefaultPort: false };
|
|
187
|
+
|
|
114
188
|
try {
|
|
115
189
|
const timeoutMs = getRemainingTimeoutMs(state);
|
|
116
190
|
if (timeoutMs === null) {
|
|
117
|
-
return
|
|
191
|
+
return result;
|
|
118
192
|
}
|
|
119
193
|
|
|
120
194
|
const output = execSync('ps -axo command', {
|
|
@@ -122,16 +196,73 @@ function getPortsFromProcessArgs(state: DiscoveryState): number[] {
|
|
|
122
196
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
123
197
|
timeout: timeoutMs,
|
|
124
198
|
});
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
199
|
+
|
|
200
|
+
for (const line of output.split('\n')) {
|
|
201
|
+
const command = line.trim();
|
|
202
|
+
if (!command || !isOpencodeCommand(command)) {
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const ports = extractPortFlags(command);
|
|
207
|
+
if (ports.length === 0) {
|
|
208
|
+
result.shouldProbeDefaultPort = true;
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
for (const port of ports) {
|
|
213
|
+
if (port === 0) {
|
|
214
|
+
result.shouldProbeDefaultPort = true;
|
|
215
|
+
} else {
|
|
216
|
+
result.ports.push(port);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return result;
|
|
129
222
|
} catch (error) {
|
|
130
223
|
if (isCommandTimeoutError(error)) {
|
|
131
224
|
state.timedOut = true;
|
|
132
225
|
}
|
|
133
|
-
return
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function probeOpencodePort(port: number, state: DiscoveryState): ProbeResult {
|
|
231
|
+
if (!Number.isInteger(port) || port <= 0 || port > 65535) {
|
|
232
|
+
return 'failed';
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
for (const endpoint of OPENCODE_PROBE_ENDPOINTS) {
|
|
236
|
+
const timeoutMs = getRemainingTimeoutMs(state);
|
|
237
|
+
if (timeoutMs === null) {
|
|
238
|
+
return 'timedOut';
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
const timeoutSeconds = Math.max(0.001, timeoutMs / 1000).toFixed(3);
|
|
243
|
+
const response = execSync(`curl -fsS --max-time ${timeoutSeconds} http://127.0.0.1:${port}${endpoint}`, {
|
|
244
|
+
encoding: 'utf-8',
|
|
245
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
246
|
+
timeout: timeoutMs,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const body = typeof response === 'string' ? response : String(response);
|
|
250
|
+
const isValidProbe = endpoint === '/global/health'
|
|
251
|
+
? isOpenCodeHealthResponse(body)
|
|
252
|
+
: isOpenCodeDocsResponse(body);
|
|
253
|
+
|
|
254
|
+
if (isValidProbe) {
|
|
255
|
+
return 'ok';
|
|
256
|
+
}
|
|
257
|
+
} catch (error) {
|
|
258
|
+
if (isCommandTimeoutError(error)) {
|
|
259
|
+
state.timedOut = true;
|
|
260
|
+
return 'timedOut';
|
|
261
|
+
}
|
|
262
|
+
}
|
|
134
263
|
}
|
|
264
|
+
|
|
265
|
+
return 'failed';
|
|
135
266
|
}
|
|
136
267
|
|
|
137
268
|
export function discoverOpencodePorts(): number[] {
|
|
@@ -146,17 +277,41 @@ export function discoverOpencodePortsWithMeta(): OpencodePortDiscoveryResult {
|
|
|
146
277
|
deadlineMs: Date.now() + timeoutMs,
|
|
147
278
|
};
|
|
148
279
|
|
|
280
|
+
const processDiscovery = getPortsFromProcessArgs(state);
|
|
149
281
|
const discoveredPorts = toUniqueSortedPorts([
|
|
150
282
|
...getPortsFromLsof(state),
|
|
151
|
-
...
|
|
283
|
+
...processDiscovery.ports,
|
|
152
284
|
]);
|
|
153
285
|
|
|
154
286
|
for (const port of discoveredPorts) {
|
|
155
287
|
knownPorts.add(port);
|
|
156
288
|
}
|
|
157
289
|
|
|
290
|
+
const probeCandidates = new Set<number>();
|
|
291
|
+
if (processDiscovery.shouldProbeDefaultPort && !discoveredPorts.includes(DEFAULT_OPENCODE_PORT)) {
|
|
292
|
+
probeCandidates.add(DEFAULT_OPENCODE_PORT);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
for (const port of knownPorts) {
|
|
296
|
+
if (!discoveredPorts.includes(port)) {
|
|
297
|
+
probeCandidates.add(port);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const probedPorts: number[] = [];
|
|
302
|
+
for (const port of probeCandidates) {
|
|
303
|
+
const probeResult = probeOpencodePort(port, state);
|
|
304
|
+
if (probeResult === 'ok') {
|
|
305
|
+
knownPorts.add(port);
|
|
306
|
+
probedPorts.push(port);
|
|
307
|
+
} else if (probeResult === 'failed' && knownPorts.has(port)) {
|
|
308
|
+
knownPorts.delete(port);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
158
312
|
const ports = toUniqueSortedPorts([
|
|
159
313
|
...discoveredPorts,
|
|
314
|
+
...probedPorts,
|
|
160
315
|
...Array.from(knownPorts),
|
|
161
316
|
]);
|
|
162
317
|
|
|
@@ -194,7 +349,7 @@ function getOpencodePidsWithoutPortFlag(state: DiscoveryState): number[] {
|
|
|
194
349
|
|
|
195
350
|
if (!Number.isFinite(pid)) continue;
|
|
196
351
|
if (!/\bopencode\b/.test(command)) continue;
|
|
197
|
-
if (
|
|
352
|
+
if (extractPortFlags(command).length > 0) continue;
|
|
198
353
|
|
|
199
354
|
pids.push(pid);
|
|
200
355
|
}
|