sales-frontend-ju-hong-e 0.0.2
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/dist/be.cjs +4446 -0
- package/dist/be.cjs.map +1 -0
- package/dist/be.js +4432 -0
- package/dist/be.js.map +1 -0
- package/dist/cli.cjs +4446 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.js +4432 -0
- package/dist/cli.js.map +1 -0
- package/dist/command-modules/ju-hong-e/change-service/index.cjs +2311 -0
- package/dist/command-modules/ju-hong-e/change-service/index.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/change-service/index.js +2301 -0
- package/dist/command-modules/ju-hong-e/change-service/index.js.map +1 -0
- package/dist/command-modules/ju-hong-e/commit/action.cjs +2304 -0
- package/dist/command-modules/ju-hong-e/commit/action.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/commit/action.js +2294 -0
- package/dist/command-modules/ju-hong-e/commit/action.js.map +1 -0
- package/dist/command-modules/ju-hong-e/commit/index.cjs +2527 -0
- package/dist/command-modules/ju-hong-e/commit/index.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/commit/index.js +2515 -0
- package/dist/command-modules/ju-hong-e/commit/index.js.map +1 -0
- package/dist/command-modules/ju-hong-e/exit/index.cjs +2070 -0
- package/dist/command-modules/ju-hong-e/exit/index.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/exit/index.js +2058 -0
- package/dist/command-modules/ju-hong-e/exit/index.js.map +1 -0
- package/dist/command-modules/ju-hong-e/gen-html/action.cjs +4260 -0
- package/dist/command-modules/ju-hong-e/gen-html/action.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/gen-html/action.js +4248 -0
- package/dist/command-modules/ju-hong-e/gen-html/action.js.map +1 -0
- package/dist/command-modules/ju-hong-e/gen-html/index.cjs +4435 -0
- package/dist/command-modules/ju-hong-e/gen-html/index.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/gen-html/index.js +4421 -0
- package/dist/command-modules/ju-hong-e/gen-html/index.js.map +1 -0
- package/dist/command-modules/ju-hong-e/gen-html/runtime.cjs +335 -0
- package/dist/command-modules/ju-hong-e/gen-html/runtime.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/gen-html/runtime.js +324 -0
- package/dist/command-modules/ju-hong-e/gen-html/runtime.js.map +1 -0
- package/dist/command-modules/ju-hong-e/gen-storybook/action.cjs +2542 -0
- package/dist/command-modules/ju-hong-e/gen-storybook/action.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/gen-storybook/action.js +2532 -0
- package/dist/command-modules/ju-hong-e/gen-storybook/action.js.map +1 -0
- package/dist/command-modules/ju-hong-e/gen-storybook/index.cjs +2724 -0
- package/dist/command-modules/ju-hong-e/gen-storybook/index.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/gen-storybook/index.js +2712 -0
- package/dist/command-modules/ju-hong-e/gen-storybook/index.js.map +1 -0
- package/dist/command-modules/ju-hong-e/gen-storybook/runtime.cjs +287 -0
- package/dist/command-modules/ju-hong-e/gen-storybook/runtime.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/gen-storybook/runtime.js +278 -0
- package/dist/command-modules/ju-hong-e/gen-storybook/runtime.js.map +1 -0
- package/dist/command-modules/ju-hong-e/help/index.cjs +2107 -0
- package/dist/command-modules/ju-hong-e/help/index.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/help/index.js +2097 -0
- package/dist/command-modules/ju-hong-e/help/index.js.map +1 -0
- package/dist/command-modules/ju-hong-e/local-server/action.cjs +2212 -0
- package/dist/command-modules/ju-hong-e/local-server/action.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/local-server/action.js +2199 -0
- package/dist/command-modules/ju-hong-e/local-server/action.js.map +1 -0
- package/dist/command-modules/ju-hong-e/mcp/action.cjs +1684 -0
- package/dist/command-modules/ju-hong-e/mcp/action.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/mcp/action.js +1672 -0
- package/dist/command-modules/ju-hong-e/mcp/action.js.map +1 -0
- package/dist/command-modules/ju-hong-e/mcp/index.cjs +2194 -0
- package/dist/command-modules/ju-hong-e/mcp/index.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/mcp/index.js +2182 -0
- package/dist/command-modules/ju-hong-e/mcp/index.js.map +1 -0
- package/dist/command-modules/ju-hong-e/mcp/runtime.cjs +554 -0
- package/dist/command-modules/ju-hong-e/mcp/runtime.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/mcp/runtime.js +538 -0
- package/dist/command-modules/ju-hong-e/mcp/runtime.js.map +1 -0
- package/dist/command-modules/ju-hong-e/mcp-health-check/index.cjs +2617 -0
- package/dist/command-modules/ju-hong-e/mcp-health-check/index.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/mcp-health-check/index.js +2605 -0
- package/dist/command-modules/ju-hong-e/mcp-health-check/index.js.map +1 -0
- package/dist/command-modules/ju-hong-e/mcp-register/index.cjs +2591 -0
- package/dist/command-modules/ju-hong-e/mcp-register/index.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/mcp-register/index.js +2579 -0
- package/dist/command-modules/ju-hong-e/mcp-register/index.js.map +1 -0
- package/dist/command-modules/ju-hong-e/mcp-unregister/index.cjs +2649 -0
- package/dist/command-modules/ju-hong-e/mcp-unregister/index.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/mcp-unregister/index.js +2637 -0
- package/dist/command-modules/ju-hong-e/mcp-unregister/index.js.map +1 -0
- package/dist/command-modules/ju-hong-e/restart-local-server/index.cjs +3490 -0
- package/dist/command-modules/ju-hong-e/restart-local-server/index.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/restart-local-server/index.js +3476 -0
- package/dist/command-modules/ju-hong-e/restart-local-server/index.js.map +1 -0
- package/dist/command-modules/ju-hong-e/review/action.cjs +2360 -0
- package/dist/command-modules/ju-hong-e/review/action.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/review/action.js +2350 -0
- package/dist/command-modules/ju-hong-e/review/action.js.map +1 -0
- package/dist/command-modules/ju-hong-e/review/index.cjs +2548 -0
- package/dist/command-modules/ju-hong-e/review/index.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/review/index.js +2536 -0
- package/dist/command-modules/ju-hong-e/review/index.js.map +1 -0
- package/dist/command-modules/ju-hong-e/security-policy/action.cjs +871 -0
- package/dist/command-modules/ju-hong-e/security-policy/action.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/security-policy/action.js +861 -0
- package/dist/command-modules/ju-hong-e/security-policy/action.js.map +1 -0
- package/dist/command-modules/ju-hong-e/security-policy/index.cjs +2341 -0
- package/dist/command-modules/ju-hong-e/security-policy/index.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/security-policy/index.js +2329 -0
- package/dist/command-modules/ju-hong-e/security-policy/index.js.map +1 -0
- package/dist/command-modules/ju-hong-e/setting-local-server/index.cjs +3135 -0
- package/dist/command-modules/ju-hong-e/setting-local-server/index.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/setting-local-server/index.js +3120 -0
- package/dist/command-modules/ju-hong-e/setting-local-server/index.js.map +1 -0
- package/dist/command-modules/ju-hong-e/show-stream/action.cjs +679 -0
- package/dist/command-modules/ju-hong-e/show-stream/action.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/show-stream/action.js +669 -0
- package/dist/command-modules/ju-hong-e/show-stream/action.js.map +1 -0
- package/dist/command-modules/ju-hong-e/show-stream/index.cjs +2190 -0
- package/dist/command-modules/ju-hong-e/show-stream/index.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/show-stream/index.js +2178 -0
- package/dist/command-modules/ju-hong-e/show-stream/index.js.map +1 -0
- package/dist/command-modules/ju-hong-e/toggle-storybook-mode/index.cjs +3514 -0
- package/dist/command-modules/ju-hong-e/toggle-storybook-mode/index.cjs.map +1 -0
- package/dist/command-modules/ju-hong-e/toggle-storybook-mode/index.js +3500 -0
- package/dist/command-modules/ju-hong-e/toggle-storybook-mode/index.js.map +1 -0
- package/dist/command-modules/native/claude/catalog/index.cjs +2111 -0
- package/dist/command-modules/native/claude/catalog/index.cjs.map +1 -0
- package/dist/command-modules/native/claude/catalog/index.js +2099 -0
- package/dist/command-modules/native/claude/catalog/index.js.map +1 -0
- package/dist/command-modules/native/codex/catalog/index.cjs +2087 -0
- package/dist/command-modules/native/codex/catalog/index.cjs.map +1 -0
- package/dist/command-modules/native/codex/catalog/index.js +2075 -0
- package/dist/command-modules/native/codex/catalog/index.js.map +1 -0
- package/dist/command-modules/native/gemini/catalog/index.cjs +2107 -0
- package/dist/command-modules/native/gemini/catalog/index.cjs.map +1 -0
- package/dist/command-modules/native/gemini/catalog/index.js +2095 -0
- package/dist/command-modules/native/gemini/catalog/index.js.map +1 -0
- package/dist/common/actions/chat.cjs +2049 -0
- package/dist/common/actions/chat.cjs.map +1 -0
- package/dist/common/actions/chat.js +2039 -0
- package/dist/common/actions/chat.js.map +1 -0
- package/dist/common/actions/native-command.cjs +1994 -0
- package/dist/common/actions/native-command.cjs.map +1 -0
- package/dist/common/actions/native-command.js +1984 -0
- package/dist/common/actions/native-command.js.map +1 -0
- package/dist/common/actions/shared.cjs +1475 -0
- package/dist/common/actions/shared.cjs.map +1 -0
- package/dist/common/actions/shared.js +1462 -0
- package/dist/common/actions/shared.js.map +1 -0
- package/dist/common/ai-services/builders/claude.cjs +185 -0
- package/dist/common/ai-services/builders/claude.cjs.map +1 -0
- package/dist/common/ai-services/builders/claude.js +178 -0
- package/dist/common/ai-services/builders/claude.js.map +1 -0
- package/dist/common/ai-services/builders/codex.cjs +160 -0
- package/dist/common/ai-services/builders/codex.cjs.map +1 -0
- package/dist/common/ai-services/builders/codex.js +153 -0
- package/dist/common/ai-services/builders/codex.js.map +1 -0
- package/dist/common/ai-services/builders/gemini.cjs +183 -0
- package/dist/common/ai-services/builders/gemini.cjs.map +1 -0
- package/dist/common/ai-services/builders/gemini.js +176 -0
- package/dist/common/ai-services/builders/gemini.js.map +1 -0
- package/dist/common/ai-services/index.cjs +1428 -0
- package/dist/common/ai-services/index.cjs.map +1 -0
- package/dist/common/ai-services/index.js +1417 -0
- package/dist/common/ai-services/index.js.map +1 -0
- package/dist/common/ai-services/internal/build-service-command.cjs +270 -0
- package/dist/common/ai-services/internal/build-service-command.cjs.map +1 -0
- package/dist/common/ai-services/internal/build-service-command.js +263 -0
- package/dist/common/ai-services/internal/build-service-command.js.map +1 -0
- package/dist/common/ai-services/internal/cli-management.cjs +94 -0
- package/dist/common/ai-services/internal/cli-management.cjs.map +1 -0
- package/dist/common/ai-services/internal/cli-management.js +90 -0
- package/dist/common/ai-services/internal/cli-management.js.map +1 -0
- package/dist/common/ai-services/internal/execute-attempt.cjs +241 -0
- package/dist/common/ai-services/internal/execute-attempt.cjs.map +1 -0
- package/dist/common/ai-services/internal/execute-attempt.js +232 -0
- package/dist/common/ai-services/internal/execute-attempt.js.map +1 -0
- package/dist/common/ai-services/internal/shared.cjs +60 -0
- package/dist/common/ai-services/internal/shared.cjs.map +1 -0
- package/dist/common/ai-services/internal/shared.js +50 -0
- package/dist/common/ai-services/internal/shared.js.map +1 -0
- package/dist/common/ai-services/internal/types.cjs +4 -0
- package/dist/common/ai-services/internal/types.cjs.map +1 -0
- package/dist/common/ai-services/internal/types.js +3 -0
- package/dist/common/ai-services/internal/types.js.map +1 -0
- package/dist/common/ai-services/stream-output.cjs +62 -0
- package/dist/common/ai-services/stream-output.cjs.map +1 -0
- package/dist/common/ai-services/stream-output.js +53 -0
- package/dist/common/ai-services/stream-output.js.map +1 -0
- package/dist/common/analytics/posthog.cjs +212 -0
- package/dist/common/analytics/posthog.cjs.map +1 -0
- package/dist/common/analytics/posthog.js +204 -0
- package/dist/common/analytics/posthog.js.map +1 -0
- package/dist/common/app/run-cli-app.cjs +4440 -0
- package/dist/common/app/run-cli-app.cjs.map +1 -0
- package/dist/common/app/run-cli-app.js +4426 -0
- package/dist/common/app/run-cli-app.js.map +1 -0
- package/dist/common/app/service-selection.cjs +553 -0
- package/dist/common/app/service-selection.cjs.map +1 -0
- package/dist/common/app/service-selection.js +541 -0
- package/dist/common/app/service-selection.js.map +1 -0
- package/dist/common/command-runtime/help.cjs +40 -0
- package/dist/common/command-runtime/help.cjs.map +1 -0
- package/dist/common/command-runtime/help.js +38 -0
- package/dist/common/command-runtime/help.js.map +1 -0
- package/dist/common/command-runtime/index.cjs +2549 -0
- package/dist/common/command-runtime/index.cjs.map +1 -0
- package/dist/common/command-runtime/index.js +2525 -0
- package/dist/common/command-runtime/index.js.map +1 -0
- package/dist/common/command-runtime/loader.cjs +145 -0
- package/dist/common/command-runtime/loader.cjs.map +1 -0
- package/dist/common/command-runtime/loader.js +135 -0
- package/dist/common/command-runtime/loader.js.map +1 -0
- package/dist/common/command-runtime/parser.cjs +57 -0
- package/dist/common/command-runtime/parser.cjs.map +1 -0
- package/dist/common/command-runtime/parser.js +55 -0
- package/dist/common/command-runtime/parser.js.map +1 -0
- package/dist/common/command-runtime/registry.cjs +2379 -0
- package/dist/common/command-runtime/registry.cjs.map +1 -0
- package/dist/common/command-runtime/registry.js +2357 -0
- package/dist/common/command-runtime/registry.js.map +1 -0
- package/dist/common/command-runtime/types.cjs +4 -0
- package/dist/common/command-runtime/types.cjs.map +1 -0
- package/dist/common/command-runtime/types.js +3 -0
- package/dist/common/command-runtime/types.js.map +1 -0
- package/dist/common/core/cancellation.cjs +34 -0
- package/dist/common/core/cancellation.cjs.map +1 -0
- package/dist/common/core/cancellation.js +29 -0
- package/dist/common/core/cancellation.js.map +1 -0
- package/dist/common/core/helpers.cjs +678 -0
- package/dist/common/core/helpers.cjs.map +1 -0
- package/dist/common/core/helpers.js +633 -0
- package/dist/common/core/helpers.js.map +1 -0
- package/dist/common/core/types.cjs +4 -0
- package/dist/common/core/types.cjs.map +1 -0
- package/dist/common/core/types.js +3 -0
- package/dist/common/core/types.js.map +1 -0
- package/dist/common/local-server/index.cjs +1665 -0
- package/dist/common/local-server/index.cjs.map +1 -0
- package/dist/common/local-server/index.js +1647 -0
- package/dist/common/local-server/index.js.map +1 -0
- package/dist/common/prompts/index.cjs +215 -0
- package/dist/common/prompts/index.cjs.map +1 -0
- package/dist/common/prompts/index.js +204 -0
- package/dist/common/prompts/index.js.map +1 -0
- package/dist/common/prompts/render.cjs +90 -0
- package/dist/common/prompts/render.cjs.map +1 -0
- package/dist/common/prompts/render.js +83 -0
- package/dist/common/prompts/render.js.map +1 -0
- package/dist/common/runtime-profile.cjs +46 -0
- package/dist/common/runtime-profile.cjs.map +1 -0
- package/dist/common/runtime-profile.js +40 -0
- package/dist/common/runtime-profile.js.map +1 -0
- package/dist/common/security/default-rules.cjs +49 -0
- package/dist/common/security/default-rules.cjs.map +1 -0
- package/dist/common/security/default-rules.js +42 -0
- package/dist/common/security/default-rules.js.map +1 -0
- package/dist/common/security/guard.cjs +784 -0
- package/dist/common/security/guard.cjs.map +1 -0
- package/dist/common/security/guard.js +775 -0
- package/dist/common/security/guard.js.map +1 -0
- package/dist/common/security/policy.cjs +117 -0
- package/dist/common/security/policy.cjs.map +1 -0
- package/dist/common/security/policy.js +105 -0
- package/dist/common/security/policy.js.map +1 -0
- package/dist/common/security/registry.cjs +456 -0
- package/dist/common/security/registry.cjs.map +1 -0
- package/dist/common/security/registry.js +445 -0
- package/dist/common/security/registry.js.map +1 -0
- package/dist/common/security/resource-resolver.cjs +314 -0
- package/dist/common/security/resource-resolver.cjs.map +1 -0
- package/dist/common/security/resource-resolver.js +306 -0
- package/dist/common/security/resource-resolver.js.map +1 -0
- package/dist/common/security/types.cjs +4 -0
- package/dist/common/security/types.cjs.map +1 -0
- package/dist/common/security/types.js +3 -0
- package/dist/common/security/types.js.map +1 -0
- package/dist/common/types/app.cjs +4 -0
- package/dist/common/types/app.cjs.map +1 -0
- package/dist/common/types/app.js +3 -0
- package/dist/common/types/app.js.map +1 -0
- package/dist/common/types/command.cjs +4 -0
- package/dist/common/types/command.cjs.map +1 -0
- package/dist/common/types/command.js +3 -0
- package/dist/common/types/command.js.map +1 -0
- package/dist/common/types/history.cjs +4 -0
- package/dist/common/types/history.cjs.map +1 -0
- package/dist/common/types/history.js +3 -0
- package/dist/common/types/history.js.map +1 -0
- package/dist/common/types/index.cjs +4 -0
- package/dist/common/types/index.cjs.map +1 -0
- package/dist/common/types/index.js +3 -0
- package/dist/common/types/index.js.map +1 -0
- package/dist/common/types/prompt.cjs +4 -0
- package/dist/common/types/prompt.cjs.map +1 -0
- package/dist/common/types/prompt.js +3 -0
- package/dist/common/types/prompt.js.map +1 -0
- package/dist/common/types/security.cjs +4 -0
- package/dist/common/types/security.cjs.map +1 -0
- package/dist/common/types/security.js +3 -0
- package/dist/common/types/security.js.map +1 -0
- package/dist/common/types/service-run.cjs +4 -0
- package/dist/common/types/service-run.cjs.map +1 -0
- package/dist/common/types/service-run.js +3 -0
- package/dist/common/types/service-run.js.map +1 -0
- package/dist/common/types/storage.cjs +4 -0
- package/dist/common/types/storage.cjs.map +1 -0
- package/dist/common/types/storage.js +3 -0
- package/dist/common/types/storage.js.map +1 -0
- package/dist/common/types/ui.cjs +4 -0
- package/dist/common/types/ui.cjs.map +1 -0
- package/dist/common/types/ui.js +3 -0
- package/dist/common/types/ui.js.map +1 -0
- package/dist/common/ui/index.cjs +854 -0
- package/dist/common/ui/index.cjs.map +1 -0
- package/dist/common/ui/index.js +836 -0
- package/dist/common/ui/index.js.map +1 -0
- package/dist/fe.cjs +4446 -0
- package/dist/fe.cjs.map +1 -0
- package/dist/fe.js +4432 -0
- package/dist/fe.js.map +1 -0
- package/package.json +52 -0
- package/src/common/context/rules/be/commit.md +50 -0
- package/src/common/context/rules/be/dsp-be-guide.md +184 -0
- package/src/common/context/rules/be/reference-summary.md +130 -0
- package/src/common/context/rules/fe/base.md +280 -0
- package/src/common/context/rules/fe/clean-architecture-diagram.html +189 -0
- package/src/common/context/rules/fe/clean-architecture-diagram.md +114 -0
- package/src/common/context/rules/fe/clean-architecture-diagram.png +0 -0
- package/src/common/context/rules/fe/clean-architecture.md +488 -0
- package/src/common/context/rules/fe/coding-convention.md +617 -0
- package/src/common/context/rules/fe/commit.md +48 -0
- package/src/common/context/rules/fe/figma-to-code.md +502 -0
- package/src/common/context/rules/fe/naming-rule.md +345 -0
- package/src/common/context/rules/fe/testing-guide.md +415 -0
- package/src/common/prompts/assets/chat/forms/chat-response.md +3 -0
- package/src/common/prompts/assets/chat/instructions/chat-request.md +5 -0
- package/src/common/prompts/assets/common/base-instructions.md +4 -0
package/dist/be.cjs
ADDED
|
@@ -0,0 +1,4446 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var child_process = require('child_process');
|
|
5
|
+
var fs7 = require('fs');
|
|
6
|
+
var path6 = require('path');
|
|
7
|
+
var util = require('util');
|
|
8
|
+
var fs2 = require('fs/promises');
|
|
9
|
+
var os = require('os');
|
|
10
|
+
var url = require('url');
|
|
11
|
+
var process$1 = require('process');
|
|
12
|
+
var readline = require('readline');
|
|
13
|
+
var posthogNode = require('posthog-node');
|
|
14
|
+
require('crypto');
|
|
15
|
+
require('http');
|
|
16
|
+
var module$1 = require('module');
|
|
17
|
+
require('net');
|
|
18
|
+
|
|
19
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
20
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
21
|
+
|
|
22
|
+
var fs7__default = /*#__PURE__*/_interopDefault(fs7);
|
|
23
|
+
var path6__default = /*#__PURE__*/_interopDefault(path6);
|
|
24
|
+
var fs2__default = /*#__PURE__*/_interopDefault(fs2);
|
|
25
|
+
var os__default = /*#__PURE__*/_interopDefault(os);
|
|
26
|
+
var readline__default = /*#__PURE__*/_interopDefault(readline);
|
|
27
|
+
|
|
28
|
+
var AI_SERVICES = ["codex", "gemini", "claude"];
|
|
29
|
+
var DEFAULT_HISTORY_LIMIT = 5;
|
|
30
|
+
var DEFAULT_HISTORY_CHAR_LIMIT = 14e3;
|
|
31
|
+
var PROMPT_HISTORY_LIMIT = 100;
|
|
32
|
+
var CONVERSATION_HISTORY_SCOPES = [
|
|
33
|
+
"chat",
|
|
34
|
+
"commit",
|
|
35
|
+
"review",
|
|
36
|
+
"gen-html",
|
|
37
|
+
"gen-storybook",
|
|
38
|
+
"local-server",
|
|
39
|
+
"mcp",
|
|
40
|
+
"native-command",
|
|
41
|
+
"security-policy",
|
|
42
|
+
"show-stream",
|
|
43
|
+
"service-change"
|
|
44
|
+
];
|
|
45
|
+
var TRACE_MESSAGES = [];
|
|
46
|
+
function pad(value) {
|
|
47
|
+
return String(value).padStart(2, "0");
|
|
48
|
+
}
|
|
49
|
+
function getTimestampParts(now = /* @__PURE__ */ new Date()) {
|
|
50
|
+
return {
|
|
51
|
+
year: now.getFullYear(),
|
|
52
|
+
month: pad(now.getMonth() + 1),
|
|
53
|
+
day: pad(now.getDate()),
|
|
54
|
+
hour: pad(now.getHours()),
|
|
55
|
+
minute: pad(now.getMinutes()),
|
|
56
|
+
second: pad(now.getSeconds())
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function getNowString(now = /* @__PURE__ */ new Date()) {
|
|
60
|
+
const { year, month, day, hour, minute, second } = getTimestampParts(now);
|
|
61
|
+
return `${year}-${month}-${day}_${hour}-${minute}-${second}`;
|
|
62
|
+
}
|
|
63
|
+
function getHumanReadableNow(now = /* @__PURE__ */ new Date()) {
|
|
64
|
+
const { year, month, day, hour, minute, second } = getTimestampParts(now);
|
|
65
|
+
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
|
|
66
|
+
}
|
|
67
|
+
function getStoragePaths(rootDir = process.cwd()) {
|
|
68
|
+
const baseDir = path6__default.default.join(rootDir, ".ju-hong-e");
|
|
69
|
+
const codeReviewDir = path6__default.default.join(baseDir, "code-review");
|
|
70
|
+
const historyDir = path6__default.default.join(baseDir, "ai-history");
|
|
71
|
+
const mcpDir = path6__default.default.join(baseDir, "mcp");
|
|
72
|
+
return {
|
|
73
|
+
baseDir,
|
|
74
|
+
codeReviewDir,
|
|
75
|
+
conversationHistoryPath: path6__default.default.join(baseDir, "conversation-history.jsonl"),
|
|
76
|
+
errorDir: path6__default.default.join(baseDir, "error"),
|
|
77
|
+
historyDir,
|
|
78
|
+
historyIndexPath: path6__default.default.join(historyDir, "index.md"),
|
|
79
|
+
mcpDir,
|
|
80
|
+
mcpRegistryPath: path6__default.default.join(mcpDir, "servers.json"),
|
|
81
|
+
promptHistoryPath: path6__default.default.join(baseDir, "prompt-history.json"),
|
|
82
|
+
rootDir,
|
|
83
|
+
securityPolicyPath: path6__default.default.join(baseDir, "security-policy.json"),
|
|
84
|
+
serviceFilePath: path6__default.default.join(baseDir, "service"),
|
|
85
|
+
servicesDir: path6__default.default.join(historyDir, "services"),
|
|
86
|
+
streamOutputConfigPath: path6__default.default.join(baseDir, "show-stream.json"),
|
|
87
|
+
testDir: path6__default.default.join(baseDir, "test"),
|
|
88
|
+
tmpDir: path6__default.default.join(baseDir, "tmp"),
|
|
89
|
+
turnsDir: path6__default.default.join(historyDir, "turns")
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function ensureStorageDirectories(paths) {
|
|
93
|
+
[paths.baseDir, paths.codeReviewDir, paths.historyDir, paths.turnsDir, paths.servicesDir, paths.testDir, paths.errorDir, paths.tmpDir, paths.mcpDir].forEach((directory) => {
|
|
94
|
+
fs7__default.default.mkdirSync(directory, { recursive: true });
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function parseCliArgs(args = process.argv.slice(2)) {
|
|
98
|
+
const parsed = {
|
|
99
|
+
help: false,
|
|
100
|
+
testMode: false,
|
|
101
|
+
traceMode: false
|
|
102
|
+
};
|
|
103
|
+
const getNextValue = (index) => {
|
|
104
|
+
const value = args[index + 1];
|
|
105
|
+
if (!value || value.startsWith("--")) {
|
|
106
|
+
return "";
|
|
107
|
+
}
|
|
108
|
+
return value;
|
|
109
|
+
};
|
|
110
|
+
args.forEach((arg, index) => {
|
|
111
|
+
if (arg === "--help" || arg === "-h") {
|
|
112
|
+
parsed.help = true;
|
|
113
|
+
}
|
|
114
|
+
if (arg === "--test") {
|
|
115
|
+
parsed.testMode = true;
|
|
116
|
+
}
|
|
117
|
+
if (arg === "--trace") {
|
|
118
|
+
parsed.traceMode = true;
|
|
119
|
+
}
|
|
120
|
+
if (arg === "--model") {
|
|
121
|
+
parsed.model = getNextValue(index) || void 0;
|
|
122
|
+
}
|
|
123
|
+
if (arg === "--service") {
|
|
124
|
+
const service = getNextValue(index);
|
|
125
|
+
if (AI_SERVICES.includes(service)) {
|
|
126
|
+
parsed.service = service;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (arg === "--reasoning-effort") {
|
|
130
|
+
const effort = getNextValue(index);
|
|
131
|
+
if (["minimal", "low", "medium", "high"].includes(effort)) {
|
|
132
|
+
parsed.reasoningEffort = effort;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
return parsed;
|
|
137
|
+
}
|
|
138
|
+
function clearTraceMessages() {
|
|
139
|
+
TRACE_MESSAGES.length = 0;
|
|
140
|
+
}
|
|
141
|
+
function getTraceMessages() {
|
|
142
|
+
return [...TRACE_MESSAGES];
|
|
143
|
+
}
|
|
144
|
+
function createTraceLogger(scope, args) {
|
|
145
|
+
const enabled = args.testMode || args.traceMode;
|
|
146
|
+
return (step, detail) => {
|
|
147
|
+
const message = `[${(/* @__PURE__ */ new Date()).toISOString()}][TRACE][${scope}] ${step}${detail ? ` | ${detail}` : ""}`;
|
|
148
|
+
TRACE_MESSAGES.push(message);
|
|
149
|
+
if (enabled) {
|
|
150
|
+
console.log(message);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function shellQuote(value) {
|
|
155
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
156
|
+
}
|
|
157
|
+
function stringifyUnknown(value) {
|
|
158
|
+
if (value === void 0 || value === null) {
|
|
159
|
+
return "";
|
|
160
|
+
}
|
|
161
|
+
if (typeof value === "string") {
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
if (Buffer.isBuffer(value)) {
|
|
165
|
+
return value.toString("utf8");
|
|
166
|
+
}
|
|
167
|
+
if (value instanceof Error) {
|
|
168
|
+
return value.stack || value.message;
|
|
169
|
+
}
|
|
170
|
+
return util.inspect(value, { depth: 6, breakLength: 120 });
|
|
171
|
+
}
|
|
172
|
+
function getErrorSummary(error) {
|
|
173
|
+
if (error instanceof Error) {
|
|
174
|
+
return `${error.name}: ${error.message}`;
|
|
175
|
+
}
|
|
176
|
+
return stringifyUnknown(error) || "Unknown error";
|
|
177
|
+
}
|
|
178
|
+
function serializeError(error) {
|
|
179
|
+
const serialized = {
|
|
180
|
+
summary: getErrorSummary(error)
|
|
181
|
+
};
|
|
182
|
+
if (error instanceof Error) {
|
|
183
|
+
serialized.name = error.name;
|
|
184
|
+
serialized.message = error.message;
|
|
185
|
+
serialized.stack = error.stack;
|
|
186
|
+
} else {
|
|
187
|
+
serialized.value = stringifyUnknown(error);
|
|
188
|
+
}
|
|
189
|
+
if (error && typeof error === "object") {
|
|
190
|
+
const errorLike = error;
|
|
191
|
+
["code", "errno", "syscall", "path", "cmd", "status", "signal"].forEach((key) => {
|
|
192
|
+
if (errorLike[key] !== void 0) {
|
|
193
|
+
serialized[key] = errorLike[key];
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
const stdout2 = stringifyUnknown(errorLike.stdout);
|
|
197
|
+
if (stdout2) {
|
|
198
|
+
serialized.stdout = stdout2;
|
|
199
|
+
}
|
|
200
|
+
const stderr = stringifyUnknown(errorLike.stderr);
|
|
201
|
+
if (stderr) {
|
|
202
|
+
serialized.stderr = stderr;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return serialized;
|
|
206
|
+
}
|
|
207
|
+
function runGitCommand(rootDir, command) {
|
|
208
|
+
return child_process.execSync(command, {
|
|
209
|
+
cwd: rootDir,
|
|
210
|
+
encoding: "utf8",
|
|
211
|
+
maxBuffer: 1024 * 1024 * 20,
|
|
212
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
213
|
+
}).trim();
|
|
214
|
+
}
|
|
215
|
+
function isGitRepository(rootDir) {
|
|
216
|
+
try {
|
|
217
|
+
runGitCommand(rootDir, "git rev-parse --show-toplevel");
|
|
218
|
+
return true;
|
|
219
|
+
} catch {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
function captureWorkspaceSnapshot(rootDir) {
|
|
224
|
+
if (!isGitRepository(rootDir)) {
|
|
225
|
+
return {
|
|
226
|
+
available: false,
|
|
227
|
+
branch: "",
|
|
228
|
+
diffNameStatus: "",
|
|
229
|
+
diffStat: "",
|
|
230
|
+
gitRoot: "",
|
|
231
|
+
note: "\uD604\uC7AC \uC791\uC5C5 \uACBD\uB85C\uAC00 Git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4.",
|
|
232
|
+
recentCommits: [],
|
|
233
|
+
statusLines: []
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
const gitRoot = runGitCommand(rootDir, "git rev-parse --show-toplevel");
|
|
237
|
+
const branch = runGitCommand(rootDir, "git branch --show-current");
|
|
238
|
+
const statusOutput = runGitCommand(rootDir, "git status --short");
|
|
239
|
+
const diffStat = runGitCommand(rootDir, "git diff --stat");
|
|
240
|
+
const diffNameStatus = runGitCommand(rootDir, "git diff --name-status");
|
|
241
|
+
const recentCommits = runGitCommand(rootDir, "git log --oneline -5");
|
|
242
|
+
return {
|
|
243
|
+
available: true,
|
|
244
|
+
branch,
|
|
245
|
+
diffNameStatus,
|
|
246
|
+
diffStat,
|
|
247
|
+
gitRoot,
|
|
248
|
+
note: "",
|
|
249
|
+
recentCommits: recentCommits ? recentCommits.split("\n").filter(Boolean) : [],
|
|
250
|
+
statusLines: statusOutput ? statusOutput.split("\n").filter(Boolean) : []
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function statusLineToPath(statusLine) {
|
|
254
|
+
const normalized = statusLine.trim();
|
|
255
|
+
if (!normalized) {
|
|
256
|
+
return "";
|
|
257
|
+
}
|
|
258
|
+
const pathValue = normalized.slice(3).split(" -> ").pop();
|
|
259
|
+
return pathValue?.trim() || normalized;
|
|
260
|
+
}
|
|
261
|
+
function diffWorkspaceSnapshots(before, after) {
|
|
262
|
+
const beforeSet = new Set(before.statusLines);
|
|
263
|
+
const afterSet = new Set(after.statusLines);
|
|
264
|
+
const beforeOnly = before.statusLines.filter((line) => !afterSet.has(line));
|
|
265
|
+
const afterOnly = after.statusLines.filter((line) => !beforeSet.has(line));
|
|
266
|
+
const touchedFiles = Array.from(
|
|
267
|
+
new Set([...beforeOnly.map(statusLineToPath), ...afterOnly.map(statusLineToPath)].filter(Boolean))
|
|
268
|
+
);
|
|
269
|
+
return {
|
|
270
|
+
afterOnly,
|
|
271
|
+
beforeOnly,
|
|
272
|
+
touchedFiles
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
function formatCodeBlock(content, language = "") {
|
|
276
|
+
return `\`\`\`${language}
|
|
277
|
+
${content || "(\uC5C6\uC74C)"}
|
|
278
|
+
\`\`\``;
|
|
279
|
+
}
|
|
280
|
+
function formatWorkspaceSnapshot(snapshot) {
|
|
281
|
+
if (!snapshot.available) {
|
|
282
|
+
return snapshot.note;
|
|
283
|
+
}
|
|
284
|
+
return [
|
|
285
|
+
`- Git Root: \`${snapshot.gitRoot}\``,
|
|
286
|
+
`- Branch: \`${snapshot.branch || "(detached)"}\``,
|
|
287
|
+
"",
|
|
288
|
+
"### git status --short",
|
|
289
|
+
formatCodeBlock(snapshot.statusLines.join("\n")),
|
|
290
|
+
"",
|
|
291
|
+
"### git diff --stat",
|
|
292
|
+
formatCodeBlock(snapshot.diffStat),
|
|
293
|
+
"",
|
|
294
|
+
"### git diff --name-status",
|
|
295
|
+
formatCodeBlock(snapshot.diffNameStatus),
|
|
296
|
+
"",
|
|
297
|
+
"### \uCD5C\uADFC \uCEE4\uBC0B",
|
|
298
|
+
formatCodeBlock(snapshot.recentCommits.join("\n"))
|
|
299
|
+
].join("\n");
|
|
300
|
+
}
|
|
301
|
+
function formatWorkspaceDelta(delta) {
|
|
302
|
+
return [
|
|
303
|
+
"### \uC0C8\uB85C \uAC10\uC9C0\uB41C \uC0C1\uD0DC",
|
|
304
|
+
formatCodeBlock(delta.afterOnly.join("\n")),
|
|
305
|
+
"",
|
|
306
|
+
"### \uC0AC\uB77C\uC9C4 \uC774\uC804 \uC0C1\uD0DC",
|
|
307
|
+
formatCodeBlock(delta.beforeOnly.join("\n")),
|
|
308
|
+
"",
|
|
309
|
+
"### \uC601\uD5A5 \uD30C\uC77C",
|
|
310
|
+
formatCodeBlock(delta.touchedFiles.join("\n"))
|
|
311
|
+
].join("\n");
|
|
312
|
+
}
|
|
313
|
+
function safeExcerpt(content, limit = 3e3) {
|
|
314
|
+
if (!content) {
|
|
315
|
+
return "";
|
|
316
|
+
}
|
|
317
|
+
if (content.length <= limit) {
|
|
318
|
+
return content;
|
|
319
|
+
}
|
|
320
|
+
return `${content.slice(0, limit)}
|
|
321
|
+
... (truncated ${content.length - limit} chars)`;
|
|
322
|
+
}
|
|
323
|
+
function isConversationHistoryScope(value) {
|
|
324
|
+
return CONVERSATION_HISTORY_SCOPES.includes(value);
|
|
325
|
+
}
|
|
326
|
+
function parseConversationHistoryEntry(raw) {
|
|
327
|
+
try {
|
|
328
|
+
const parsed = JSON.parse(raw);
|
|
329
|
+
if (!parsed || typeof parsed !== "object") {
|
|
330
|
+
return void 0;
|
|
331
|
+
}
|
|
332
|
+
if (typeof parsed.timestamp !== "string" || typeof parsed.title !== "string" || typeof parsed.summary !== "string" || typeof parsed.scope !== "string" || typeof parsed.service !== "string") {
|
|
333
|
+
return void 0;
|
|
334
|
+
}
|
|
335
|
+
if (!isConversationHistoryScope(parsed.scope) || !AI_SERVICES.includes(parsed.service)) {
|
|
336
|
+
return void 0;
|
|
337
|
+
}
|
|
338
|
+
const previousService = typeof parsed.previousService === "string" && (parsed.previousService === "" || AI_SERVICES.includes(parsed.previousService)) ? parsed.previousService : void 0;
|
|
339
|
+
const nextService = typeof parsed.nextService === "string" && AI_SERVICES.includes(parsed.nextService) ? parsed.nextService : void 0;
|
|
340
|
+
return {
|
|
341
|
+
input: typeof parsed.input === "string" ? parsed.input : void 0,
|
|
342
|
+
nextService,
|
|
343
|
+
output: typeof parsed.output === "string" ? parsed.output : void 0,
|
|
344
|
+
previousService,
|
|
345
|
+
scope: parsed.scope,
|
|
346
|
+
service: parsed.service,
|
|
347
|
+
summary: parsed.summary,
|
|
348
|
+
timestamp: parsed.timestamp,
|
|
349
|
+
title: parsed.title
|
|
350
|
+
};
|
|
351
|
+
} catch {
|
|
352
|
+
return void 0;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function appendConversationHistory(paths, entry) {
|
|
356
|
+
const normalizedEntry = {
|
|
357
|
+
...entry,
|
|
358
|
+
summary: entry.summary.trim() || entry.title.trim(),
|
|
359
|
+
timestamp: entry.timestamp || getHumanReadableNow(),
|
|
360
|
+
title: entry.title.trim()
|
|
361
|
+
};
|
|
362
|
+
fs7__default.default.appendFileSync(paths.conversationHistoryPath, `${JSON.stringify(normalizedEntry)}
|
|
363
|
+
`, "utf8");
|
|
364
|
+
return normalizedEntry;
|
|
365
|
+
}
|
|
366
|
+
function loadConversationHistory(paths, limit = DEFAULT_HISTORY_LIMIT) {
|
|
367
|
+
if (!fs7__default.default.existsSync(paths.conversationHistoryPath)) {
|
|
368
|
+
return [];
|
|
369
|
+
}
|
|
370
|
+
const lines = fs7__default.default.readFileSync(paths.conversationHistoryPath, "utf8").split("\n").map((line) => line.trim()).filter(Boolean);
|
|
371
|
+
if (lines.length === 0) {
|
|
372
|
+
return [];
|
|
373
|
+
}
|
|
374
|
+
return lines.map(parseConversationHistoryEntry).filter((entry) => entry !== void 0).slice(-limit);
|
|
375
|
+
}
|
|
376
|
+
function formatConversationHistoryEntry(entry, limit) {
|
|
377
|
+
const inputLimit = Math.max(180, Math.floor(limit * 0.35));
|
|
378
|
+
const outputLimit = Math.max(220, limit - inputLimit);
|
|
379
|
+
const lines = [`## ${entry.timestamp} | ${entry.scope} | ${entry.service}`, `\uC81C\uBAA9: ${entry.title}`];
|
|
380
|
+
if (entry.scope === "service-change") {
|
|
381
|
+
lines.push(`\uC11C\uBE44\uC2A4 \uBCC0\uACBD: ${entry.previousService || "(\uC5C6\uC74C)"} -> ${entry.nextService || entry.service}`);
|
|
382
|
+
lines.push(`\uC694\uC57D: ${safeExcerpt(entry.summary, outputLimit)}`);
|
|
383
|
+
return lines.join("\n");
|
|
384
|
+
}
|
|
385
|
+
if (entry.input) {
|
|
386
|
+
lines.push("");
|
|
387
|
+
lines.push("\uC0AC\uC6A9\uC790 \uC785\uB825");
|
|
388
|
+
lines.push(formatCodeBlock(safeExcerpt(entry.input, inputLimit)));
|
|
389
|
+
}
|
|
390
|
+
if (entry.output) {
|
|
391
|
+
lines.push("");
|
|
392
|
+
lines.push("\uC774\uC804 \uACB0\uACFC \uC694\uC57D");
|
|
393
|
+
lines.push(formatCodeBlock(safeExcerpt(entry.output, outputLimit)));
|
|
394
|
+
} else if (entry.summary) {
|
|
395
|
+
lines.push("");
|
|
396
|
+
lines.push("\uACB0\uACFC \uC694\uC57D");
|
|
397
|
+
lines.push(formatCodeBlock(safeExcerpt(entry.summary, outputLimit)));
|
|
398
|
+
}
|
|
399
|
+
return lines.join("\n");
|
|
400
|
+
}
|
|
401
|
+
function formatConversationHistory(entries, charLimit) {
|
|
402
|
+
if (entries.length === 0) {
|
|
403
|
+
return "";
|
|
404
|
+
}
|
|
405
|
+
const entryLimit = Math.max(400, Math.floor(charLimit / Math.max(entries.length, 1)));
|
|
406
|
+
const content = entries.map((entry) => formatConversationHistoryEntry(entry, entryLimit)).join("\n\n");
|
|
407
|
+
return safeExcerpt(content, charLimit);
|
|
408
|
+
}
|
|
409
|
+
function loadRecentHistoryContext(paths, limit = DEFAULT_HISTORY_LIMIT, charLimit = DEFAULT_HISTORY_CHAR_LIMIT) {
|
|
410
|
+
const conversationEntries = loadConversationHistory(paths, limit);
|
|
411
|
+
return formatConversationHistory(conversationEntries, charLimit);
|
|
412
|
+
}
|
|
413
|
+
function getAvailableFilePath(directory, baseName, extension) {
|
|
414
|
+
const first = path6__default.default.join(directory, `${baseName}${extension}`);
|
|
415
|
+
if (!fs7__default.default.existsSync(first)) {
|
|
416
|
+
return first;
|
|
417
|
+
}
|
|
418
|
+
let next = first;
|
|
419
|
+
let count = 1;
|
|
420
|
+
while (fs7__default.default.existsSync(next)) {
|
|
421
|
+
next = path6__default.default.join(directory, `${baseName}-${count}${extension}`);
|
|
422
|
+
if (!fs7__default.default.existsSync(next)) {
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
count += 1;
|
|
426
|
+
}
|
|
427
|
+
return next;
|
|
428
|
+
}
|
|
429
|
+
function readSavedService(paths) {
|
|
430
|
+
if (!fs7__default.default.existsSync(paths.serviceFilePath)) {
|
|
431
|
+
return "";
|
|
432
|
+
}
|
|
433
|
+
const service = fs7__default.default.readFileSync(paths.serviceFilePath, "utf8").trim();
|
|
434
|
+
return AI_SERVICES.includes(service) ? service : "";
|
|
435
|
+
}
|
|
436
|
+
function saveService(paths, service) {
|
|
437
|
+
fs7__default.default.writeFileSync(paths.serviceFilePath, `${service}
|
|
438
|
+
`, "utf8");
|
|
439
|
+
}
|
|
440
|
+
function loadPromptHistory(paths, limit = PROMPT_HISTORY_LIMIT) {
|
|
441
|
+
if (!fs7__default.default.existsSync(paths.promptHistoryPath)) {
|
|
442
|
+
return [];
|
|
443
|
+
}
|
|
444
|
+
try {
|
|
445
|
+
const parsed = JSON.parse(fs7__default.default.readFileSync(paths.promptHistoryPath, "utf8"));
|
|
446
|
+
if (!Array.isArray(parsed)) {
|
|
447
|
+
return [];
|
|
448
|
+
}
|
|
449
|
+
return parsed.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter(Boolean).slice(-limit);
|
|
450
|
+
} catch {
|
|
451
|
+
return [];
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
function savePromptHistory(paths, history) {
|
|
455
|
+
const normalized = history.map((entry) => entry.trim()).filter(Boolean).slice(-100);
|
|
456
|
+
fs7__default.default.writeFileSync(paths.promptHistoryPath, JSON.stringify(normalized, null, 2), "utf8");
|
|
457
|
+
}
|
|
458
|
+
function appendPromptHistory(paths, value) {
|
|
459
|
+
const normalizedValue = value.trim();
|
|
460
|
+
if (!normalizedValue) {
|
|
461
|
+
return loadPromptHistory(paths);
|
|
462
|
+
}
|
|
463
|
+
const existing = loadPromptHistory(paths).filter((entry) => entry !== normalizedValue);
|
|
464
|
+
const nextHistory = [...existing, normalizedValue].slice(-100);
|
|
465
|
+
savePromptHistory(paths, nextHistory);
|
|
466
|
+
return nextHistory;
|
|
467
|
+
}
|
|
468
|
+
function appendHistoryIndex(paths, summary, relativeFilePath) {
|
|
469
|
+
const line = `- ${getHumanReadableNow()} | ${summary} | file: \`${relativeFilePath}\`
|
|
470
|
+
`;
|
|
471
|
+
fs7__default.default.appendFileSync(paths.historyIndexPath, line, "utf8");
|
|
472
|
+
}
|
|
473
|
+
function renderSections(sections = []) {
|
|
474
|
+
if (sections.length === 0) {
|
|
475
|
+
return "";
|
|
476
|
+
}
|
|
477
|
+
return sections.map((section) => `
|
|
478
|
+
## ${section.heading}
|
|
479
|
+
|
|
480
|
+
${section.markdown}
|
|
481
|
+
`).join("\n");
|
|
482
|
+
}
|
|
483
|
+
function writeTurnHistory(paths, record) {
|
|
484
|
+
const baseName = `${getNowString()}-${record.scope}`;
|
|
485
|
+
const filePath = getAvailableFilePath(paths.turnsDir, baseName, ".md");
|
|
486
|
+
const markdown = `# ${record.title}
|
|
487
|
+
|
|
488
|
+
- \uC2DC\uAC01: ${getHumanReadableNow()}
|
|
489
|
+
- scope: \`${record.scope}\`
|
|
490
|
+
- service: \`${record.service}\`
|
|
491
|
+
- skipped: \`${record.skipped}\`
|
|
492
|
+
|
|
493
|
+
## \uC0AC\uC6A9\uC790 \uC785\uB825
|
|
494
|
+
|
|
495
|
+
${formatCodeBlock(record.input)}
|
|
496
|
+
|
|
497
|
+
## AI \uC751\uB2F5
|
|
498
|
+
|
|
499
|
+
${record.output || "(\uC751\uB2F5 \uC5C6\uC74C)"}
|
|
500
|
+
|
|
501
|
+
## \uCD5C\uADFC \uD788\uC2A4\uD1A0\uB9AC \uC8FC\uC785
|
|
502
|
+
|
|
503
|
+
${formatCodeBlock(record.historyContext)}
|
|
504
|
+
|
|
505
|
+
## \uC2E4\uD589 \uBA85\uB839\uC5B4 \uBBF8\uB9AC\uBCF4\uAE30
|
|
506
|
+
|
|
507
|
+
${formatCodeBlock(record.previewCommand, "sh")}
|
|
508
|
+
|
|
509
|
+
## \uC791\uC5C5 \uC804 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4
|
|
510
|
+
|
|
511
|
+
${formatWorkspaceSnapshot(record.workspaceBefore)}
|
|
512
|
+
|
|
513
|
+
## \uC791\uC5C5 \uD6C4 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4
|
|
514
|
+
|
|
515
|
+
${formatWorkspaceSnapshot(record.workspaceAfter)}
|
|
516
|
+
|
|
517
|
+
## \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uBCC0\uD654
|
|
518
|
+
|
|
519
|
+
${formatWorkspaceDelta(record.workspaceDelta)}${renderSections(record.extraSections)}
|
|
520
|
+
`;
|
|
521
|
+
fs7__default.default.writeFileSync(filePath, markdown, "utf8");
|
|
522
|
+
appendHistoryIndex(paths, `${record.scope} | ${record.service} | ${record.title}`, path6__default.default.relative(paths.rootDir, filePath));
|
|
523
|
+
return filePath;
|
|
524
|
+
}
|
|
525
|
+
function writeServiceChangeHistory(paths, previousService, nextService, historyContext) {
|
|
526
|
+
const baseName = `${getNowString()}-service-change`;
|
|
527
|
+
const filePath = getAvailableFilePath(paths.servicesDir, baseName, ".md");
|
|
528
|
+
const markdown = `# Service Change
|
|
529
|
+
|
|
530
|
+
- \uC2DC\uAC01: ${getHumanReadableNow()}
|
|
531
|
+
- \uC774\uC804 \uC11C\uBE44\uC2A4: \`${previousService || "(\uC5C6\uC74C)"}\`
|
|
532
|
+
- \uB2E4\uC74C \uC11C\uBE44\uC2A4: \`${nextService}\`
|
|
533
|
+
|
|
534
|
+
## \uC8FC\uC785 \uC608\uC815 \uD788\uC2A4\uD1A0\uB9AC
|
|
535
|
+
|
|
536
|
+
${formatCodeBlock(historyContext)}
|
|
537
|
+
`;
|
|
538
|
+
fs7__default.default.writeFileSync(filePath, markdown, "utf8");
|
|
539
|
+
appendHistoryIndex(paths, `service-change | ${previousService || "(\uC5C6\uC74C)"} -> ${nextService}`, path6__default.default.relative(paths.rootDir, filePath));
|
|
540
|
+
return filePath;
|
|
541
|
+
}
|
|
542
|
+
function writeTestReport(paths, title, markdown) {
|
|
543
|
+
const filePath = getAvailableFilePath(paths.testDir, `${getNowString()}-test`, ".md");
|
|
544
|
+
const content = `# ${title}
|
|
545
|
+
|
|
546
|
+
- \uC2DC\uAC01: ${getHumanReadableNow()}
|
|
547
|
+
|
|
548
|
+
${markdown}
|
|
549
|
+
`;
|
|
550
|
+
fs7__default.default.writeFileSync(filePath, content, "utf8");
|
|
551
|
+
return filePath;
|
|
552
|
+
}
|
|
553
|
+
function writeErrorReport(paths, error, options) {
|
|
554
|
+
const filePath = getAvailableFilePath(paths.errorDir, `${getNowString()}-error`, ".md");
|
|
555
|
+
const serializedError = serializeError(error);
|
|
556
|
+
const traceMessages = options.traceMessages ?? getTraceMessages();
|
|
557
|
+
const markdown = `# Error Report
|
|
558
|
+
|
|
559
|
+
- \uC2DC\uAC01: ${getHumanReadableNow()}
|
|
560
|
+
- scope: \`${options.scope}\`
|
|
561
|
+
- \uC791\uC5C5 \uACBD\uB85C: \`${paths.rootDir}\`
|
|
562
|
+
- \uC2E4\uD589 \uC778\uC790: \`${JSON.stringify(options.args ?? process.argv.slice(2))}\`
|
|
563
|
+
|
|
564
|
+
## Summary
|
|
565
|
+
|
|
566
|
+
${serializedError.summary || "Unknown error"}
|
|
567
|
+
|
|
568
|
+
## Error
|
|
569
|
+
|
|
570
|
+
\`\`\`json
|
|
571
|
+
${JSON.stringify(serializedError, null, 2)}
|
|
572
|
+
\`\`\`
|
|
573
|
+
|
|
574
|
+
## Trace
|
|
575
|
+
|
|
576
|
+
\`\`\`json
|
|
577
|
+
${JSON.stringify(traceMessages, null, 2)}
|
|
578
|
+
\`\`\`${renderSections(options.extraSections)}
|
|
579
|
+
`;
|
|
580
|
+
fs7__default.default.writeFileSync(filePath, markdown, "utf8");
|
|
581
|
+
return filePath;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// src/common/prompts/assets/chat/forms/chat-response.md
|
|
585
|
+
var chat_response_default = "- \uBCC0\uACBD\uD55C \uD30C\uC77C \uBAA9\uB85D\n- \uD575\uC2EC \uACB0\uACFC 3\uC904 \uC774\uB0B4 \uC694\uC57D\n- \uCD94\uAC00 \uD655\uC778\uC774 \uD544\uC694\uD55C \uD56D\uBAA9\n";
|
|
586
|
+
|
|
587
|
+
// src/common/prompts/assets/chat/instructions/chat-request.md
|
|
588
|
+
var chat_request_default = "\uC0AC\uC6A9\uC790 \uC694\uCCAD\uC744 \uD574\uACB0\uD55C\uB2E4.\n\n- \uD544\uC694\uD55C \uACBD\uC6B0 \uC9C1\uC811 \uD30C\uC77C\uC744 \uC218\uC815\uD55C\uB2E4.\n- \uCF54\uB4DC\uB098 \uAD6C\uC870\uB97C \uC124\uBA85\uD560 \uB54C\uB294 \uC2E4\uC81C \uBCC0\uACBD \uB0B4\uC6A9 \uAE30\uC900\uC73C\uB85C \uC815\uB9AC\uD55C\uB2E4.\n- \uC751\uB2F5\uC740 \uC2E4\uD589 \uAC00\uB2A5\uD55C \uB2E4\uC74C \uC561\uC158\uC774 \uB4DC\uB7EC\uB098\uB3C4\uB85D \uC791\uC131\uD55C\uB2E4.\n";
|
|
589
|
+
|
|
590
|
+
// src/common/prompts/assets/common/base-instructions.md
|
|
591
|
+
var base_instructions_default = "- \uBAA8\uB4E0 \uC751\uB2F5\uC740 \uD55C\uAD6D\uC5B4\uB85C \uC791\uC131\uD55C\uB2E4.\n- \uD604\uC7AC \uC791\uC5C5 \uB514\uB809\uD130\uB9AC \uAE30\uC900\uC73C\uB85C \uD544\uC694\uD55C \uD30C\uC77C\uC744 \uC9C1\uC811 \uC218\uC815\uD560 \uC218 \uC788\uC73C\uBA74 \uC218\uC815\uD55C\uB2E4.\n- \uC218\uC815\uC774 \uC5B4\uB835\uAC70\uB098 \uB3C4\uAD6C \uC81C\uC57D\uC774 \uC788\uC73C\uBA74 \uC0DD\uC131\uD574\uC57C \uD560 \uD30C\uC77C\uACFC \uCF54\uB4DC \uB0B4\uC6A9\uC744 \uBA85\uD655\uD788 \uC81C\uC2DC\uD55C\uB2E4.\n- \uBCC0\uACBD\uC0AC\uD56D\uC740 \uC720\uC9C0\uBCF4\uC218\uC640 \uD655\uC7A5\uC131\uC744 \uC6B0\uC120\uC73C\uB85C \uACE0\uB824\uD55C\uB2E4.\n";
|
|
592
|
+
|
|
593
|
+
// src/common/prompts/index.ts
|
|
594
|
+
function buildCommonPrompt(options) {
|
|
595
|
+
return {
|
|
596
|
+
forms: options.forms,
|
|
597
|
+
historyContext: options.historyContext,
|
|
598
|
+
instructions: [base_instructions_default, ...options.instructions || []],
|
|
599
|
+
rules: options.rules,
|
|
600
|
+
sections: options.sections,
|
|
601
|
+
service: options.service,
|
|
602
|
+
title: options.title,
|
|
603
|
+
workspaceSnapshot: options.workspaceSnapshot
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
function buildChatPrompt(options) {
|
|
607
|
+
return buildCommonPrompt({
|
|
608
|
+
forms: [chat_response_default],
|
|
609
|
+
historyContext: options.historyContext,
|
|
610
|
+
instructions: [chat_request_default],
|
|
611
|
+
sections: [
|
|
612
|
+
{
|
|
613
|
+
heading: "\uC0AC\uC6A9\uC790 \uC694\uCCAD",
|
|
614
|
+
markdown: options.userInput
|
|
615
|
+
}
|
|
616
|
+
],
|
|
617
|
+
service: options.service,
|
|
618
|
+
title: "Ju-hong-e Chat",
|
|
619
|
+
workspaceSnapshot: options.workspaceSnapshot
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
var execFileAsync = util.promisify(child_process.execFile);
|
|
623
|
+
var MAX_REFERENCED_RESOURCES = 16;
|
|
624
|
+
var MAX_TEXT_LENGTH = 12e4;
|
|
625
|
+
var MAX_FILE_BUFFER = 1024 * 1024 * 4;
|
|
626
|
+
var MAX_REMOTE_BUFFER = 1024 * 1024 * 5;
|
|
627
|
+
var COMMAND_TIMEOUT_MS = 15e3;
|
|
628
|
+
var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
629
|
+
".cjs",
|
|
630
|
+
".conf",
|
|
631
|
+
".css",
|
|
632
|
+
".csv",
|
|
633
|
+
".d.ts",
|
|
634
|
+
".env",
|
|
635
|
+
".html",
|
|
636
|
+
".htm",
|
|
637
|
+
".ini",
|
|
638
|
+
".java",
|
|
639
|
+
".js",
|
|
640
|
+
".json",
|
|
641
|
+
".jsx",
|
|
642
|
+
".kt",
|
|
643
|
+
".less",
|
|
644
|
+
".log",
|
|
645
|
+
".md",
|
|
646
|
+
".mjs",
|
|
647
|
+
".php",
|
|
648
|
+
".py",
|
|
649
|
+
".rb",
|
|
650
|
+
".rs",
|
|
651
|
+
".scss",
|
|
652
|
+
".sh",
|
|
653
|
+
".sql",
|
|
654
|
+
".svg",
|
|
655
|
+
".swift",
|
|
656
|
+
".ts",
|
|
657
|
+
".tsx",
|
|
658
|
+
".txt",
|
|
659
|
+
".xml",
|
|
660
|
+
".yaml",
|
|
661
|
+
".yml",
|
|
662
|
+
".zsh"
|
|
663
|
+
]);
|
|
664
|
+
var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".bmp", ".gif", ".heic", ".jpeg", ".jpg", ".png", ".tif", ".tiff", ".webp"]);
|
|
665
|
+
var TEXTUTIL_EXTENSIONS = /* @__PURE__ */ new Set([".doc", ".docx", ".odt", ".rtf", ".rtfd", ".webarchive"]);
|
|
666
|
+
var REMOTE_URL_PATTERN = /\bhttps?:\/\/[^\s<>()"'`]+/gi;
|
|
667
|
+
var FILE_URL_PATTERN = /\bfile:\/\/[^\s<>()"'`]+/gi;
|
|
668
|
+
var LOCAL_PATH_WITH_PREFIX_PATTERN = /(?:^|[\s("'`])((?:~|\.{1,2}\/|\/)[^\s<>()"'`]+)/g;
|
|
669
|
+
var RELATIVE_PATH_WITH_SEPARATOR_PATTERN = /(?:^|[\s("'`])((?:[A-Za-z0-9._-]+\/)+[A-Za-z0-9._-]+\.[A-Za-z0-9._-]+)/g;
|
|
670
|
+
function trimTrailingPunctuation(value) {
|
|
671
|
+
return value.replace(/[),.;:!?`'"\]}]+$/g, "").trim();
|
|
672
|
+
}
|
|
673
|
+
function limitTextContent(value) {
|
|
674
|
+
if (!value) {
|
|
675
|
+
return "";
|
|
676
|
+
}
|
|
677
|
+
const normalized = value.replace(/\0/g, "").trim();
|
|
678
|
+
if (normalized.length <= MAX_TEXT_LENGTH) {
|
|
679
|
+
return normalized;
|
|
680
|
+
}
|
|
681
|
+
return normalized.slice(0, MAX_TEXT_LENGTH);
|
|
682
|
+
}
|
|
683
|
+
function isLikelyTextMimeType(mimeType) {
|
|
684
|
+
return mimeType.startsWith("text/") || mimeType.includes("json") || mimeType.includes("javascript") || mimeType.includes("xml") || mimeType.includes("yaml") || mimeType.includes("svg");
|
|
685
|
+
}
|
|
686
|
+
function isLikelyImageMimeType(mimeType) {
|
|
687
|
+
return mimeType.startsWith("image/");
|
|
688
|
+
}
|
|
689
|
+
function collectMatches(text, pattern) {
|
|
690
|
+
const values = [];
|
|
691
|
+
for (const match of text.matchAll(pattern)) {
|
|
692
|
+
const candidate = trimTrailingPunctuation((match[1] || match[0] || "").trim());
|
|
693
|
+
if (!candidate) {
|
|
694
|
+
continue;
|
|
695
|
+
}
|
|
696
|
+
values.push(candidate);
|
|
697
|
+
}
|
|
698
|
+
return values;
|
|
699
|
+
}
|
|
700
|
+
function resolveLocalResource(value, rootDir) {
|
|
701
|
+
try {
|
|
702
|
+
if (value.startsWith("file://")) {
|
|
703
|
+
return path6__default.default.normalize(url.fileURLToPath(value));
|
|
704
|
+
}
|
|
705
|
+
} catch {
|
|
706
|
+
return void 0;
|
|
707
|
+
}
|
|
708
|
+
if (value.startsWith("~")) {
|
|
709
|
+
return path6__default.default.normalize(path6__default.default.join(os__default.default.homedir(), value.slice(1)));
|
|
710
|
+
}
|
|
711
|
+
if (path6__default.default.isAbsolute(value)) {
|
|
712
|
+
return path6__default.default.normalize(value);
|
|
713
|
+
}
|
|
714
|
+
if (!rootDir) {
|
|
715
|
+
return void 0;
|
|
716
|
+
}
|
|
717
|
+
return path6__default.default.normalize(path6__default.default.resolve(rootDir, value));
|
|
718
|
+
}
|
|
719
|
+
function toReferencedResources(values, rootDir) {
|
|
720
|
+
const seen = /* @__PURE__ */ new Set();
|
|
721
|
+
const resources = [];
|
|
722
|
+
for (const value of values) {
|
|
723
|
+
for (const candidate of collectMatches(value, REMOTE_URL_PATTERN)) {
|
|
724
|
+
const normalized = trimTrailingPunctuation(candidate);
|
|
725
|
+
if (!normalized || seen.has(normalized)) {
|
|
726
|
+
continue;
|
|
727
|
+
}
|
|
728
|
+
seen.add(normalized);
|
|
729
|
+
resources.push({
|
|
730
|
+
kind: "remote-url",
|
|
731
|
+
location: normalized
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
for (const candidate of [...collectMatches(value, FILE_URL_PATTERN), ...collectMatches(value, LOCAL_PATH_WITH_PREFIX_PATTERN), ...collectMatches(value, RELATIVE_PATH_WITH_SEPARATOR_PATTERN)]) {
|
|
735
|
+
const resolved = resolveLocalResource(candidate, rootDir);
|
|
736
|
+
if (!resolved || seen.has(resolved)) {
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
seen.add(resolved);
|
|
740
|
+
resources.push({
|
|
741
|
+
kind: "local-file",
|
|
742
|
+
location: resolved
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
if (resources.length >= MAX_REFERENCED_RESOURCES) {
|
|
746
|
+
return resources.slice(0, MAX_REFERENCED_RESOURCES);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
return resources;
|
|
750
|
+
}
|
|
751
|
+
async function detectLocalMimeType(filePath) {
|
|
752
|
+
try {
|
|
753
|
+
const { stdout: stdout2 } = await execFileAsync("file", ["--brief", "--mime-type", filePath], {
|
|
754
|
+
maxBuffer: 64 * 1024,
|
|
755
|
+
timeout: COMMAND_TIMEOUT_MS
|
|
756
|
+
});
|
|
757
|
+
return stdout2.trim().toLowerCase();
|
|
758
|
+
} catch {
|
|
759
|
+
return "";
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
async function readTextFile(filePath) {
|
|
763
|
+
try {
|
|
764
|
+
const buffer = await fs2__default.default.readFile(filePath);
|
|
765
|
+
return limitTextContent(buffer.subarray(0, MAX_FILE_BUFFER).toString("utf8"));
|
|
766
|
+
} catch {
|
|
767
|
+
return "";
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
async function readTextViaTextutil(filePath) {
|
|
771
|
+
try {
|
|
772
|
+
const { stdout: stdout2 } = await execFileAsync("textutil", ["-convert", "txt", "-stdout", filePath], {
|
|
773
|
+
maxBuffer: MAX_FILE_BUFFER,
|
|
774
|
+
timeout: COMMAND_TIMEOUT_MS
|
|
775
|
+
});
|
|
776
|
+
return limitTextContent(stdout2);
|
|
777
|
+
} catch {
|
|
778
|
+
return "";
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
async function readTextViaMetadata(filePath) {
|
|
782
|
+
try {
|
|
783
|
+
const { stdout: stdout2 } = await execFileAsync("mdls", ["-raw", "-name", "kMDItemTextContent", filePath], {
|
|
784
|
+
maxBuffer: MAX_FILE_BUFFER,
|
|
785
|
+
timeout: COMMAND_TIMEOUT_MS
|
|
786
|
+
});
|
|
787
|
+
const normalized = stdout2.trim();
|
|
788
|
+
if (!normalized || normalized === "(null)") {
|
|
789
|
+
return "";
|
|
790
|
+
}
|
|
791
|
+
return limitTextContent(normalized);
|
|
792
|
+
} catch {
|
|
793
|
+
return "";
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
async function readTextViaStrings(filePath) {
|
|
797
|
+
try {
|
|
798
|
+
const { stdout: stdout2 } = await execFileAsync("strings", ["-n", "6", filePath], {
|
|
799
|
+
maxBuffer: MAX_FILE_BUFFER,
|
|
800
|
+
timeout: COMMAND_TIMEOUT_MS
|
|
801
|
+
});
|
|
802
|
+
return limitTextContent(stdout2);
|
|
803
|
+
} catch {
|
|
804
|
+
return "";
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
async function readTextViaTesseract(filePath) {
|
|
808
|
+
try {
|
|
809
|
+
const { stdout: stdout2 } = await execFileAsync("tesseract", [filePath, "stdout", "--psm", "6"], {
|
|
810
|
+
maxBuffer: MAX_FILE_BUFFER,
|
|
811
|
+
timeout: COMMAND_TIMEOUT_MS
|
|
812
|
+
});
|
|
813
|
+
return limitTextContent(stdout2);
|
|
814
|
+
} catch {
|
|
815
|
+
return "";
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
async function readLocalResourceText(filePath) {
|
|
819
|
+
try {
|
|
820
|
+
const stats = await fs2__default.default.stat(filePath);
|
|
821
|
+
if (!stats.isFile()) {
|
|
822
|
+
return "";
|
|
823
|
+
}
|
|
824
|
+
} catch {
|
|
825
|
+
return "";
|
|
826
|
+
}
|
|
827
|
+
const extension = path6__default.default.extname(filePath).toLowerCase();
|
|
828
|
+
const mimeType = await detectLocalMimeType(filePath);
|
|
829
|
+
if (IMAGE_EXTENSIONS.has(extension) || isLikelyImageMimeType(mimeType)) {
|
|
830
|
+
return await readTextViaTesseract(filePath) || await readTextViaMetadata(filePath);
|
|
831
|
+
}
|
|
832
|
+
if (TEXT_EXTENSIONS.has(extension) || isLikelyTextMimeType(mimeType)) {
|
|
833
|
+
return readTextFile(filePath);
|
|
834
|
+
}
|
|
835
|
+
if (TEXTUTIL_EXTENSIONS.has(extension)) {
|
|
836
|
+
return await readTextViaTextutil(filePath) || await readTextViaMetadata(filePath);
|
|
837
|
+
}
|
|
838
|
+
return await readTextViaMetadata(filePath) || await readTextViaStrings(filePath);
|
|
839
|
+
}
|
|
840
|
+
async function readRemoteBuffer(location) {
|
|
841
|
+
const controller = new AbortController();
|
|
842
|
+
const timer = setTimeout(() => controller.abort(), COMMAND_TIMEOUT_MS);
|
|
843
|
+
try {
|
|
844
|
+
const response = await fetch(location, {
|
|
845
|
+
redirect: "follow",
|
|
846
|
+
signal: controller.signal
|
|
847
|
+
});
|
|
848
|
+
if (!response.ok) {
|
|
849
|
+
return void 0;
|
|
850
|
+
}
|
|
851
|
+
const contentLength = Number(response.headers.get("content-length") || "0");
|
|
852
|
+
if (contentLength > MAX_REMOTE_BUFFER) {
|
|
853
|
+
return void 0;
|
|
854
|
+
}
|
|
855
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
856
|
+
const buffer = Buffer.from(arrayBuffer);
|
|
857
|
+
if (buffer.byteLength > MAX_REMOTE_BUFFER) {
|
|
858
|
+
return void 0;
|
|
859
|
+
}
|
|
860
|
+
return {
|
|
861
|
+
buffer,
|
|
862
|
+
contentType: (response.headers.get("content-type") || "").toLowerCase()
|
|
863
|
+
};
|
|
864
|
+
} catch {
|
|
865
|
+
return void 0;
|
|
866
|
+
} finally {
|
|
867
|
+
clearTimeout(timer);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
async function withTemporaryFile(extension, buffer, task) {
|
|
871
|
+
const tempPath = path6__default.default.join(os__default.default.tmpdir(), `ju-hong-e-security-${Date.now()}-${Math.random().toString(16).slice(2)}${extension}`);
|
|
872
|
+
try {
|
|
873
|
+
await fs2__default.default.writeFile(tempPath, buffer);
|
|
874
|
+
return await task(tempPath);
|
|
875
|
+
} finally {
|
|
876
|
+
await fs2__default.default.rm(tempPath, { force: true });
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
async function readRemoteResourceText(location) {
|
|
880
|
+
const response = await readRemoteBuffer(location);
|
|
881
|
+
if (!response) {
|
|
882
|
+
return "";
|
|
883
|
+
}
|
|
884
|
+
let extension = "";
|
|
885
|
+
try {
|
|
886
|
+
extension = path6__default.default.extname(new URL(location).pathname).toLowerCase();
|
|
887
|
+
} catch {
|
|
888
|
+
extension = "";
|
|
889
|
+
}
|
|
890
|
+
if (IMAGE_EXTENSIONS.has(extension) || isLikelyImageMimeType(response.contentType)) {
|
|
891
|
+
return withTemporaryFile(extension || ".img", response.buffer, readTextViaTesseract);
|
|
892
|
+
}
|
|
893
|
+
if (TEXT_EXTENSIONS.has(extension) || isLikelyTextMimeType(response.contentType)) {
|
|
894
|
+
return limitTextContent(response.buffer.toString("utf8"));
|
|
895
|
+
}
|
|
896
|
+
if (TEXTUTIL_EXTENSIONS.has(extension)) {
|
|
897
|
+
return withTemporaryFile(extension, response.buffer, async (filePath) => await readTextViaTextutil(filePath) || await readTextViaMetadata(filePath));
|
|
898
|
+
}
|
|
899
|
+
return "";
|
|
900
|
+
}
|
|
901
|
+
async function collectReferencedResourceContents(values, rootDir) {
|
|
902
|
+
const resources = toReferencedResources(values, rootDir);
|
|
903
|
+
const contents = [];
|
|
904
|
+
for (const resource of resources) {
|
|
905
|
+
const content = resource.kind === "local-file" ? await readLocalResourceText(resource.location) : await readRemoteResourceText(resource.location);
|
|
906
|
+
if (!content) {
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
909
|
+
contents.push({
|
|
910
|
+
location: resource.location,
|
|
911
|
+
value: content
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
return contents;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// src/common/security/registry.ts
|
|
918
|
+
var securityRegistryState = globalThis.__JUHONG_E_SECURITY_REGISTRY__ ?? (globalThis.__JUHONG_E_SECURITY_REGISTRY__ = {
|
|
919
|
+
patternRules: /* @__PURE__ */ new Map(),
|
|
920
|
+
validatorRules: /* @__PURE__ */ new Map()
|
|
921
|
+
});
|
|
922
|
+
var GLOBAL_PATTERN_RULES = securityRegistryState.patternRules;
|
|
923
|
+
var GLOBAL_VALIDATOR_RULES = securityRegistryState.validatorRules;
|
|
924
|
+
function resetRegex(pattern) {
|
|
925
|
+
pattern.lastIndex = 0;
|
|
926
|
+
return pattern;
|
|
927
|
+
}
|
|
928
|
+
function collectStringValues(value) {
|
|
929
|
+
if (typeof value === "string") {
|
|
930
|
+
return value.trim() ? [value] : [];
|
|
931
|
+
}
|
|
932
|
+
if (Array.isArray(value)) {
|
|
933
|
+
return value.flatMap((item) => collectStringValues(item));
|
|
934
|
+
}
|
|
935
|
+
if (value && typeof value === "object") {
|
|
936
|
+
return Object.values(value).flatMap((item) => collectStringValues(item));
|
|
937
|
+
}
|
|
938
|
+
return [];
|
|
939
|
+
}
|
|
940
|
+
function collectCandidateValues(value, source) {
|
|
941
|
+
return collectStringValues(value).map((entry) => ({
|
|
942
|
+
source,
|
|
943
|
+
value: entry
|
|
944
|
+
}));
|
|
945
|
+
}
|
|
946
|
+
async function collectSecurityStringValues(payload, options) {
|
|
947
|
+
const directValues = [
|
|
948
|
+
...collectCandidateValues(payload.input, "input"),
|
|
949
|
+
...collectCandidateValues(payload.rawCommand, "rawCommand"),
|
|
950
|
+
...collectCandidateValues(payload.rawArgs, "rawArgs"),
|
|
951
|
+
...collectCandidateValues(payload.args, "args"),
|
|
952
|
+
...options.inspectPrompt ? collectCandidateValues(payload.prompt, "prompt") : []
|
|
953
|
+
];
|
|
954
|
+
if (!options.inspectResources) {
|
|
955
|
+
return directValues;
|
|
956
|
+
}
|
|
957
|
+
const referencedContents = await collectReferencedResourceContents(
|
|
958
|
+
directValues.map((entry) => entry.value),
|
|
959
|
+
payload.promptRootDir
|
|
960
|
+
);
|
|
961
|
+
return [
|
|
962
|
+
...directValues,
|
|
963
|
+
...referencedContents.map(
|
|
964
|
+
(entry) => ({
|
|
965
|
+
location: entry.location,
|
|
966
|
+
source: "resource",
|
|
967
|
+
value: entry.value
|
|
968
|
+
})
|
|
969
|
+
)
|
|
970
|
+
];
|
|
971
|
+
}
|
|
972
|
+
function getPatternRules(config) {
|
|
973
|
+
return [...GLOBAL_PATTERN_RULES.values(), ...config?.patterns || []];
|
|
974
|
+
}
|
|
975
|
+
function getValidatorRules(config) {
|
|
976
|
+
return [...GLOBAL_VALIDATOR_RULES.values(), ...config?.validators || []];
|
|
977
|
+
}
|
|
978
|
+
function addGlobalSecurityPattern(rule) {
|
|
979
|
+
GLOBAL_PATTERN_RULES.set(rule.name, rule);
|
|
980
|
+
}
|
|
981
|
+
async function validateSecurityPayload(payload, config, options = {
|
|
982
|
+
inspectPrompt: true,
|
|
983
|
+
inspectResources: true,
|
|
984
|
+
mode: "prompt-with-resources"
|
|
985
|
+
}) {
|
|
986
|
+
const stringValues = await collectSecurityStringValues(payload, options);
|
|
987
|
+
for (const rule of getPatternRules(config)) {
|
|
988
|
+
const matched = findMatchedCandidate(stringValues, rule);
|
|
989
|
+
if (matched) {
|
|
990
|
+
return {
|
|
991
|
+
detail: {
|
|
992
|
+
location: matched.location,
|
|
993
|
+
matchedText: matched.matchedText,
|
|
994
|
+
source: matched.source
|
|
995
|
+
},
|
|
996
|
+
message: rule.message,
|
|
997
|
+
ok: false,
|
|
998
|
+
ruleName: rule.name
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
for (const rule of getValidatorRules(config)) {
|
|
1003
|
+
const result = await rule.validate(payload);
|
|
1004
|
+
if (result === true) {
|
|
1005
|
+
continue;
|
|
1006
|
+
}
|
|
1007
|
+
return {
|
|
1008
|
+
detail: {
|
|
1009
|
+
source: payload.prompt ? "prompt" : payload.input ? "input" : "args"
|
|
1010
|
+
},
|
|
1011
|
+
message: typeof result === "string" && result.trim() ? result : rule.message,
|
|
1012
|
+
ok: false,
|
|
1013
|
+
ruleName: rule.name
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
return {
|
|
1017
|
+
ok: true
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
function findMatchedCandidate(stringValues, rule) {
|
|
1021
|
+
for (const candidate of stringValues) {
|
|
1022
|
+
const matchedText = extractMatchedText(candidate.value, rule);
|
|
1023
|
+
if (!matchedText) {
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
return {
|
|
1027
|
+
location: candidate.location,
|
|
1028
|
+
matchedText,
|
|
1029
|
+
source: candidate.source
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
return void 0;
|
|
1033
|
+
}
|
|
1034
|
+
function extractMatchedText(value, rule) {
|
|
1035
|
+
const match = resetRegex(rule.pattern).exec(value);
|
|
1036
|
+
if (!match?.[0]) {
|
|
1037
|
+
return "";
|
|
1038
|
+
}
|
|
1039
|
+
const formattedMatch = rule.formatMatch ? rule.formatMatch(match[0]) : match[0];
|
|
1040
|
+
return safeExcerpt(formattedMatch, 120);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
// src/common/security/default-rules.ts
|
|
1044
|
+
var initialized = false;
|
|
1045
|
+
function formatResidentRegistrationNumber(matchedText) {
|
|
1046
|
+
const digits = matchedText.replace(/\D/g, "");
|
|
1047
|
+
if (digits.length !== 13) {
|
|
1048
|
+
return `${matchedText.slice(0, 3)}***`;
|
|
1049
|
+
}
|
|
1050
|
+
return `${digits.slice(0, 6)}-${digits[6]}******`;
|
|
1051
|
+
}
|
|
1052
|
+
function ensureDefaultSecurityRules() {
|
|
1053
|
+
if (initialized) {
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
initialized = true;
|
|
1057
|
+
addGlobalSecurityPattern({
|
|
1058
|
+
formatMatch: formatResidentRegistrationNumber,
|
|
1059
|
+
message: "\uBCF4\uC548 \uC815\uCC45\uC5D0 \uC758\uD574 \uC8FC\uBBFC\uB4F1\uB85D\uBC88\uD638 \uD615\uD0DC\uC758 \uAC12\uC774 \uD3EC\uD568\uB41C \uC785\uB825\uC740 \uC2E4\uD589\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
1060
|
+
name: "resident-registration-number",
|
|
1061
|
+
pattern: /\b\d{6}[- ]?[1-4]\d{6}\b/
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
var DEFAULT_SECURITY_POLICY_MODE = "prompt-with-resources";
|
|
1065
|
+
var SECURITY_POLICY_OPTIONS = [
|
|
1066
|
+
{
|
|
1067
|
+
description: "\uBCF4\uC548 \uAC80\uC0AC\uB97C \uC644\uC804\uD788 \uBE44\uD65C\uC131\uD654\uD569\uB2C8\uB2E4. \uBBFC\uAC10\uC815\uBCF4\uAC00 \uD3EC\uD568\uB418\uC5B4\uB3C4 \uCC28\uB2E8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
1068
|
+
label: "\uBCF4\uC548 \uAC80\uC0AC \uC0AC\uC6A9 \uC548 \uD568",
|
|
1069
|
+
mode: "disabled",
|
|
1070
|
+
summary: "\uBCF4\uC548 \uAC80\uC0AC \uBE44\uD65C\uC131\uD654"
|
|
1071
|
+
},
|
|
1072
|
+
{
|
|
1073
|
+
description: "\uC0AC\uC6A9\uC790 \uC785\uB825\uACFC \uBA85\uB839 \uC778\uC790\uB9CC \uAC80\uC0AC\uD569\uB2C8\uB2E4. \uD504\uB86C\uD504\uD2B8 \uBCF8\uBB38\uACFC \uCC38\uC870 \uB9AC\uC18C\uC2A4\uB294 \uC77D\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
1074
|
+
label: "\uC785\uB825/\uBA85\uB839\uB9CC \uAC80\uC0AC",
|
|
1075
|
+
mode: "input-only",
|
|
1076
|
+
summary: "\uC785\uB825/\uBA85\uB839 \uBB38\uC790\uC5F4\uB9CC \uAC80\uC0AC"
|
|
1077
|
+
},
|
|
1078
|
+
{
|
|
1079
|
+
description: "\uC785\uB825\uACFC AI \uD504\uB86C\uD504\uD2B8 \uBB38\uC790\uC5F4\uAE4C\uC9C0 \uAC80\uC0AC\uD569\uB2C8\uB2E4. \uACBD\uB85C/URL\uC774 \uAC00\uB9AC\uD0A4\uB294 \uB0B4\uC6A9\uC740 \uC77D\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
1080
|
+
label: "\uD504\uB86C\uD504\uD2B8 \uD14D\uC2A4\uD2B8\uAE4C\uC9C0 \uAC80\uC0AC",
|
|
1081
|
+
mode: "prompt-only",
|
|
1082
|
+
summary: "\uD504\uB86C\uD504\uD2B8 \uBB38\uC790\uC5F4\uAE4C\uC9C0 \uAC80\uC0AC"
|
|
1083
|
+
},
|
|
1084
|
+
{
|
|
1085
|
+
description: "\uC785\uB825, \uD504\uB86C\uD504\uD2B8, \uACBD\uB85C/URL\uC774 \uAC00\uB9AC\uD0A4\uB294 \uCC38\uC870 \uB9AC\uC18C\uC2A4 \uB0B4\uC6A9\uAE4C\uC9C0 \uD568\uAED8 \uAC80\uC0AC\uD569\uB2C8\uB2E4.",
|
|
1086
|
+
label: "\uD504\uB86C\uD504\uD2B8\uC640 \uCC38\uC870 \uB9AC\uC18C\uC2A4\uAE4C\uC9C0 \uC2EC\uCE35 \uAC80\uC0AC",
|
|
1087
|
+
mode: "prompt-with-resources",
|
|
1088
|
+
summary: "\uD504\uB86C\uD504\uD2B8\uC640 \uCC38\uC870 \uB9AC\uC18C\uC2A4\uAE4C\uC9C0 \uC2EC\uCE35 \uAC80\uC0AC"
|
|
1089
|
+
}
|
|
1090
|
+
];
|
|
1091
|
+
function createDefaultSecurityPolicy() {
|
|
1092
|
+
return {
|
|
1093
|
+
mode: DEFAULT_SECURITY_POLICY_MODE
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
function isSecurityPolicyMode(value) {
|
|
1097
|
+
return SECURITY_POLICY_OPTIONS.some((option) => option.mode === value);
|
|
1098
|
+
}
|
|
1099
|
+
function readSecurityPolicy(paths) {
|
|
1100
|
+
if (!paths?.securityPolicyPath || !fs7__default.default.existsSync(paths.securityPolicyPath)) {
|
|
1101
|
+
return createDefaultSecurityPolicy();
|
|
1102
|
+
}
|
|
1103
|
+
try {
|
|
1104
|
+
const parsed = JSON.parse(fs7__default.default.readFileSync(paths.securityPolicyPath, "utf8"));
|
|
1105
|
+
if (!isSecurityPolicyMode(parsed.mode)) {
|
|
1106
|
+
return createDefaultSecurityPolicy();
|
|
1107
|
+
}
|
|
1108
|
+
return {
|
|
1109
|
+
mode: parsed.mode
|
|
1110
|
+
};
|
|
1111
|
+
} catch {
|
|
1112
|
+
return createDefaultSecurityPolicy();
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
function getSecurityPolicyOption(mode) {
|
|
1116
|
+
return SECURITY_POLICY_OPTIONS.find((option) => option.mode === mode) || SECURITY_POLICY_OPTIONS.at(-1);
|
|
1117
|
+
}
|
|
1118
|
+
function describeSecurityPolicyMode(mode) {
|
|
1119
|
+
return getSecurityPolicyOption(mode).label;
|
|
1120
|
+
}
|
|
1121
|
+
function isSecurityPolicyEnabled(mode) {
|
|
1122
|
+
return mode !== "disabled";
|
|
1123
|
+
}
|
|
1124
|
+
function resolveSecurityValidationOptions(mode) {
|
|
1125
|
+
switch (mode) {
|
|
1126
|
+
case "disabled":
|
|
1127
|
+
return {
|
|
1128
|
+
inspectPrompt: false,
|
|
1129
|
+
inspectResources: false,
|
|
1130
|
+
mode
|
|
1131
|
+
};
|
|
1132
|
+
case "input-only":
|
|
1133
|
+
return {
|
|
1134
|
+
inspectPrompt: false,
|
|
1135
|
+
inspectResources: false,
|
|
1136
|
+
mode
|
|
1137
|
+
};
|
|
1138
|
+
case "prompt-only":
|
|
1139
|
+
return {
|
|
1140
|
+
inspectPrompt: true,
|
|
1141
|
+
inspectResources: false,
|
|
1142
|
+
mode
|
|
1143
|
+
};
|
|
1144
|
+
case "prompt-with-resources":
|
|
1145
|
+
default:
|
|
1146
|
+
return {
|
|
1147
|
+
inspectPrompt: true,
|
|
1148
|
+
inspectResources: true,
|
|
1149
|
+
mode
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
// src/common/security/guard.ts
|
|
1155
|
+
function toSecuritySourceLabel(source, location) {
|
|
1156
|
+
switch (source) {
|
|
1157
|
+
case "input":
|
|
1158
|
+
return "\uC0AC\uC6A9\uC790 \uC785\uB825";
|
|
1159
|
+
case "rawCommand":
|
|
1160
|
+
return "\uC6D0\uBCF8 \uBA85\uB839\uC5B4";
|
|
1161
|
+
case "rawArgs":
|
|
1162
|
+
return "\uC6D0\uBCF8 \uBA85\uB839 \uC778\uC790";
|
|
1163
|
+
case "args":
|
|
1164
|
+
return "\uBA85\uB839 \uC778\uC790";
|
|
1165
|
+
case "prompt":
|
|
1166
|
+
return "AI \uD504\uB86C\uD504\uD2B8";
|
|
1167
|
+
case "resource":
|
|
1168
|
+
return location ? `\uCC38\uC870 \uB9AC\uC18C\uC2A4 (${location})` : "\uCC38\uC870 \uB9AC\uC18C\uC2A4";
|
|
1169
|
+
default:
|
|
1170
|
+
return source;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
function buildSecurityBlockSections(result, policyModeLabel) {
|
|
1174
|
+
const detailLines = [`- policyMode: ${policyModeLabel}`, `- rule: \`${result.ruleName}\``, `- message: ${result.message}`];
|
|
1175
|
+
if (result.detail) {
|
|
1176
|
+
detailLines.push(`- source: ${toSecuritySourceLabel(result.detail.source, result.detail.location)}`);
|
|
1177
|
+
if (result.detail.matchedText) {
|
|
1178
|
+
detailLines.push(`- matched: \`${result.detail.matchedText}\``);
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
return [
|
|
1182
|
+
{
|
|
1183
|
+
heading: "Security Block",
|
|
1184
|
+
markdown: detailLines.join("\n")
|
|
1185
|
+
}
|
|
1186
|
+
];
|
|
1187
|
+
}
|
|
1188
|
+
function buildSecurityBlockUserMessage(result, policyModeLabel, errorReportPath) {
|
|
1189
|
+
const lines = ["\uBCF4\uC548 \uC815\uCC45\uC5D0 \uC758\uD574 \uC694\uCCAD\uC744 \uC911\uB2E8\uD588\uC2B5\uB2C8\uB2E4.", result.message, `- \uD604\uC7AC \uC815\uCC45: ${policyModeLabel}`, `- \uBCF4\uC548 \uADDC\uCE59: \`${result.ruleName}\``];
|
|
1190
|
+
if (result.detail) {
|
|
1191
|
+
lines.push(`- \uAC10\uC9C0 \uC704\uCE58: ${toSecuritySourceLabel(result.detail.source, result.detail.location)}`);
|
|
1192
|
+
if (result.detail.matchedText) {
|
|
1193
|
+
lines.push(`- \uAC10\uC9C0 \uB0B4\uC6A9: \`${result.detail.matchedText}\``);
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
if (errorReportPath) {
|
|
1197
|
+
lines.push(`- \uC5D0\uB7EC \uB9AC\uD3EC\uD2B8: ${errorReportPath}`);
|
|
1198
|
+
}
|
|
1199
|
+
return lines.join("\n");
|
|
1200
|
+
}
|
|
1201
|
+
async function guardSecurityPayload(options) {
|
|
1202
|
+
ensureDefaultSecurityRules();
|
|
1203
|
+
const policy = readSecurityPolicy(options.paths);
|
|
1204
|
+
const policyModeLabel = describeSecurityPolicyMode(policy.mode);
|
|
1205
|
+
if (!isSecurityPolicyEnabled(policy.mode)) {
|
|
1206
|
+
options.trace("security:skip", `${options.traceLabel} | ${policy.mode}`);
|
|
1207
|
+
return {
|
|
1208
|
+
ok: true
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
const validationOptions = resolveSecurityValidationOptions(policy.mode);
|
|
1212
|
+
const result = await validateSecurityPayload(options.payload, options.config, validationOptions);
|
|
1213
|
+
if (!result.ok) {
|
|
1214
|
+
options.trace("security:block", `${options.traceLabel} | ${result.ruleName}`);
|
|
1215
|
+
const errorReportPath = options.paths ? writeErrorReport(
|
|
1216
|
+
options.paths,
|
|
1217
|
+
{
|
|
1218
|
+
detail: result.detail,
|
|
1219
|
+
message: result.message,
|
|
1220
|
+
name: "SecurityPolicyBlockedError",
|
|
1221
|
+
payload: {
|
|
1222
|
+
command: options.payload.command,
|
|
1223
|
+
requestedService: options.payload.requestedService,
|
|
1224
|
+
service: options.payload.service
|
|
1225
|
+
},
|
|
1226
|
+
ruleName: result.ruleName
|
|
1227
|
+
},
|
|
1228
|
+
{
|
|
1229
|
+
extraSections: buildSecurityBlockSections(result, policyModeLabel),
|
|
1230
|
+
scope: options.reportScope || `security:${options.traceLabel}`
|
|
1231
|
+
}
|
|
1232
|
+
) : void 0;
|
|
1233
|
+
const userMessage = buildSecurityBlockUserMessage(result, policyModeLabel, errorReportPath);
|
|
1234
|
+
console.error(userMessage);
|
|
1235
|
+
return {
|
|
1236
|
+
...result,
|
|
1237
|
+
errorReportPath,
|
|
1238
|
+
userMessage
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
1241
|
+
return result;
|
|
1242
|
+
}
|
|
1243
|
+
var POSTHOG_API_KEY = "phc_MX929Nca94SLcOo7b5Mdvhv9KQi7lSESFNKRhAJRKi9";
|
|
1244
|
+
var POSTHOG_HOST = "https://us.i.posthog.com";
|
|
1245
|
+
var POSTHOG_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
1246
|
+
var telemetryRuntimeState = globalThis.__JU_HONG_E_POSTHOG_RUNTIME_STATE__ ?? (globalThis.__JU_HONG_E_POSTHOG_RUNTIME_STATE__ = {});
|
|
1247
|
+
function getPostHogClient() {
|
|
1248
|
+
if (!telemetryRuntimeState.posthogClient) {
|
|
1249
|
+
telemetryRuntimeState.posthogClient = new posthogNode.PostHog(POSTHOG_API_KEY, {
|
|
1250
|
+
host: POSTHOG_HOST
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1253
|
+
return telemetryRuntimeState.posthogClient;
|
|
1254
|
+
}
|
|
1255
|
+
function readGitConfigValue(rootDir, configKey) {
|
|
1256
|
+
if (!isGitRepository(rootDir)) {
|
|
1257
|
+
return null;
|
|
1258
|
+
}
|
|
1259
|
+
try {
|
|
1260
|
+
return runGitCommand(rootDir, `git config --get ${configKey}`) || null;
|
|
1261
|
+
} catch {
|
|
1262
|
+
return null;
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
function resolveDistinctId(gitUserName, gitUserEmail) {
|
|
1266
|
+
if (gitUserEmail) {
|
|
1267
|
+
return `git-email:${gitUserEmail}`;
|
|
1268
|
+
}
|
|
1269
|
+
if (gitUserName) {
|
|
1270
|
+
return `git-name:${gitUserName}`;
|
|
1271
|
+
}
|
|
1272
|
+
try {
|
|
1273
|
+
const currentUser = os.userInfo();
|
|
1274
|
+
return `os-user:${currentUser.username}@${os.hostname()}`;
|
|
1275
|
+
} catch {
|
|
1276
|
+
return `host:${os.hostname()}`;
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
function resolveGitTelemetryMetadata(rootDir) {
|
|
1280
|
+
const gitUserName = readGitConfigValue(rootDir, "user.name");
|
|
1281
|
+
const gitUserEmail = readGitConfigValue(rootDir, "user.email");
|
|
1282
|
+
return {
|
|
1283
|
+
distinctId: resolveDistinctId(gitUserName, gitUserEmail),
|
|
1284
|
+
gitUserEmail,
|
|
1285
|
+
gitUserName
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1288
|
+
function resolveUserIpAddressValue() {
|
|
1289
|
+
const ipAddresses = /* @__PURE__ */ new Set();
|
|
1290
|
+
Object.values(os.networkInterfaces()).forEach((records) => {
|
|
1291
|
+
records?.forEach((record) => {
|
|
1292
|
+
if (!record || record.internal) {
|
|
1293
|
+
return;
|
|
1294
|
+
}
|
|
1295
|
+
ipAddresses.add(record.address);
|
|
1296
|
+
});
|
|
1297
|
+
});
|
|
1298
|
+
return [...ipAddresses].join(", ");
|
|
1299
|
+
}
|
|
1300
|
+
function buildCommandParameterProperties(args) {
|
|
1301
|
+
const parameterProperties = {};
|
|
1302
|
+
if (args.length === 0) {
|
|
1303
|
+
parameterProperties["\uBA85\uB839\uC5B4\uC5D0\uC0AC\uC6A9\uB41C\uD30C\uB77C\uBBF8\uD1301"] = null;
|
|
1304
|
+
return parameterProperties;
|
|
1305
|
+
}
|
|
1306
|
+
args.forEach((arg, index) => {
|
|
1307
|
+
parameterProperties[`\uBA85\uB839\uC5B4\uC5D0\uC0AC\uC6A9\uB41C\uD30C\uB77C\uBBF8\uD130${index + 1}`] = arg;
|
|
1308
|
+
});
|
|
1309
|
+
return parameterProperties;
|
|
1310
|
+
}
|
|
1311
|
+
function beginSlashCommandTelemetryInputCollection(initialArgs) {
|
|
1312
|
+
telemetryRuntimeState.slashCommandTelemetryInputValues = [...initialArgs];
|
|
1313
|
+
}
|
|
1314
|
+
function appendSlashCommandTelemetryInputValue(value) {
|
|
1315
|
+
if (!telemetryRuntimeState.slashCommandTelemetryInputValues) {
|
|
1316
|
+
return;
|
|
1317
|
+
}
|
|
1318
|
+
telemetryRuntimeState.slashCommandTelemetryInputValues.push(...normalizeSlashCommandTelemetryValues(value));
|
|
1319
|
+
}
|
|
1320
|
+
function endSlashCommandTelemetryInputCollection() {
|
|
1321
|
+
const values = [...telemetryRuntimeState.slashCommandTelemetryInputValues || []];
|
|
1322
|
+
telemetryRuntimeState.slashCommandTelemetryInputValues = void 0;
|
|
1323
|
+
return values;
|
|
1324
|
+
}
|
|
1325
|
+
function normalizeSlashCommandTelemetryValues(value) {
|
|
1326
|
+
if (value === void 0) {
|
|
1327
|
+
return [];
|
|
1328
|
+
}
|
|
1329
|
+
if (value === null) {
|
|
1330
|
+
return [null];
|
|
1331
|
+
}
|
|
1332
|
+
if (Array.isArray(value)) {
|
|
1333
|
+
return value.flatMap((item) => normalizeSlashCommandTelemetryValues(item));
|
|
1334
|
+
}
|
|
1335
|
+
if (typeof value === "string") {
|
|
1336
|
+
return [value];
|
|
1337
|
+
}
|
|
1338
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
1339
|
+
return [String(value)];
|
|
1340
|
+
}
|
|
1341
|
+
if (typeof value === "object") {
|
|
1342
|
+
const objectValue = value;
|
|
1343
|
+
if (typeof objectValue.url === "string" && objectValue.url) {
|
|
1344
|
+
return [objectValue.url];
|
|
1345
|
+
}
|
|
1346
|
+
if (typeof objectValue.hash === "string" && objectValue.hash) {
|
|
1347
|
+
return [objectValue.hash];
|
|
1348
|
+
}
|
|
1349
|
+
if (typeof objectValue.name === "string" && objectValue.name) {
|
|
1350
|
+
return [objectValue.name];
|
|
1351
|
+
}
|
|
1352
|
+
if (typeof objectValue.label === "string" && objectValue.label) {
|
|
1353
|
+
return [objectValue.label];
|
|
1354
|
+
}
|
|
1355
|
+
try {
|
|
1356
|
+
return [JSON.stringify(objectValue)];
|
|
1357
|
+
} catch {
|
|
1358
|
+
return ["[object]"];
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
return [String(value)];
|
|
1362
|
+
}
|
|
1363
|
+
function trackSlashCommandExecution(options) {
|
|
1364
|
+
try {
|
|
1365
|
+
const gitMetadata = resolveGitTelemetryMetadata(options.rootDir);
|
|
1366
|
+
getPostHogClient().capture({
|
|
1367
|
+
distinctId: gitMetadata.distinctId,
|
|
1368
|
+
event: options.command,
|
|
1369
|
+
properties: {
|
|
1370
|
+
"userinfo-git-user-email": gitMetadata.gitUserEmail,
|
|
1371
|
+
"userinfo-git-user-name": gitMetadata.gitUserName,
|
|
1372
|
+
"userinfo-ip": resolveUserIpAddressValue(),
|
|
1373
|
+
"command-kind": options.kind,
|
|
1374
|
+
"command-service": options.service,
|
|
1375
|
+
"command-status": options.status,
|
|
1376
|
+
"requested-service": options.requestedService ?? null,
|
|
1377
|
+
...buildCommandParameterProperties(options.args)
|
|
1378
|
+
}
|
|
1379
|
+
});
|
|
1380
|
+
options.trace?.("posthog:slash-command:capture", `${options.command} status=${options.status}`);
|
|
1381
|
+
} catch (error) {
|
|
1382
|
+
options.trace?.("posthog:slash-command:skip", getErrorSummary(error));
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
async function shutdownPostHogClient() {
|
|
1386
|
+
if (!telemetryRuntimeState.posthogClient) {
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
if (!telemetryRuntimeState.shutdownPromise) {
|
|
1390
|
+
const client = telemetryRuntimeState.posthogClient;
|
|
1391
|
+
telemetryRuntimeState.shutdownPromise = Promise.resolve(client.shutdown(POSTHOG_SHUTDOWN_TIMEOUT_MS)).catch(() => void 0).finally(() => {
|
|
1392
|
+
telemetryRuntimeState.posthogClient = void 0;
|
|
1393
|
+
telemetryRuntimeState.shutdownPromise = void 0;
|
|
1394
|
+
});
|
|
1395
|
+
}
|
|
1396
|
+
await telemetryRuntimeState.shutdownPromise;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
// src/common/core/cancellation.ts
|
|
1400
|
+
var USER_CANCELLED_ERROR_CODE = "JUHONG_E_USER_CANCELLED";
|
|
1401
|
+
var USER_TERMINATED_ERROR_CODE = "JUHONG_E_USER_TERMINATED";
|
|
1402
|
+
function createUserCancelledError(message = "\uC0AC\uC6A9\uC790\uAC00 \uD604\uC7AC \uC791\uC5C5\uC744 \uCDE8\uC18C\uD588\uC2B5\uB2C8\uB2E4.") {
|
|
1403
|
+
const error = new Error(message);
|
|
1404
|
+
error.name = "UserCancelledError";
|
|
1405
|
+
error.code = USER_CANCELLED_ERROR_CODE;
|
|
1406
|
+
return error;
|
|
1407
|
+
}
|
|
1408
|
+
function isUserCancelledError(error) {
|
|
1409
|
+
return Boolean(
|
|
1410
|
+
error && typeof error === "object" && "code" in error && error.code === USER_CANCELLED_ERROR_CODE
|
|
1411
|
+
);
|
|
1412
|
+
}
|
|
1413
|
+
function createUserTerminatedError(message = "\uC0AC\uC6A9\uC790\uAC00 \uC138\uC158 \uC885\uB8CC\uB97C \uC694\uCCAD\uD588\uC2B5\uB2C8\uB2E4.") {
|
|
1414
|
+
const error = new Error(message);
|
|
1415
|
+
error.name = "UserTerminatedError";
|
|
1416
|
+
error.code = USER_TERMINATED_ERROR_CODE;
|
|
1417
|
+
return error;
|
|
1418
|
+
}
|
|
1419
|
+
function isUserTerminatedError(error) {
|
|
1420
|
+
return Boolean(
|
|
1421
|
+
error && typeof error === "object" && "code" in error && error.code === USER_TERMINATED_ERROR_CODE
|
|
1422
|
+
);
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
// src/common/ui/index.ts
|
|
1426
|
+
var ANSI = {
|
|
1427
|
+
blue: "\x1B[34m",
|
|
1428
|
+
bold: "\x1B[1m",
|
|
1429
|
+
cyan: "\x1B[36m",
|
|
1430
|
+
dim: "\x1B[2m",
|
|
1431
|
+
gray: "\x1B[90m",
|
|
1432
|
+
green: "\x1B[32m",
|
|
1433
|
+
red: "\x1B[31m",
|
|
1434
|
+
reset: "\x1B[0m",
|
|
1435
|
+
yellow: "\x1B[33m"
|
|
1436
|
+
};
|
|
1437
|
+
var ANSI_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
1438
|
+
var COMBINING_MARK_PATTERN = /\p{Mark}/u;
|
|
1439
|
+
var GRAPHEME_SEGMENTER = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter("ko", { granularity: "grapheme" }) : null;
|
|
1440
|
+
var STATUS_FRAMES = ["-", "\\", "|", "/"];
|
|
1441
|
+
function colorize(text, color) {
|
|
1442
|
+
return `${color}${text}${ANSI.reset}`;
|
|
1443
|
+
}
|
|
1444
|
+
function formatStatusPrefix(title) {
|
|
1445
|
+
return `${colorize(`[${title}]`, `${ANSI.bold}${ANSI.cyan}`)}`;
|
|
1446
|
+
}
|
|
1447
|
+
function formatElapsed(startedAt) {
|
|
1448
|
+
return `${Math.max(0, Math.floor((Date.now() - startedAt) / 1e3))}s`;
|
|
1449
|
+
}
|
|
1450
|
+
function buildStatusRenderLayout(prefix, body) {
|
|
1451
|
+
return buildPromptRenderLayout(`${prefix} `, body);
|
|
1452
|
+
}
|
|
1453
|
+
function clearRenderedStatus(previousState) {
|
|
1454
|
+
if (previousState?.totalLines && previousState.totalLines > 1) {
|
|
1455
|
+
readline__default.default.moveCursor(process$1.stdout, 0, -(previousState.totalLines - 1));
|
|
1456
|
+
}
|
|
1457
|
+
readline__default.default.cursorTo(process$1.stdout, 0);
|
|
1458
|
+
readline__default.default.clearScreenDown(process$1.stdout);
|
|
1459
|
+
}
|
|
1460
|
+
function renderStatusLayout(layout, previousState) {
|
|
1461
|
+
clearRenderedStatus(previousState);
|
|
1462
|
+
process$1.stdout.write(layout.lines.join("\n"));
|
|
1463
|
+
return {
|
|
1464
|
+
totalLines: layout.totalLines
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1467
|
+
function createSessionActionStatus(title) {
|
|
1468
|
+
const prefix = formatStatusPrefix(title);
|
|
1469
|
+
const print = (message) => {
|
|
1470
|
+
console.log(`${prefix} ${message}`);
|
|
1471
|
+
};
|
|
1472
|
+
return {
|
|
1473
|
+
fail(message) {
|
|
1474
|
+
print(colorize(message, ANSI.red));
|
|
1475
|
+
},
|
|
1476
|
+
step(message) {
|
|
1477
|
+
print(message);
|
|
1478
|
+
},
|
|
1479
|
+
async track(message, task) {
|
|
1480
|
+
if (!process$1.stdout.isTTY) {
|
|
1481
|
+
print(`${message}...`);
|
|
1482
|
+
return task();
|
|
1483
|
+
}
|
|
1484
|
+
const startedAt = Date.now();
|
|
1485
|
+
let frameIndex = 0;
|
|
1486
|
+
let renderState;
|
|
1487
|
+
const render = () => {
|
|
1488
|
+
const frame = STATUS_FRAMES[frameIndex % STATUS_FRAMES.length];
|
|
1489
|
+
frameIndex += 1;
|
|
1490
|
+
renderState = renderStatusLayout(buildStatusRenderLayout(`${prefix} ${frame}`, `${message} (${formatElapsed(startedAt)})`), renderState);
|
|
1491
|
+
};
|
|
1492
|
+
render();
|
|
1493
|
+
const timer = setInterval(render, 120);
|
|
1494
|
+
try {
|
|
1495
|
+
const result = await task();
|
|
1496
|
+
clearInterval(timer);
|
|
1497
|
+
renderState = renderStatusLayout(
|
|
1498
|
+
buildStatusRenderLayout(`${prefix} done`, `${message} (${formatElapsed(startedAt)})`),
|
|
1499
|
+
renderState
|
|
1500
|
+
);
|
|
1501
|
+
process$1.stdout.write("\n");
|
|
1502
|
+
return result;
|
|
1503
|
+
} catch (error) {
|
|
1504
|
+
clearInterval(timer);
|
|
1505
|
+
renderState = renderStatusLayout(
|
|
1506
|
+
buildStatusRenderLayout(`${prefix} fail`, `${message} (${formatElapsed(startedAt)})`),
|
|
1507
|
+
renderState
|
|
1508
|
+
);
|
|
1509
|
+
process$1.stdout.write("\n");
|
|
1510
|
+
throw error;
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
function segmentGraphemes(value) {
|
|
1516
|
+
if (!GRAPHEME_SEGMENTER) {
|
|
1517
|
+
return [...value];
|
|
1518
|
+
}
|
|
1519
|
+
return [...GRAPHEME_SEGMENTER.segment(value)].map(({ segment }) => segment);
|
|
1520
|
+
}
|
|
1521
|
+
function getGraphemeWidth(grapheme) {
|
|
1522
|
+
let width = 0;
|
|
1523
|
+
for (const character of grapheme) {
|
|
1524
|
+
const codePoint = character.codePointAt(0);
|
|
1525
|
+
if (!codePoint || COMBINING_MARK_PATTERN.test(character) || codePoint === 8205) {
|
|
1526
|
+
continue;
|
|
1527
|
+
}
|
|
1528
|
+
if (codePoint >= 65024 && codePoint <= 65039 || codePoint >= 917760 && codePoint <= 917999) {
|
|
1529
|
+
continue;
|
|
1530
|
+
}
|
|
1531
|
+
if (isWideCodePoint(codePoint) || isEmojiCodePoint(codePoint)) {
|
|
1532
|
+
width = Math.max(width, 2);
|
|
1533
|
+
continue;
|
|
1534
|
+
}
|
|
1535
|
+
width = Math.max(width, 1);
|
|
1536
|
+
}
|
|
1537
|
+
return width;
|
|
1538
|
+
}
|
|
1539
|
+
function tokenizeVisibleText(value) {
|
|
1540
|
+
const tokens = [];
|
|
1541
|
+
let lastIndex = 0;
|
|
1542
|
+
for (const match of value.matchAll(ANSI_PATTERN)) {
|
|
1543
|
+
const index = match.index ?? 0;
|
|
1544
|
+
if (index > lastIndex) {
|
|
1545
|
+
tokens.push(...tokenizePlainText(value.slice(lastIndex, index)));
|
|
1546
|
+
}
|
|
1547
|
+
tokens.push({
|
|
1548
|
+
value: match[0],
|
|
1549
|
+
visibleWidth: 0
|
|
1550
|
+
});
|
|
1551
|
+
lastIndex = index + match[0].length;
|
|
1552
|
+
}
|
|
1553
|
+
if (lastIndex < value.length) {
|
|
1554
|
+
tokens.push(...tokenizePlainText(value.slice(lastIndex)));
|
|
1555
|
+
}
|
|
1556
|
+
return tokens;
|
|
1557
|
+
}
|
|
1558
|
+
function tokenizePlainText(value) {
|
|
1559
|
+
return segmentGraphemes(value).map((segment) => ({
|
|
1560
|
+
value: segment,
|
|
1561
|
+
visibleWidth: getGraphemeWidth(segment)
|
|
1562
|
+
}));
|
|
1563
|
+
}
|
|
1564
|
+
function isWideCodePoint(codePoint) {
|
|
1565
|
+
return codePoint >= 4352 && (codePoint <= 4447 || codePoint === 9001 || codePoint === 9002 || codePoint >= 11904 && codePoint <= 12871 && codePoint !== 12351 || codePoint >= 12880 && codePoint <= 19903 || codePoint >= 19968 && codePoint <= 42182 || codePoint >= 43360 && codePoint <= 43388 || codePoint >= 44032 && codePoint <= 55203 || codePoint >= 63744 && codePoint <= 64255 || codePoint >= 65040 && codePoint <= 65049 || codePoint >= 65072 && codePoint <= 65131 || codePoint >= 65281 && codePoint <= 65376 || codePoint >= 65504 && codePoint <= 65510 || codePoint >= 127488 && codePoint <= 127569 || codePoint >= 131072 && codePoint <= 262141);
|
|
1566
|
+
}
|
|
1567
|
+
function isEmojiCodePoint(codePoint) {
|
|
1568
|
+
return codePoint >= 127462 && codePoint <= 127487 || codePoint >= 127744 && codePoint <= 129791 || codePoint >= 9728 && codePoint <= 10175;
|
|
1569
|
+
}
|
|
1570
|
+
function visibleLength(value) {
|
|
1571
|
+
return tokenizeVisibleText(value).reduce((total, token) => total + token.visibleWidth, 0);
|
|
1572
|
+
}
|
|
1573
|
+
function sliceVisible(value, width) {
|
|
1574
|
+
if (width <= 0) {
|
|
1575
|
+
return "";
|
|
1576
|
+
}
|
|
1577
|
+
let currentWidth = 0;
|
|
1578
|
+
let output = "";
|
|
1579
|
+
let truncated = false;
|
|
1580
|
+
let containsAnsi = false;
|
|
1581
|
+
for (const token of tokenizeVisibleText(value)) {
|
|
1582
|
+
if (token.visibleWidth === 0) {
|
|
1583
|
+
output += token.value;
|
|
1584
|
+
containsAnsi = true;
|
|
1585
|
+
continue;
|
|
1586
|
+
}
|
|
1587
|
+
if (currentWidth + token.visibleWidth > width) {
|
|
1588
|
+
truncated = true;
|
|
1589
|
+
break;
|
|
1590
|
+
}
|
|
1591
|
+
output += token.value;
|
|
1592
|
+
currentWidth += token.visibleWidth;
|
|
1593
|
+
}
|
|
1594
|
+
if (truncated && containsAnsi && !output.endsWith(ANSI.reset)) {
|
|
1595
|
+
output += ANSI.reset;
|
|
1596
|
+
}
|
|
1597
|
+
return output;
|
|
1598
|
+
}
|
|
1599
|
+
function padVisible(value, width) {
|
|
1600
|
+
const slicedValue = sliceVisible(value, width);
|
|
1601
|
+
const length = visibleLength(slicedValue);
|
|
1602
|
+
if (length >= width) {
|
|
1603
|
+
return slicedValue;
|
|
1604
|
+
}
|
|
1605
|
+
return `${slicedValue}${" ".repeat(width - length)}`;
|
|
1606
|
+
}
|
|
1607
|
+
function getViewportWidth() {
|
|
1608
|
+
const columns = process$1.stdout.columns || 100;
|
|
1609
|
+
return Math.max(Math.min(columns - 2, 108), 4);
|
|
1610
|
+
}
|
|
1611
|
+
function getTerminalColumns() {
|
|
1612
|
+
return Math.max(process$1.stdout.columns || 100, 4);
|
|
1613
|
+
}
|
|
1614
|
+
function renderBadge(text, tone) {
|
|
1615
|
+
const theme = tone === "service" ? ANSI.cyan : tone === "accent" ? ANSI.green : tone === "warn" ? ANSI.yellow : ANSI.gray;
|
|
1616
|
+
return colorize(`[${text}]`, theme);
|
|
1617
|
+
}
|
|
1618
|
+
function createBox(title, lines, footerLines = []) {
|
|
1619
|
+
const contentLines = lines.length > 0 ? lines : [""];
|
|
1620
|
+
const footer = footerLines.length > 0 ? ["", ...footerLines] : [];
|
|
1621
|
+
const width = getViewportWidth();
|
|
1622
|
+
const top = `+${"-".repeat(width - 2)}+`;
|
|
1623
|
+
const titleLine = `| ${padVisible(colorize(title, ANSI.bold), width - 4)} |`;
|
|
1624
|
+
const body = [...contentLines, ...footer].map((line) => `| ${padVisible(line, width - 4)} |`);
|
|
1625
|
+
return [top, titleLine, top, ...body, top];
|
|
1626
|
+
}
|
|
1627
|
+
function renderScreenBox(title, lines, footerLines = []) {
|
|
1628
|
+
console.clear();
|
|
1629
|
+
console.log(createBox(title, lines, footerLines).join("\n"));
|
|
1630
|
+
}
|
|
1631
|
+
function getPromptPrefix(service, testMode = false, traceMode = false) {
|
|
1632
|
+
return [
|
|
1633
|
+
colorize("ju-hong-e", `${ANSI.bold}${ANSI.cyan}`),
|
|
1634
|
+
renderBadge(service, "service"),
|
|
1635
|
+
testMode ? renderBadge("TEST", "warn") : "",
|
|
1636
|
+
traceMode ? renderBadge("TRACE", "muted") : ""
|
|
1637
|
+
].filter(Boolean).join(" ");
|
|
1638
|
+
}
|
|
1639
|
+
function isSlashCommandQuery(input) {
|
|
1640
|
+
const trimmed = input.trimStart();
|
|
1641
|
+
return trimmed.startsWith("/") || /^\[(codex|gemini|claude)\]\s+\/?/i.test(trimmed);
|
|
1642
|
+
}
|
|
1643
|
+
function getVisibleCommandOptions(commands, input) {
|
|
1644
|
+
if (!isSlashCommandQuery(input)) {
|
|
1645
|
+
return [];
|
|
1646
|
+
}
|
|
1647
|
+
const keyword = input.toLowerCase();
|
|
1648
|
+
return commands.filter((command) => {
|
|
1649
|
+
const candidates = [command.command, command.insertValue, ...command.matchKeywords || []].filter((candidate) => Boolean(candidate)).map((candidate) => candidate.toLowerCase());
|
|
1650
|
+
return candidates.some((candidate) => candidate.startsWith(keyword));
|
|
1651
|
+
});
|
|
1652
|
+
}
|
|
1653
|
+
function renderCommandPalette(commands, cursor) {
|
|
1654
|
+
const windowSize = 8;
|
|
1655
|
+
const start = Math.max(0, Math.min(cursor - Math.floor(windowSize / 2), Math.max(commands.length - windowSize, 0)));
|
|
1656
|
+
const visible = commands.slice(start, start + windowSize);
|
|
1657
|
+
return createBox(
|
|
1658
|
+
"Slash Commands",
|
|
1659
|
+
visible.map((command, index) => {
|
|
1660
|
+
const commandIndex = start + index;
|
|
1661
|
+
const prefix = cursor === commandIndex ? colorize(">", ANSI.green) : " ";
|
|
1662
|
+
const label = cursor === commandIndex ? colorize(command.command, ANSI.green) : command.command;
|
|
1663
|
+
const description = colorize(command.description, ANSI.gray);
|
|
1664
|
+
return `${prefix} ${padVisible(label, 28)} ${description}`;
|
|
1665
|
+
}),
|
|
1666
|
+
[
|
|
1667
|
+
`${colorize("\uD45C\uC2DC \uBC94\uC704", ANSI.gray)} ${start + 1}-${Math.min(start + visible.length, commands.length)} / ${commands.length}`,
|
|
1668
|
+
`${colorize("Tab", ANSI.yellow)} \uC120\uD0DD \uC801\uC6A9`,
|
|
1669
|
+
`${colorize("Up/Down", ANSI.yellow)} \uBA85\uB839 \uC774\uB3D9`,
|
|
1670
|
+
`${colorize("Shift+Up/Down", ANSI.yellow)} \uD788\uC2A4\uD1A0\uB9AC \uC774\uB3D9`,
|
|
1671
|
+
`${colorize("ESC", ANSI.yellow)} \uD604\uC7AC \uC785\uB825 \uC9C0\uC6B0\uAE30`,
|
|
1672
|
+
`${colorize("Ctrl+C", ANSI.yellow)} \uC138\uC158 \uC885\uB8CC`,
|
|
1673
|
+
`${colorize("Enter", ANSI.yellow)} \uD604\uC7AC \uC785\uB825 \uC2E4\uD589`
|
|
1674
|
+
]
|
|
1675
|
+
);
|
|
1676
|
+
}
|
|
1677
|
+
function commitInteractiveLine(prefix, buffer) {
|
|
1678
|
+
readline__default.default.cursorTo(process$1.stdout, 0);
|
|
1679
|
+
readline__default.default.clearScreenDown(process$1.stdout);
|
|
1680
|
+
process$1.stdout.write(`${prefix} ${colorize(">", ANSI.blue)} ${buffer}
|
|
1681
|
+
`);
|
|
1682
|
+
}
|
|
1683
|
+
function wrapPlainText(value, firstLineWidth, continuationLineWidth) {
|
|
1684
|
+
const renderedLines = [];
|
|
1685
|
+
const safeFirstLineWidth = Math.max(firstLineWidth, 1);
|
|
1686
|
+
const safeContinuationLineWidth = Math.max(continuationLineWidth, 1);
|
|
1687
|
+
const normalizedValue = value.replace(/\r\n?/g, "\n");
|
|
1688
|
+
const pushWrappedLine = (line) => {
|
|
1689
|
+
if (line.length === 0) {
|
|
1690
|
+
renderedLines.push("");
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1693
|
+
let currentLine = "";
|
|
1694
|
+
let currentWidth = 0;
|
|
1695
|
+
let currentLineWidth = renderedLines.length === 0 ? safeFirstLineWidth : safeContinuationLineWidth;
|
|
1696
|
+
for (const segment of segmentGraphemes(line)) {
|
|
1697
|
+
const segmentWidth = Math.max(getGraphemeWidth(segment), 1);
|
|
1698
|
+
if (currentLine && currentWidth + segmentWidth > currentLineWidth) {
|
|
1699
|
+
renderedLines.push(currentLine);
|
|
1700
|
+
currentLine = "";
|
|
1701
|
+
currentWidth = 0;
|
|
1702
|
+
currentLineWidth = safeContinuationLineWidth;
|
|
1703
|
+
}
|
|
1704
|
+
currentLine += segment;
|
|
1705
|
+
currentWidth += segmentWidth;
|
|
1706
|
+
}
|
|
1707
|
+
renderedLines.push(currentLine);
|
|
1708
|
+
};
|
|
1709
|
+
normalizedValue.split("\n").forEach(pushWrappedLine);
|
|
1710
|
+
return renderedLines.length > 0 ? renderedLines : [""];
|
|
1711
|
+
}
|
|
1712
|
+
function buildPromptRenderLayout(promptLabel, buffer, placeholder) {
|
|
1713
|
+
const columns = getTerminalColumns();
|
|
1714
|
+
const promptLabelWidth = visibleLength(promptLabel);
|
|
1715
|
+
const continuationIndentWidth = Math.min(promptLabelWidth, Math.max(columns - 1, 0));
|
|
1716
|
+
const continuationIndent = " ".repeat(continuationIndentWidth);
|
|
1717
|
+
const firstLineWidth = Math.max(columns - promptLabelWidth, 1);
|
|
1718
|
+
const continuationLineWidth = Math.max(columns - continuationIndentWidth, 1);
|
|
1719
|
+
const hasBuffer = buffer.length > 0;
|
|
1720
|
+
const contentLines = hasBuffer ? wrapPlainText(buffer, firstLineWidth, continuationLineWidth) : wrapPlainText(placeholder || "", firstLineWidth, continuationLineWidth);
|
|
1721
|
+
const cursorLines = hasBuffer ? contentLines : [""];
|
|
1722
|
+
const renderedPromptLines = contentLines.map((line, index) => {
|
|
1723
|
+
const linePrefix = index === 0 ? promptLabel : continuationIndent;
|
|
1724
|
+
return `${linePrefix}${hasBuffer ? line : colorize(line, ANSI.dim)}`;
|
|
1725
|
+
});
|
|
1726
|
+
const activeCursorLine = cursorLines.at(-1) || "";
|
|
1727
|
+
const activeCursorLinePrefixWidth = cursorLines.length <= 1 ? promptLabelWidth : continuationIndentWidth;
|
|
1728
|
+
return {
|
|
1729
|
+
cursorColumn: activeCursorLinePrefixWidth + visibleLength(activeCursorLine),
|
|
1730
|
+
cursorRowOffset: Math.max(cursorLines.length - 1, 0),
|
|
1731
|
+
lines: renderedPromptLines,
|
|
1732
|
+
totalLines: renderedPromptLines.length
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1735
|
+
function clearRenderedPrompt(previousState) {
|
|
1736
|
+
if (previousState?.cursorRowOffset) {
|
|
1737
|
+
readline__default.default.moveCursor(process$1.stdout, 0, -previousState.cursorRowOffset);
|
|
1738
|
+
}
|
|
1739
|
+
readline__default.default.cursorTo(process$1.stdout, 0);
|
|
1740
|
+
readline__default.default.clearScreenDown(process$1.stdout);
|
|
1741
|
+
}
|
|
1742
|
+
function renderPromptLayout(layout, previousState) {
|
|
1743
|
+
clearRenderedPrompt(previousState);
|
|
1744
|
+
process$1.stdout.write(layout.lines.join("\n"));
|
|
1745
|
+
const linesBelowCursor = layout.totalLines - 1 - layout.cursorRowOffset;
|
|
1746
|
+
if (linesBelowCursor > 0) {
|
|
1747
|
+
readline__default.default.moveCursor(process$1.stdout, 0, -linesBelowCursor);
|
|
1748
|
+
}
|
|
1749
|
+
readline__default.default.cursorTo(process$1.stdout, layout.cursorColumn);
|
|
1750
|
+
return {
|
|
1751
|
+
cursorRowOffset: layout.cursorRowOffset
|
|
1752
|
+
};
|
|
1753
|
+
}
|
|
1754
|
+
function renderInteractivePrompt(prefix, buffer, commands, commandCursor, testMode = false, previousState) {
|
|
1755
|
+
const promptLabel = `${prefix} ${colorize(">", ANSI.blue)} `;
|
|
1756
|
+
const paletteLines = commands.length > 0 ? renderCommandPalette(commands, commandCursor) : [];
|
|
1757
|
+
const hintLines = commands.length === 0 ? wrapPlainText("Tip: '/' \uB85C \uBA85\uB839 \uBA54\uB274\uB97C \uC5F4\uACE0, \uC11C\uBE44\uC2A4 \uBA85\uB839\uC740 [service] prefix\uB85C \uC801\uC6A9\uB429\uB2C8\uB2E4.", getTerminalColumns(), getTerminalColumns()).map(
|
|
1758
|
+
(line) => colorize(line, ANSI.gray)
|
|
1759
|
+
) : [];
|
|
1760
|
+
const promptLayout = buildPromptRenderLayout(promptLabel, buffer, "\uBA54\uC2DC\uC9C0\uB97C \uC785\uB825\uD558\uC138\uC694. '/' \uB85C \uBA85\uB839 \uBA54\uB274 \uC5F4\uAE30");
|
|
1761
|
+
const lines = [...promptLayout.lines, ...hintLines, ...paletteLines];
|
|
1762
|
+
if (testMode && !buffer && commands.length === 0) {
|
|
1763
|
+
process$1.stdout.write("");
|
|
1764
|
+
}
|
|
1765
|
+
return renderPromptLayout(
|
|
1766
|
+
{
|
|
1767
|
+
...promptLayout,
|
|
1768
|
+
lines,
|
|
1769
|
+
totalLines: lines.length
|
|
1770
|
+
},
|
|
1771
|
+
previousState
|
|
1772
|
+
);
|
|
1773
|
+
}
|
|
1774
|
+
function restoreHistoryBuffer(history, historyCursor) {
|
|
1775
|
+
if (historyCursor < 0 || historyCursor >= history.length) {
|
|
1776
|
+
return "";
|
|
1777
|
+
}
|
|
1778
|
+
return history[historyCursor] || "";
|
|
1779
|
+
}
|
|
1780
|
+
function getHistoryNavigationDirection(key) {
|
|
1781
|
+
if (key.shift && key.name === "up") {
|
|
1782
|
+
return "up";
|
|
1783
|
+
}
|
|
1784
|
+
if (key.shift && key.name === "down") {
|
|
1785
|
+
return "down";
|
|
1786
|
+
}
|
|
1787
|
+
return null;
|
|
1788
|
+
}
|
|
1789
|
+
function printSessionBanner(options) {
|
|
1790
|
+
const commandSummary = options.commands.map((command) => `${command.command} ${colorize("-", ANSI.gray)} ${command.description}`);
|
|
1791
|
+
console.log(
|
|
1792
|
+
createBox(
|
|
1793
|
+
"Ju-hong-e Interactive Session( \uD55C\uD654\uC0DD\uBA85 \uD1B5\uD569 AI-Tool )",
|
|
1794
|
+
[
|
|
1795
|
+
`${colorize("Service", ANSI.gray)} ${renderBadge(options.service, "service")}`,
|
|
1796
|
+
`${colorize("Mode", ANSI.gray)} ${options.testMode ? renderBadge("TEST", "warn") : renderBadge("LIVE", "accent")}${options.traceMode ? ` ${renderBadge("TRACE", "muted")}` : ""}`,
|
|
1797
|
+
`${colorize("AI Stream", ANSI.gray)} ${options.streamOutputEnabled ? renderBadge("ON", "accent") : renderBadge("OFF", "muted")}`,
|
|
1798
|
+
"",
|
|
1799
|
+
...commandSummary
|
|
1800
|
+
],
|
|
1801
|
+
[
|
|
1802
|
+
colorize("Slash \uBA85\uB839\uC740 '/' \uC785\uB825 \uC2DC \uBCFC \uC218 \uC788\uACE0, \uC11C\uBE44\uC2A4 \uACE0\uC720 \uBA85\uB839\uC740 [service] prefix\uB85C \uD45C\uC2DC\uB429\uB2C8\uB2E4.", ANSI.gray),
|
|
1803
|
+
colorize("\uC2E4\uC2DC\uAC04 \uC751\uB2F5 \uCD9C\uB825\uC740 /show-stream \uC73C\uB85C \uCF1C\uACE0 \uB04C \uC218 \uC788\uC2B5\uB2C8\uB2E4.", ANSI.gray),
|
|
1804
|
+
colorize("ESC\uB294 \uD604\uC7AC \uC791\uC5C5 \uCDE8\uC18C, Ctrl+C\uB294 \uC138\uC158 \uC885\uB8CC\uC785\uB2C8\uB2E4.", ANSI.gray)
|
|
1805
|
+
]
|
|
1806
|
+
).join("\n")
|
|
1807
|
+
);
|
|
1808
|
+
}
|
|
1809
|
+
function promptSessionInput(options) {
|
|
1810
|
+
return new Promise((resolve, reject) => {
|
|
1811
|
+
const { stdin } = process;
|
|
1812
|
+
const rawState = "isRaw" in stdin ? Boolean(stdin.isRaw) : false;
|
|
1813
|
+
const prefix = getPromptPrefix(options.service, options.testMode, options.traceMode);
|
|
1814
|
+
let buffer = "";
|
|
1815
|
+
let historyCursor = -1;
|
|
1816
|
+
let historyDraft = "";
|
|
1817
|
+
let commandCursor = 0;
|
|
1818
|
+
let renderState;
|
|
1819
|
+
const cleanup = () => {
|
|
1820
|
+
stdin.removeListener("keypress", onKeyPress);
|
|
1821
|
+
if (stdin.isTTY) {
|
|
1822
|
+
stdin.setRawMode(rawState);
|
|
1823
|
+
}
|
|
1824
|
+
stdin.pause();
|
|
1825
|
+
};
|
|
1826
|
+
const applySelectedCommand = () => {
|
|
1827
|
+
const commands = getVisibleCommandOptions(options.commands, buffer);
|
|
1828
|
+
const selectedCommand = commands[commandCursor];
|
|
1829
|
+
if (!selectedCommand) {
|
|
1830
|
+
return false;
|
|
1831
|
+
}
|
|
1832
|
+
buffer = selectedCommand.insertValue || selectedCommand.command;
|
|
1833
|
+
commandCursor = 0;
|
|
1834
|
+
historyCursor = -1;
|
|
1835
|
+
historyDraft = "";
|
|
1836
|
+
return true;
|
|
1837
|
+
};
|
|
1838
|
+
const render = () => {
|
|
1839
|
+
const commands = getVisibleCommandOptions(options.commands, buffer);
|
|
1840
|
+
commandCursor = Math.max(0, Math.min(commandCursor, Math.max(commands.length - 1, 0)));
|
|
1841
|
+
renderState = renderInteractivePrompt(prefix, buffer, commands, commandCursor, options.testMode, renderState);
|
|
1842
|
+
};
|
|
1843
|
+
const moveHistory = (direction) => {
|
|
1844
|
+
if (options.history.length === 0) {
|
|
1845
|
+
return;
|
|
1846
|
+
}
|
|
1847
|
+
if (historyCursor === -1) {
|
|
1848
|
+
historyDraft = buffer;
|
|
1849
|
+
}
|
|
1850
|
+
if (direction === "up") {
|
|
1851
|
+
historyCursor = historyCursor === -1 ? options.history.length - 1 : Math.max(historyCursor - 1, 0);
|
|
1852
|
+
buffer = restoreHistoryBuffer(options.history, historyCursor);
|
|
1853
|
+
return;
|
|
1854
|
+
}
|
|
1855
|
+
if (historyCursor === -1) {
|
|
1856
|
+
return;
|
|
1857
|
+
}
|
|
1858
|
+
if (historyCursor >= options.history.length - 1) {
|
|
1859
|
+
historyCursor = -1;
|
|
1860
|
+
buffer = historyDraft;
|
|
1861
|
+
return;
|
|
1862
|
+
}
|
|
1863
|
+
historyCursor += 1;
|
|
1864
|
+
buffer = restoreHistoryBuffer(options.history, historyCursor);
|
|
1865
|
+
};
|
|
1866
|
+
const onKeyPress = (input, key) => {
|
|
1867
|
+
const visibleCommands = getVisibleCommandOptions(options.commands, buffer);
|
|
1868
|
+
const historyDirection = getHistoryNavigationDirection(key);
|
|
1869
|
+
if (key.ctrl && key.name === "c") {
|
|
1870
|
+
clearRenderedPrompt(renderState);
|
|
1871
|
+
cleanup();
|
|
1872
|
+
reject(createUserTerminatedError("\uC0AC\uC6A9\uC790\uAC00 \uC138\uC158 \uC885\uB8CC\uB97C \uC694\uCCAD\uD588\uC2B5\uB2C8\uB2E4."));
|
|
1873
|
+
return;
|
|
1874
|
+
}
|
|
1875
|
+
if (key.name === "return" || key.name === "enter") {
|
|
1876
|
+
if (buffer.trim() === "/" && visibleCommands.length > 0) {
|
|
1877
|
+
applySelectedCommand();
|
|
1878
|
+
render();
|
|
1879
|
+
return;
|
|
1880
|
+
}
|
|
1881
|
+
const resolved = buffer.trim();
|
|
1882
|
+
clearRenderedPrompt(renderState);
|
|
1883
|
+
commitInteractiveLine(prefix, resolved);
|
|
1884
|
+
cleanup();
|
|
1885
|
+
resolve(resolved);
|
|
1886
|
+
return;
|
|
1887
|
+
}
|
|
1888
|
+
if (key.name === "tab") {
|
|
1889
|
+
if (applySelectedCommand()) {
|
|
1890
|
+
render();
|
|
1891
|
+
}
|
|
1892
|
+
return;
|
|
1893
|
+
}
|
|
1894
|
+
if (key.name === "escape") {
|
|
1895
|
+
if (buffer.length > 0) {
|
|
1896
|
+
buffer = "";
|
|
1897
|
+
commandCursor = 0;
|
|
1898
|
+
historyCursor = -1;
|
|
1899
|
+
historyDraft = "";
|
|
1900
|
+
render();
|
|
1901
|
+
}
|
|
1902
|
+
return;
|
|
1903
|
+
}
|
|
1904
|
+
if (key.name === "backspace") {
|
|
1905
|
+
if (buffer.length > 0) {
|
|
1906
|
+
buffer = buffer.slice(0, -1);
|
|
1907
|
+
historyCursor = -1;
|
|
1908
|
+
historyDraft = "";
|
|
1909
|
+
commandCursor = 0;
|
|
1910
|
+
render();
|
|
1911
|
+
}
|
|
1912
|
+
return;
|
|
1913
|
+
}
|
|
1914
|
+
if (historyDirection) {
|
|
1915
|
+
moveHistory(historyDirection);
|
|
1916
|
+
render();
|
|
1917
|
+
return;
|
|
1918
|
+
}
|
|
1919
|
+
if (key.name === "up") {
|
|
1920
|
+
if (visibleCommands.length > 0) {
|
|
1921
|
+
commandCursor = commandCursor === 0 ? visibleCommands.length - 1 : commandCursor - 1;
|
|
1922
|
+
}
|
|
1923
|
+
render();
|
|
1924
|
+
return;
|
|
1925
|
+
}
|
|
1926
|
+
if (key.name === "down") {
|
|
1927
|
+
if (visibleCommands.length > 0) {
|
|
1928
|
+
commandCursor = commandCursor === visibleCommands.length - 1 ? 0 : commandCursor + 1;
|
|
1929
|
+
}
|
|
1930
|
+
render();
|
|
1931
|
+
return;
|
|
1932
|
+
}
|
|
1933
|
+
if (input && !key.ctrl && !key.meta) {
|
|
1934
|
+
buffer += input;
|
|
1935
|
+
historyCursor = -1;
|
|
1936
|
+
historyDraft = "";
|
|
1937
|
+
commandCursor = 0;
|
|
1938
|
+
render();
|
|
1939
|
+
}
|
|
1940
|
+
};
|
|
1941
|
+
readline__default.default.emitKeypressEvents(stdin);
|
|
1942
|
+
if (stdin.isTTY) {
|
|
1943
|
+
stdin.setRawMode(true);
|
|
1944
|
+
}
|
|
1945
|
+
stdin.resume();
|
|
1946
|
+
stdin.on("keypress", onKeyPress);
|
|
1947
|
+
render();
|
|
1948
|
+
});
|
|
1949
|
+
}
|
|
1950
|
+
function selectSingleOption(title, options) {
|
|
1951
|
+
return new Promise((resolve, reject) => {
|
|
1952
|
+
if (options.length === 0) {
|
|
1953
|
+
reject(new Error("\uC120\uD0DD \uAC00\uB2A5\uD55C \uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
1954
|
+
return;
|
|
1955
|
+
}
|
|
1956
|
+
const { stdin } = process;
|
|
1957
|
+
const rawState = "isRaw" in stdin ? Boolean(stdin.isRaw) : false;
|
|
1958
|
+
let cursor = 0;
|
|
1959
|
+
const cleanup = () => {
|
|
1960
|
+
stdin.removeListener("keypress", onKeyPress);
|
|
1961
|
+
if (stdin.isTTY) {
|
|
1962
|
+
stdin.setRawMode(rawState);
|
|
1963
|
+
}
|
|
1964
|
+
stdin.pause();
|
|
1965
|
+
console.clear();
|
|
1966
|
+
};
|
|
1967
|
+
const render = () => {
|
|
1968
|
+
renderScreenBox(
|
|
1969
|
+
title,
|
|
1970
|
+
options.map((option, index) => {
|
|
1971
|
+
const prefix = cursor === index ? colorize(">", ANSI.green) : " ";
|
|
1972
|
+
const label = cursor === index ? colorize(option.label, ANSI.green) : option.label;
|
|
1973
|
+
return `${prefix} ${padVisible(label, 24)} ${colorize(option.description || "", ANSI.gray)}`;
|
|
1974
|
+
}),
|
|
1975
|
+
[
|
|
1976
|
+
`${colorize("Up/Down", ANSI.yellow)} \uC774\uB3D9`,
|
|
1977
|
+
`${colorize("Enter", ANSI.yellow)} \uC120\uD0DD`,
|
|
1978
|
+
`${colorize("ESC", ANSI.yellow)} \uCDE8\uC18C`,
|
|
1979
|
+
`${colorize("Ctrl+C", ANSI.yellow)} \uC138\uC158 \uC885\uB8CC`
|
|
1980
|
+
]
|
|
1981
|
+
);
|
|
1982
|
+
};
|
|
1983
|
+
const onKeyPress = (_input, key) => {
|
|
1984
|
+
if (key.ctrl && key.name === "c") {
|
|
1985
|
+
cleanup();
|
|
1986
|
+
reject(createUserTerminatedError("\uC0AC\uC6A9\uC790\uAC00 \uC120\uD0DD \uC785\uB825 \uC911 \uC138\uC158 \uC885\uB8CC\uB97C \uC694\uCCAD\uD588\uC2B5\uB2C8\uB2E4."));
|
|
1987
|
+
return;
|
|
1988
|
+
}
|
|
1989
|
+
if (key.name === "escape") {
|
|
1990
|
+
cleanup();
|
|
1991
|
+
reject(createUserCancelledError("\uC0AC\uC6A9\uC790\uAC00 \uC120\uD0DD \uC785\uB825\uC744 \uCDE8\uC18C\uD588\uC2B5\uB2C8\uB2E4."));
|
|
1992
|
+
return;
|
|
1993
|
+
}
|
|
1994
|
+
if (key.name === "up") {
|
|
1995
|
+
cursor = cursor === 0 ? options.length - 1 : cursor - 1;
|
|
1996
|
+
render();
|
|
1997
|
+
return;
|
|
1998
|
+
}
|
|
1999
|
+
if (key.name === "down") {
|
|
2000
|
+
cursor = cursor === options.length - 1 ? 0 : cursor + 1;
|
|
2001
|
+
render();
|
|
2002
|
+
return;
|
|
2003
|
+
}
|
|
2004
|
+
if (key.name === "return" || key.name === "enter") {
|
|
2005
|
+
const selected = options[cursor];
|
|
2006
|
+
if (!selected) {
|
|
2007
|
+
cleanup();
|
|
2008
|
+
reject(new Error("\uC120\uD0DD\uD55C \uD56D\uBAA9\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
2009
|
+
return;
|
|
2010
|
+
}
|
|
2011
|
+
cleanup();
|
|
2012
|
+
appendSlashCommandTelemetryInputValue(selected.value);
|
|
2013
|
+
resolve(selected.value);
|
|
2014
|
+
}
|
|
2015
|
+
};
|
|
2016
|
+
readline__default.default.emitKeypressEvents(stdin);
|
|
2017
|
+
if (stdin.isTTY) {
|
|
2018
|
+
stdin.setRawMode(true);
|
|
2019
|
+
}
|
|
2020
|
+
stdin.resume();
|
|
2021
|
+
stdin.on("keypress", onKeyPress);
|
|
2022
|
+
render();
|
|
2023
|
+
});
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
// src/common/prompts/render.ts
|
|
2027
|
+
function normalizeMarkdown(markdown) {
|
|
2028
|
+
return markdown.trim();
|
|
2029
|
+
}
|
|
2030
|
+
function renderMarkdownBlocks(markdownBlocks) {
|
|
2031
|
+
const blocks = (markdownBlocks || []).map(normalizeMarkdown).filter(Boolean);
|
|
2032
|
+
if (blocks.length === 0) {
|
|
2033
|
+
return "";
|
|
2034
|
+
}
|
|
2035
|
+
return blocks.join("\n\n");
|
|
2036
|
+
}
|
|
2037
|
+
function renderLabeledSection(section) {
|
|
2038
|
+
const markdown = normalizeMarkdown(section.markdown);
|
|
2039
|
+
if (!markdown) {
|
|
2040
|
+
return "";
|
|
2041
|
+
}
|
|
2042
|
+
return [`[${section.heading}]`, markdown].join("\n");
|
|
2043
|
+
}
|
|
2044
|
+
function toPromptSections(prompt) {
|
|
2045
|
+
const sections = [
|
|
2046
|
+
{
|
|
2047
|
+
heading: "\uC9C0\uCE68",
|
|
2048
|
+
markdown: renderMarkdownBlocks(prompt.instructions)
|
|
2049
|
+
},
|
|
2050
|
+
{
|
|
2051
|
+
heading: "\uCD5C\uADFC \uD788\uC2A4\uD1A0\uB9AC",
|
|
2052
|
+
markdown: prompt.historyContext || "(\uC5C6\uC74C)"
|
|
2053
|
+
},
|
|
2054
|
+
{
|
|
2055
|
+
heading: "\uD604\uC7AC \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uC0C1\uD0DC",
|
|
2056
|
+
markdown: prompt.workspaceSnapshot.available ? [
|
|
2057
|
+
`Git Root: ${prompt.workspaceSnapshot.gitRoot}`,
|
|
2058
|
+
`Branch: ${prompt.workspaceSnapshot.branch || "(detached)"}`,
|
|
2059
|
+
"",
|
|
2060
|
+
"[git status --short]",
|
|
2061
|
+
prompt.workspaceSnapshot.statusLines.join("\n") || "(\uC5C6\uC74C)",
|
|
2062
|
+
"",
|
|
2063
|
+
"[git diff --stat]",
|
|
2064
|
+
prompt.workspaceSnapshot.diffStat || "(\uC5C6\uC74C)",
|
|
2065
|
+
"",
|
|
2066
|
+
"[\uCD5C\uADFC \uCEE4\uBC0B]",
|
|
2067
|
+
prompt.workspaceSnapshot.recentCommits.join("\n") || "(\uC5C6\uC74C)"
|
|
2068
|
+
].join("\n") : prompt.workspaceSnapshot.note
|
|
2069
|
+
},
|
|
2070
|
+
{
|
|
2071
|
+
heading: "\uADDC\uCE59",
|
|
2072
|
+
markdown: renderMarkdownBlocks(prompt.rules)
|
|
2073
|
+
},
|
|
2074
|
+
{
|
|
2075
|
+
heading: "\uC751\uB2F5 \uC591\uC2DD",
|
|
2076
|
+
markdown: renderMarkdownBlocks(prompt.forms)
|
|
2077
|
+
},
|
|
2078
|
+
...prompt.sections || []
|
|
2079
|
+
];
|
|
2080
|
+
return sections.filter((section) => normalizeMarkdown(section.markdown));
|
|
2081
|
+
}
|
|
2082
|
+
function renderPromptForService(prompt) {
|
|
2083
|
+
if (typeof prompt === "string") {
|
|
2084
|
+
return safeExcerpt(prompt, 2e5);
|
|
2085
|
+
}
|
|
2086
|
+
return [
|
|
2087
|
+
`# ${prompt.title}`,
|
|
2088
|
+
"",
|
|
2089
|
+
`\uD604\uC7AC \uC120\uD0DD\uB41C AI \uC11C\uBE44\uC2A4: ${prompt.service}`,
|
|
2090
|
+
...toPromptSections(prompt).flatMap((section) => ["", renderLabeledSection(section)])
|
|
2091
|
+
].join("\n");
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
// src/common/ai-services/internal/shared.ts
|
|
2095
|
+
function quoteArgs(args) {
|
|
2096
|
+
return args.map((arg) => shellQuote(arg)).join(" ");
|
|
2097
|
+
}
|
|
2098
|
+
function toUnique(values) {
|
|
2099
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2100
|
+
return values.filter((value) => {
|
|
2101
|
+
if (!value || seen.has(value)) {
|
|
2102
|
+
return false;
|
|
2103
|
+
}
|
|
2104
|
+
seen.add(value);
|
|
2105
|
+
return true;
|
|
2106
|
+
});
|
|
2107
|
+
}
|
|
2108
|
+
function createServiceCommandSpec(command, attempts) {
|
|
2109
|
+
const normalizedAttempts = attempts.map(({ args, label }) => ({
|
|
2110
|
+
args,
|
|
2111
|
+
label,
|
|
2112
|
+
preview: `${command} ${quoteArgs(args)}`
|
|
2113
|
+
}));
|
|
2114
|
+
return {
|
|
2115
|
+
attempts: normalizedAttempts,
|
|
2116
|
+
command,
|
|
2117
|
+
preview: normalizedAttempts.map((attempt) => attempt.preview).join(" || ")
|
|
2118
|
+
};
|
|
2119
|
+
}
|
|
2120
|
+
function getDefaultReasoningEffort(mode) {
|
|
2121
|
+
if (mode === "review") {
|
|
2122
|
+
return "high";
|
|
2123
|
+
}
|
|
2124
|
+
if (mode === "commit") {
|
|
2125
|
+
return "medium";
|
|
2126
|
+
}
|
|
2127
|
+
if (mode === "gen-html") {
|
|
2128
|
+
return "medium";
|
|
2129
|
+
}
|
|
2130
|
+
if (mode === "gen-storybook") {
|
|
2131
|
+
return "medium";
|
|
2132
|
+
}
|
|
2133
|
+
return "low";
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
// src/common/ai-services/builders/claude.ts
|
|
2137
|
+
function normalizeClaudeEffort(effort) {
|
|
2138
|
+
if (effort === "minimal") {
|
|
2139
|
+
return "low";
|
|
2140
|
+
}
|
|
2141
|
+
return effort;
|
|
2142
|
+
}
|
|
2143
|
+
function resolveClaudePrimaryAlias(mode) {
|
|
2144
|
+
if (mode === "review") {
|
|
2145
|
+
return "opus";
|
|
2146
|
+
}
|
|
2147
|
+
return "sonnet";
|
|
2148
|
+
}
|
|
2149
|
+
function getClaudeAliasFallbacks(primaryAlias) {
|
|
2150
|
+
if (primaryAlias === "opus") {
|
|
2151
|
+
return ["opus", "sonnet", "haiku"];
|
|
2152
|
+
}
|
|
2153
|
+
if (primaryAlias === "haiku") {
|
|
2154
|
+
return ["haiku", "sonnet"];
|
|
2155
|
+
}
|
|
2156
|
+
return [primaryAlias, "sonnet", "haiku"];
|
|
2157
|
+
}
|
|
2158
|
+
function buildClaudeArgs(prompt, effort, model, fallbackModel) {
|
|
2159
|
+
const args = ["--permission-mode", "acceptEdits", "--effort", effort];
|
|
2160
|
+
if (model) {
|
|
2161
|
+
args.push("--model", model);
|
|
2162
|
+
}
|
|
2163
|
+
if (model && fallbackModel) {
|
|
2164
|
+
args.push("--fallback-model", fallbackModel);
|
|
2165
|
+
}
|
|
2166
|
+
args.push("-p", prompt);
|
|
2167
|
+
return args;
|
|
2168
|
+
}
|
|
2169
|
+
function buildClaudeCommand(prompt, mode, parsedArgs) {
|
|
2170
|
+
const renderedPrompt = renderPromptForService(prompt);
|
|
2171
|
+
const effort = normalizeClaudeEffort(parsedArgs.reasoningEffort ?? getDefaultReasoningEffort(mode));
|
|
2172
|
+
const primaryAlias = resolveClaudePrimaryAlias(mode);
|
|
2173
|
+
const aliasFallbacks = toUnique(getClaudeAliasFallbacks(primaryAlias));
|
|
2174
|
+
const modelCandidates = toUnique(parsedArgs.model ? [parsedArgs.model, ...aliasFallbacks] : aliasFallbacks);
|
|
2175
|
+
const attempts = modelCandidates.map((model, index) => ({
|
|
2176
|
+
args: buildClaudeArgs(renderedPrompt, effort, model, modelCandidates[index + 1]),
|
|
2177
|
+
label: `model:${model}`
|
|
2178
|
+
}));
|
|
2179
|
+
attempts.push({
|
|
2180
|
+
args: buildClaudeArgs(renderedPrompt, effort),
|
|
2181
|
+
label: "model:account-default"
|
|
2182
|
+
});
|
|
2183
|
+
return createServiceCommandSpec("claude", attempts);
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
// src/common/ai-services/builders/codex.ts
|
|
2187
|
+
function buildCodexArgs(prompt, reasoningEffort, model) {
|
|
2188
|
+
const args = ["exec", "--full-auto"];
|
|
2189
|
+
if (model) {
|
|
2190
|
+
args.push("--model", model);
|
|
2191
|
+
}
|
|
2192
|
+
args.push("-c", `model_reasoning_effort="${reasoningEffort}"`, prompt);
|
|
2193
|
+
return args;
|
|
2194
|
+
}
|
|
2195
|
+
function buildCodexCommand(prompt, mode, parsedArgs) {
|
|
2196
|
+
const renderedPrompt = renderPromptForService(prompt);
|
|
2197
|
+
const effort = parsedArgs.reasoningEffort ?? getDefaultReasoningEffort(mode);
|
|
2198
|
+
const preferredModelAlias = "gpt-5";
|
|
2199
|
+
const modelCandidates = toUnique(parsedArgs.model ? [parsedArgs.model, preferredModelAlias] : [preferredModelAlias]);
|
|
2200
|
+
const attempts = modelCandidates.map((model) => ({
|
|
2201
|
+
args: buildCodexArgs(renderedPrompt, effort, model),
|
|
2202
|
+
label: `model:${model}`
|
|
2203
|
+
}));
|
|
2204
|
+
attempts.push({
|
|
2205
|
+
args: buildCodexArgs(renderedPrompt, effort),
|
|
2206
|
+
label: "model:account-default"
|
|
2207
|
+
});
|
|
2208
|
+
return createServiceCommandSpec("codex", attempts);
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
// src/common/ai-services/builders/gemini.ts
|
|
2212
|
+
function resolveGeminiPrimaryAlias(mode, reasoningEffort) {
|
|
2213
|
+
if (mode === "review" || mode === "gen-html" || mode === "gen-storybook") {
|
|
2214
|
+
return "pro";
|
|
2215
|
+
}
|
|
2216
|
+
if (reasoningEffort === "high") {
|
|
2217
|
+
return "pro";
|
|
2218
|
+
}
|
|
2219
|
+
if (reasoningEffort === "minimal" || reasoningEffort === "low") {
|
|
2220
|
+
return "flash";
|
|
2221
|
+
}
|
|
2222
|
+
return "auto";
|
|
2223
|
+
}
|
|
2224
|
+
function getGeminiAliasFallbacks(primaryAlias) {
|
|
2225
|
+
if (primaryAlias === "pro") {
|
|
2226
|
+
return ["pro", "flash", "auto"];
|
|
2227
|
+
}
|
|
2228
|
+
if (primaryAlias === "flash") {
|
|
2229
|
+
return ["flash", "auto", "pro"];
|
|
2230
|
+
}
|
|
2231
|
+
return [primaryAlias, "auto", "flash", "pro"];
|
|
2232
|
+
}
|
|
2233
|
+
function buildGeminiArgs(prompt, model) {
|
|
2234
|
+
const args = ["--approval-mode", "auto_edit", "--sandbox"];
|
|
2235
|
+
if (!model) {
|
|
2236
|
+
args.push("-p", prompt);
|
|
2237
|
+
return args;
|
|
2238
|
+
}
|
|
2239
|
+
args.push("--model", model, "-p", prompt);
|
|
2240
|
+
return args;
|
|
2241
|
+
}
|
|
2242
|
+
function buildGeminiCommand(prompt, mode, parsedArgs) {
|
|
2243
|
+
const renderedPrompt = renderPromptForService(prompt);
|
|
2244
|
+
const reasoningEffort = parsedArgs.reasoningEffort ?? getDefaultReasoningEffort(mode);
|
|
2245
|
+
const primaryAlias = resolveGeminiPrimaryAlias(mode, reasoningEffort);
|
|
2246
|
+
const aliasFallbacks = toUnique(getGeminiAliasFallbacks(primaryAlias));
|
|
2247
|
+
const modelCandidates = toUnique(parsedArgs.model ? [parsedArgs.model, ...aliasFallbacks] : aliasFallbacks);
|
|
2248
|
+
const attempts = modelCandidates.map((model) => ({
|
|
2249
|
+
args: buildGeminiArgs(renderedPrompt, model),
|
|
2250
|
+
label: `model:${model}`
|
|
2251
|
+
}));
|
|
2252
|
+
attempts.push({
|
|
2253
|
+
args: buildGeminiArgs(renderedPrompt),
|
|
2254
|
+
label: "model:account-default"
|
|
2255
|
+
});
|
|
2256
|
+
return createServiceCommandSpec("gemini", attempts);
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
// src/common/ai-services/internal/build-service-command.ts
|
|
2260
|
+
function buildServiceCommand(options) {
|
|
2261
|
+
switch (options.service) {
|
|
2262
|
+
case "codex":
|
|
2263
|
+
return buildCodexCommand(options.prompt, options.mode, options.parsedArgs);
|
|
2264
|
+
case "gemini":
|
|
2265
|
+
return buildGeminiCommand(options.prompt, options.mode, options.parsedArgs);
|
|
2266
|
+
case "claude":
|
|
2267
|
+
return buildClaudeCommand(options.prompt, options.mode, options.parsedArgs);
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
function getBinaryName(service) {
|
|
2271
|
+
switch (service) {
|
|
2272
|
+
case "codex":
|
|
2273
|
+
return "codex";
|
|
2274
|
+
case "gemini":
|
|
2275
|
+
return "gemini";
|
|
2276
|
+
case "claude":
|
|
2277
|
+
return "claude";
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
function getInstallPackageName(service) {
|
|
2281
|
+
switch (service) {
|
|
2282
|
+
case "codex":
|
|
2283
|
+
return "@openai/codex";
|
|
2284
|
+
case "gemini":
|
|
2285
|
+
return "@google/gemini-cli";
|
|
2286
|
+
case "claude":
|
|
2287
|
+
return "@anthropic-ai/claude-code";
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
function isCliAvailable(service, trace) {
|
|
2291
|
+
const binaryName = getBinaryName(service);
|
|
2292
|
+
trace("cli-check:start", `${service} -> ${binaryName} --version`);
|
|
2293
|
+
const result = child_process.spawnSync(binaryName, ["--version"], {
|
|
2294
|
+
encoding: "utf8",
|
|
2295
|
+
maxBuffer: 1024 * 1024 * 2
|
|
2296
|
+
});
|
|
2297
|
+
if (result.error || result.status !== 0) {
|
|
2298
|
+
trace("cli-check:missing", getErrorSummary(result.error || result.stderr || result.status));
|
|
2299
|
+
return false;
|
|
2300
|
+
}
|
|
2301
|
+
trace("cli-check:ok", `${service}`);
|
|
2302
|
+
return true;
|
|
2303
|
+
}
|
|
2304
|
+
function installCli(service, trace) {
|
|
2305
|
+
const packageName = getInstallPackageName(service);
|
|
2306
|
+
trace("cli-install:start", `${service} -> ${packageName}`);
|
|
2307
|
+
const result = child_process.spawnSync("npm", ["install", "-g", packageName], {
|
|
2308
|
+
encoding: "utf8",
|
|
2309
|
+
maxBuffer: 1024 * 1024 * 20,
|
|
2310
|
+
stdio: "inherit"
|
|
2311
|
+
});
|
|
2312
|
+
if (result.error || result.status !== 0) {
|
|
2313
|
+
trace("cli-install:failed", getErrorSummary(result.error || result.stderr || result.status));
|
|
2314
|
+
throw new Error(`${service} CLI \uC124\uCE58\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. package=${packageName}`);
|
|
2315
|
+
}
|
|
2316
|
+
trace("cli-install:done", `${service}`);
|
|
2317
|
+
}
|
|
2318
|
+
function ensureServiceCli(service, parsedArgs, trace) {
|
|
2319
|
+
if (isCliAvailable(service, trace)) {
|
|
2320
|
+
return true;
|
|
2321
|
+
}
|
|
2322
|
+
if (parsedArgs.testMode) {
|
|
2323
|
+
trace("cli-install:skip:test-mode", service);
|
|
2324
|
+
return false;
|
|
2325
|
+
}
|
|
2326
|
+
installCli(service, trace);
|
|
2327
|
+
return isCliAvailable(service, trace);
|
|
2328
|
+
}
|
|
2329
|
+
function killServiceProcessTree(child, signal) {
|
|
2330
|
+
if (!child.pid) {
|
|
2331
|
+
return;
|
|
2332
|
+
}
|
|
2333
|
+
if (process.platform !== "win32") {
|
|
2334
|
+
try {
|
|
2335
|
+
process.kill(-child.pid, signal ?? "SIGTERM");
|
|
2336
|
+
return;
|
|
2337
|
+
} catch {
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
try {
|
|
2341
|
+
child.kill(signal);
|
|
2342
|
+
} catch {
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
function writeStreamChunk(chunk, streamOutput, writer, updateTrailingState) {
|
|
2346
|
+
if (!streamOutput) {
|
|
2347
|
+
return;
|
|
2348
|
+
}
|
|
2349
|
+
writer.write(chunk);
|
|
2350
|
+
updateTrailingState(!chunk.endsWith("\n"), writer);
|
|
2351
|
+
}
|
|
2352
|
+
function executeServiceCommandAttempt(command, args, trace, streamOutput = false) {
|
|
2353
|
+
return new Promise((resolve, reject) => {
|
|
2354
|
+
const child = child_process.spawn(command, args, {
|
|
2355
|
+
cwd: process.cwd(),
|
|
2356
|
+
detached: process.platform !== "win32",
|
|
2357
|
+
env: process.env,
|
|
2358
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
2359
|
+
});
|
|
2360
|
+
const { stdin } = process;
|
|
2361
|
+
const rawState = "isRaw" in stdin ? Boolean(stdin.isRaw) : false;
|
|
2362
|
+
let stopReason = null;
|
|
2363
|
+
let settled = false;
|
|
2364
|
+
let stdout2 = "";
|
|
2365
|
+
let stderr = "";
|
|
2366
|
+
let forceKillTimer;
|
|
2367
|
+
let lastStreamWriter;
|
|
2368
|
+
let needsTrailingNewline = false;
|
|
2369
|
+
const flushTrailingNewline = () => {
|
|
2370
|
+
if (!streamOutput || !needsTrailingNewline) {
|
|
2371
|
+
return;
|
|
2372
|
+
}
|
|
2373
|
+
(lastStreamWriter || process.stdout).write("\n");
|
|
2374
|
+
needsTrailingNewline = false;
|
|
2375
|
+
lastStreamWriter = void 0;
|
|
2376
|
+
};
|
|
2377
|
+
const cleanup = () => {
|
|
2378
|
+
stdin.removeListener("keypress", onKeyPress);
|
|
2379
|
+
if (forceKillTimer) {
|
|
2380
|
+
clearTimeout(forceKillTimer);
|
|
2381
|
+
}
|
|
2382
|
+
if (stdin.isTTY) {
|
|
2383
|
+
stdin.setRawMode(rawState);
|
|
2384
|
+
}
|
|
2385
|
+
stdin.pause();
|
|
2386
|
+
};
|
|
2387
|
+
const finish = (handler) => {
|
|
2388
|
+
if (settled) {
|
|
2389
|
+
return;
|
|
2390
|
+
}
|
|
2391
|
+
settled = true;
|
|
2392
|
+
flushTrailingNewline();
|
|
2393
|
+
cleanup();
|
|
2394
|
+
handler();
|
|
2395
|
+
};
|
|
2396
|
+
const requestStop = (reason) => {
|
|
2397
|
+
if (stopReason) {
|
|
2398
|
+
return;
|
|
2399
|
+
}
|
|
2400
|
+
stopReason = reason;
|
|
2401
|
+
trace(`service-command:${reason}:requested`, `${command} ${quoteArgs(args)}`);
|
|
2402
|
+
killServiceProcessTree(child, process.platform === "win32" ? void 0 : "SIGTERM");
|
|
2403
|
+
forceKillTimer = setTimeout(() => {
|
|
2404
|
+
if (!settled) {
|
|
2405
|
+
killServiceProcessTree(child, "SIGKILL");
|
|
2406
|
+
}
|
|
2407
|
+
}, 1500);
|
|
2408
|
+
forceKillTimer.unref?.();
|
|
2409
|
+
};
|
|
2410
|
+
const onKeyPress = (_input, key) => {
|
|
2411
|
+
if (key.name === "escape") {
|
|
2412
|
+
requestStop("cancel");
|
|
2413
|
+
}
|
|
2414
|
+
if (key.ctrl && key.name === "c") {
|
|
2415
|
+
requestStop("terminate");
|
|
2416
|
+
}
|
|
2417
|
+
};
|
|
2418
|
+
child.stdout?.setEncoding("utf8");
|
|
2419
|
+
child.stdout?.on("data", (chunk) => {
|
|
2420
|
+
stdout2 += chunk;
|
|
2421
|
+
writeStreamChunk(chunk, streamOutput, process.stdout, (nextNeedsTrailingNewline, nextWriter) => {
|
|
2422
|
+
needsTrailingNewline = nextNeedsTrailingNewline;
|
|
2423
|
+
lastStreamWriter = nextWriter;
|
|
2424
|
+
});
|
|
2425
|
+
});
|
|
2426
|
+
child.stderr?.setEncoding("utf8");
|
|
2427
|
+
child.stderr?.on("data", (chunk) => {
|
|
2428
|
+
stderr += chunk;
|
|
2429
|
+
writeStreamChunk(chunk, streamOutput, process.stderr, (nextNeedsTrailingNewline, nextWriter) => {
|
|
2430
|
+
needsTrailingNewline = nextNeedsTrailingNewline;
|
|
2431
|
+
lastStreamWriter = nextWriter;
|
|
2432
|
+
});
|
|
2433
|
+
});
|
|
2434
|
+
child.on("error", (error) => {
|
|
2435
|
+
finish(() => {
|
|
2436
|
+
if (stopReason === "cancel") {
|
|
2437
|
+
reject(createUserCancelledError("ESC \uC785\uB825\uC73C\uB85C \uD604\uC7AC AI \uC791\uC5C5\uC744 \uCDE8\uC18C\uD588\uC2B5\uB2C8\uB2E4."));
|
|
2438
|
+
return;
|
|
2439
|
+
}
|
|
2440
|
+
if (stopReason === "terminate") {
|
|
2441
|
+
reject(createUserTerminatedError("Ctrl+C \uC785\uB825\uC73C\uB85C \uC138\uC158 \uC885\uB8CC\uB97C \uC694\uCCAD\uD588\uC2B5\uB2C8\uB2E4."));
|
|
2442
|
+
return;
|
|
2443
|
+
}
|
|
2444
|
+
resolve({
|
|
2445
|
+
status: null,
|
|
2446
|
+
stderr: getErrorSummary(error),
|
|
2447
|
+
streamedOutput: streamOutput,
|
|
2448
|
+
stdout: stdout2
|
|
2449
|
+
});
|
|
2450
|
+
});
|
|
2451
|
+
});
|
|
2452
|
+
child.on("close", (status) => {
|
|
2453
|
+
finish(() => {
|
|
2454
|
+
if (stopReason === "cancel") {
|
|
2455
|
+
reject(createUserCancelledError("ESC \uC785\uB825\uC73C\uB85C \uD604\uC7AC AI \uC791\uC5C5\uC744 \uCDE8\uC18C\uD588\uC2B5\uB2C8\uB2E4."));
|
|
2456
|
+
return;
|
|
2457
|
+
}
|
|
2458
|
+
if (stopReason === "terminate") {
|
|
2459
|
+
reject(createUserTerminatedError("Ctrl+C \uC785\uB825\uC73C\uB85C \uC138\uC158 \uC885\uB8CC\uB97C \uC694\uCCAD\uD588\uC2B5\uB2C8\uB2E4."));
|
|
2460
|
+
return;
|
|
2461
|
+
}
|
|
2462
|
+
resolve({
|
|
2463
|
+
status,
|
|
2464
|
+
stderr,
|
|
2465
|
+
streamedOutput: streamOutput,
|
|
2466
|
+
stdout: stdout2
|
|
2467
|
+
});
|
|
2468
|
+
});
|
|
2469
|
+
});
|
|
2470
|
+
readline__default.default.emitKeypressEvents(stdin);
|
|
2471
|
+
if (stdin.isTTY) {
|
|
2472
|
+
stdin.setRawMode(true);
|
|
2473
|
+
}
|
|
2474
|
+
stdin.resume();
|
|
2475
|
+
stdin.on("keypress", onKeyPress);
|
|
2476
|
+
});
|
|
2477
|
+
}
|
|
2478
|
+
function createAttemptFailureError(service, failures) {
|
|
2479
|
+
const failureSummary = failures.map(({ attempt, result }, index) => {
|
|
2480
|
+
const status = result.status === null ? "spawn-error" : String(result.status);
|
|
2481
|
+
const excerpt = safeExcerpt(result.stderr || result.stdout || `exit status ${status}`, 1200);
|
|
2482
|
+
return [`[${index + 1}/${failures.length}] ${attempt.label}`, `status: ${status}`, excerpt].join("\n");
|
|
2483
|
+
}).join("\n\n");
|
|
2484
|
+
const lastFailure = failures.at(-1);
|
|
2485
|
+
const error = new Error(`\uBAA8\uB4E0 ${service} \uBAA8\uB378 \uC2DC\uB3C4\uAC00 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.
|
|
2486
|
+
|
|
2487
|
+
${failureSummary}`);
|
|
2488
|
+
Object.assign(error, {
|
|
2489
|
+
status: lastFailure?.result.status ?? 1,
|
|
2490
|
+
stderr: failureSummary,
|
|
2491
|
+
stdout: lastFailure?.result.stdout || ""
|
|
2492
|
+
});
|
|
2493
|
+
return error;
|
|
2494
|
+
}
|
|
2495
|
+
var DEFAULT_STREAM_OUTPUT_STATE = {
|
|
2496
|
+
enabled: false
|
|
2497
|
+
};
|
|
2498
|
+
var streamOutputRuntimeState = globalThis.__JU_HONG_E_STREAM_OUTPUT_STATE__ ?? (globalThis.__JU_HONG_E_STREAM_OUTPUT_STATE__ = {
|
|
2499
|
+
...DEFAULT_STREAM_OUTPUT_STATE
|
|
2500
|
+
});
|
|
2501
|
+
function normalizeStreamOutputState(value) {
|
|
2502
|
+
if (!value || typeof value !== "object") {
|
|
2503
|
+
return { ...DEFAULT_STREAM_OUTPUT_STATE };
|
|
2504
|
+
}
|
|
2505
|
+
const enabled = "enabled" in value ? Boolean(value.enabled) : DEFAULT_STREAM_OUTPUT_STATE.enabled;
|
|
2506
|
+
return {
|
|
2507
|
+
enabled
|
|
2508
|
+
};
|
|
2509
|
+
}
|
|
2510
|
+
function readStreamOutputConfig(paths) {
|
|
2511
|
+
if (!fs7__default.default.existsSync(paths.streamOutputConfigPath)) {
|
|
2512
|
+
return { ...DEFAULT_STREAM_OUTPUT_STATE };
|
|
2513
|
+
}
|
|
2514
|
+
try {
|
|
2515
|
+
const parsed = JSON.parse(fs7__default.default.readFileSync(paths.streamOutputConfigPath, "utf8"));
|
|
2516
|
+
return normalizeStreamOutputState(parsed);
|
|
2517
|
+
} catch {
|
|
2518
|
+
return { ...DEFAULT_STREAM_OUTPUT_STATE };
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
function hydrateStreamOutputState(paths) {
|
|
2522
|
+
const nextState = readStreamOutputConfig(paths);
|
|
2523
|
+
streamOutputRuntimeState.enabled = nextState.enabled;
|
|
2524
|
+
return streamOutputRuntimeState.enabled;
|
|
2525
|
+
}
|
|
2526
|
+
function isStreamOutputEnabled(paths) {
|
|
2527
|
+
if (paths) {
|
|
2528
|
+
return hydrateStreamOutputState(paths);
|
|
2529
|
+
}
|
|
2530
|
+
return streamOutputRuntimeState.enabled;
|
|
2531
|
+
}
|
|
2532
|
+
|
|
2533
|
+
// src/common/ai-services/index.ts
|
|
2534
|
+
async function runAIService(options) {
|
|
2535
|
+
const streamOutputEnabled = hydrateStreamOutputState(options.paths);
|
|
2536
|
+
const securityResult = await guardSecurityPayload({
|
|
2537
|
+
paths: options.paths,
|
|
2538
|
+
payload: {
|
|
2539
|
+
args: [],
|
|
2540
|
+
command: options.mode,
|
|
2541
|
+
input: "",
|
|
2542
|
+
prompt: options.prompt,
|
|
2543
|
+
promptRootDir: options.paths.rootDir,
|
|
2544
|
+
rawArgs: "",
|
|
2545
|
+
rawCommand: "",
|
|
2546
|
+
service: options.service
|
|
2547
|
+
},
|
|
2548
|
+
reportScope: `security:service:${options.mode}`,
|
|
2549
|
+
trace: options.trace,
|
|
2550
|
+
traceLabel: `service:${options.mode}`
|
|
2551
|
+
});
|
|
2552
|
+
if (!securityResult.ok) {
|
|
2553
|
+
options.trace("service-command:blocked", options.mode);
|
|
2554
|
+
return {
|
|
2555
|
+
blocked: true,
|
|
2556
|
+
cliAvailable: true,
|
|
2557
|
+
exitCode: 0,
|
|
2558
|
+
preview: "",
|
|
2559
|
+
service: options.service,
|
|
2560
|
+
skipped: true,
|
|
2561
|
+
stderr: "",
|
|
2562
|
+
streamedOutput: false,
|
|
2563
|
+
stdout: securityResult.userMessage || ""
|
|
2564
|
+
};
|
|
2565
|
+
}
|
|
2566
|
+
const cliAvailable = ensureServiceCli(options.service, options.parsedArgs, options.trace);
|
|
2567
|
+
const commandSpec = buildServiceCommand({
|
|
2568
|
+
mode: options.mode,
|
|
2569
|
+
parsedArgs: options.parsedArgs,
|
|
2570
|
+
prompt: options.prompt,
|
|
2571
|
+
service: options.service
|
|
2572
|
+
});
|
|
2573
|
+
options.trace("service-command:prepared", commandSpec.preview);
|
|
2574
|
+
if (options.parsedArgs.testMode || !cliAvailable) {
|
|
2575
|
+
options.trace("service-command:skipped", options.parsedArgs.testMode ? "test-mode" : "cli-missing");
|
|
2576
|
+
return {
|
|
2577
|
+
blocked: false,
|
|
2578
|
+
cliAvailable,
|
|
2579
|
+
exitCode: 0,
|
|
2580
|
+
preview: commandSpec.preview,
|
|
2581
|
+
service: options.service,
|
|
2582
|
+
skipped: true,
|
|
2583
|
+
stderr: "",
|
|
2584
|
+
streamedOutput: false,
|
|
2585
|
+
stdout: [
|
|
2586
|
+
"[TEST MODE] \uC2E4\uC81C AI \uD638\uCD9C\uC740 \uC0DD\uB7B5\uB418\uC5C8\uC2B5\uB2C8\uB2E4.",
|
|
2587
|
+
"",
|
|
2588
|
+
`service: ${options.service}`,
|
|
2589
|
+
`cliAvailable: ${cliAvailable}`,
|
|
2590
|
+
"",
|
|
2591
|
+
"\uC0DD\uC131\uB420 \uBA85\uB839\uC5B4 \uBBF8\uB9AC\uBCF4\uAE30:",
|
|
2592
|
+
commandSpec.preview
|
|
2593
|
+
].join("\n")
|
|
2594
|
+
};
|
|
2595
|
+
}
|
|
2596
|
+
options.trace("service-command:exec:start", `attempts=${commandSpec.attempts.length}`);
|
|
2597
|
+
const failures = [];
|
|
2598
|
+
for (const [index, attempt] of commandSpec.attempts.entries()) {
|
|
2599
|
+
const attemptOrder = `${index + 1}/${commandSpec.attempts.length}`;
|
|
2600
|
+
options.trace("service-command:attempt:start", `${attemptOrder} ${attempt.label}`);
|
|
2601
|
+
let result;
|
|
2602
|
+
try {
|
|
2603
|
+
result = await executeServiceCommandAttempt(
|
|
2604
|
+
commandSpec.command,
|
|
2605
|
+
attempt.args,
|
|
2606
|
+
options.trace,
|
|
2607
|
+
streamOutputEnabled
|
|
2608
|
+
);
|
|
2609
|
+
} catch (error) {
|
|
2610
|
+
if (isUserCancelledError(error) || isUserTerminatedError(error)) {
|
|
2611
|
+
options.trace(
|
|
2612
|
+
isUserTerminatedError(error) ? "service-command:terminated" : "service-command:cancelled",
|
|
2613
|
+
`${attemptOrder} ${attempt.label}`
|
|
2614
|
+
);
|
|
2615
|
+
}
|
|
2616
|
+
throw error;
|
|
2617
|
+
}
|
|
2618
|
+
options.trace(
|
|
2619
|
+
"service-command:attempt:end",
|
|
2620
|
+
`${attemptOrder} ${attempt.label} status=${result.status ?? "spawn-error"}`
|
|
2621
|
+
);
|
|
2622
|
+
if (result.status === 0) {
|
|
2623
|
+
options.trace("service-command:exec:end", `status=0 attempt=${attemptOrder} ${attempt.label}`);
|
|
2624
|
+
return {
|
|
2625
|
+
blocked: false,
|
|
2626
|
+
cliAvailable,
|
|
2627
|
+
exitCode: result.status ?? 0,
|
|
2628
|
+
preview: commandSpec.preview,
|
|
2629
|
+
service: options.service,
|
|
2630
|
+
skipped: false,
|
|
2631
|
+
stderr: result.stderr || "",
|
|
2632
|
+
streamedOutput: result.streamedOutput,
|
|
2633
|
+
stdout: result.stdout || ""
|
|
2634
|
+
};
|
|
2635
|
+
}
|
|
2636
|
+
failures.push({ attempt, result });
|
|
2637
|
+
options.trace(
|
|
2638
|
+
"service-command:attempt:failed",
|
|
2639
|
+
`${attemptOrder} ${attempt.label} ${safeExcerpt(result.stderr || result.stdout || "Unknown failure", 240)}`
|
|
2640
|
+
);
|
|
2641
|
+
}
|
|
2642
|
+
options.trace("service-command:exec:end", `status=${failures.at(-1)?.result.status ?? 1}`);
|
|
2643
|
+
throw createAttemptFailureError(options.service, failures);
|
|
2644
|
+
}
|
|
2645
|
+
|
|
2646
|
+
// src/common/actions/shared.ts
|
|
2647
|
+
function formatActionError(error) {
|
|
2648
|
+
return getErrorSummary(error);
|
|
2649
|
+
}
|
|
2650
|
+
async function runAIServiceWithStatus(options) {
|
|
2651
|
+
if (isStreamOutputEnabled(options.paths)) {
|
|
2652
|
+
options.status.step(options.streamingMessage || `${options.loadingMessage} - \uC2E4\uC2DC\uAC04 \uC751\uB2F5\uC744 \uADF8\uB300\uB85C \uCD9C\uB825\uD569\uB2C8\uB2E4.`);
|
|
2653
|
+
return runAIService({
|
|
2654
|
+
mode: options.mode,
|
|
2655
|
+
parsedArgs: options.parsedArgs,
|
|
2656
|
+
paths: options.paths,
|
|
2657
|
+
prompt: options.prompt,
|
|
2658
|
+
service: options.service,
|
|
2659
|
+
trace: options.trace
|
|
2660
|
+
});
|
|
2661
|
+
}
|
|
2662
|
+
return options.status.track(
|
|
2663
|
+
options.loadingMessage,
|
|
2664
|
+
() => runAIService({
|
|
2665
|
+
mode: options.mode,
|
|
2666
|
+
parsedArgs: options.parsedArgs,
|
|
2667
|
+
paths: options.paths,
|
|
2668
|
+
prompt: options.prompt,
|
|
2669
|
+
service: options.service,
|
|
2670
|
+
trace: options.trace
|
|
2671
|
+
})
|
|
2672
|
+
);
|
|
2673
|
+
}
|
|
2674
|
+
function printBufferedAIOutput(output, result, fallback = "(\uC751\uB2F5 \uC5C6\uC74C)") {
|
|
2675
|
+
if (result.streamedOutput) {
|
|
2676
|
+
return;
|
|
2677
|
+
}
|
|
2678
|
+
console.log(output || fallback);
|
|
2679
|
+
}
|
|
2680
|
+
|
|
2681
|
+
// src/common/actions/chat.ts
|
|
2682
|
+
async function runChatAction(options) {
|
|
2683
|
+
const securityResult = await guardSecurityPayload({
|
|
2684
|
+
paths: options.paths,
|
|
2685
|
+
payload: {
|
|
2686
|
+
args: [],
|
|
2687
|
+
command: "chat",
|
|
2688
|
+
input: options.input,
|
|
2689
|
+
rawArgs: "",
|
|
2690
|
+
rawCommand: options.input,
|
|
2691
|
+
service: options.service
|
|
2692
|
+
},
|
|
2693
|
+
reportScope: "security:chat",
|
|
2694
|
+
trace: options.trace,
|
|
2695
|
+
traceLabel: "chat"
|
|
2696
|
+
});
|
|
2697
|
+
if (!securityResult.ok) {
|
|
2698
|
+
return void 0;
|
|
2699
|
+
}
|
|
2700
|
+
const status = createSessionActionStatus("\uB300\uD654");
|
|
2701
|
+
status.step("1/4 \uB300\uD654 \uCEE8\uD14D\uC2A4\uD2B8\uC640 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uC0C1\uD0DC\uB97C \uC900\uBE44\uD558\uB294 \uC911\uC785\uB2C8\uB2E4.");
|
|
2702
|
+
const workspaceBefore = captureWorkspaceSnapshot(options.paths.rootDir);
|
|
2703
|
+
const historyContext = loadRecentHistoryContext(options.paths);
|
|
2704
|
+
status.step("2/4 AI \uC694\uCCAD \uD504\uB86C\uD504\uD2B8\uB97C \uAD6C\uC131\uD558\uB294 \uC911\uC785\uB2C8\uB2E4.");
|
|
2705
|
+
const prompt = buildChatPrompt({
|
|
2706
|
+
historyContext,
|
|
2707
|
+
service: options.service,
|
|
2708
|
+
userInput: options.input,
|
|
2709
|
+
workspaceSnapshot: workspaceBefore
|
|
2710
|
+
});
|
|
2711
|
+
const result = await runAIServiceWithStatus({
|
|
2712
|
+
loadingMessage: `3/4 ${options.service} \uC751\uB2F5\uC744 \uAE30\uB2E4\uB9AC\uB294 \uC911\uC785\uB2C8\uB2E4`,
|
|
2713
|
+
mode: "chat",
|
|
2714
|
+
parsedArgs: options.parsedArgs,
|
|
2715
|
+
paths: options.paths,
|
|
2716
|
+
prompt,
|
|
2717
|
+
service: options.service,
|
|
2718
|
+
status,
|
|
2719
|
+
streamingMessage: `3/4 ${options.service} \uC2E4\uC2DC\uAC04 \uC751\uB2F5\uC744 \uCD9C\uB825\uD558\uB294 \uC911\uC785\uB2C8\uB2E4. ESC \uCDE8\uC18C, Ctrl+C \uC138\uC158 \uC885\uB8CC`,
|
|
2720
|
+
trace: options.trace
|
|
2721
|
+
});
|
|
2722
|
+
if (result.blocked) {
|
|
2723
|
+
status.step("4/4 \uBCF4\uC548 \uC815\uCC45\uC73C\uB85C \uC778\uD574 AI \uD638\uCD9C\uC744 \uC911\uB2E8\uD588\uC2B5\uB2C8\uB2E4.");
|
|
2724
|
+
return void 0;
|
|
2725
|
+
}
|
|
2726
|
+
status.step("4/4 \uC751\uB2F5\uC744 \uBC1B\uC558\uC2B5\uB2C8\uB2E4. \uC791\uC5C5 \uC774\uB825\uC744 \uC800\uC7A5\uD558\uB294 \uC911\uC785\uB2C8\uB2E4.");
|
|
2727
|
+
const workspaceAfter = captureWorkspaceSnapshot(options.paths.rootDir);
|
|
2728
|
+
const workspaceDelta = diffWorkspaceSnapshots(workspaceBefore, workspaceAfter);
|
|
2729
|
+
const historyPath = writeTurnHistory(options.paths, {
|
|
2730
|
+
historyContext,
|
|
2731
|
+
input: options.input,
|
|
2732
|
+
output: result.stdout,
|
|
2733
|
+
previewCommand: result.preview,
|
|
2734
|
+
scope: "chat",
|
|
2735
|
+
service: options.service,
|
|
2736
|
+
skipped: result.skipped,
|
|
2737
|
+
title: `\uB300\uD654: ${safeExcerpt(options.input, 80)}`,
|
|
2738
|
+
workspaceAfter,
|
|
2739
|
+
workspaceBefore,
|
|
2740
|
+
workspaceDelta
|
|
2741
|
+
});
|
|
2742
|
+
appendConversationHistory(options.paths, {
|
|
2743
|
+
input: options.input,
|
|
2744
|
+
output: result.stdout,
|
|
2745
|
+
scope: "chat",
|
|
2746
|
+
service: options.service,
|
|
2747
|
+
summary: `\uB300\uD654 \uCC98\uB9AC \uC644\uB8CC: ${safeExcerpt(options.input, 60)}`,
|
|
2748
|
+
title: `\uB300\uD654: ${safeExcerpt(options.input, 80)}`
|
|
2749
|
+
});
|
|
2750
|
+
printBufferedAIOutput(result.stdout, result);
|
|
2751
|
+
return {
|
|
2752
|
+
historyPath,
|
|
2753
|
+
previewCommand: result.preview,
|
|
2754
|
+
scope: "chat",
|
|
2755
|
+
service: options.service,
|
|
2756
|
+
skipped: result.skipped,
|
|
2757
|
+
summary: `\uB300\uD654 \uCC98\uB9AC \uC644\uB8CC: ${safeExcerpt(options.input, 60)}`,
|
|
2758
|
+
title: "\uB300\uD654",
|
|
2759
|
+
traceMessages: []
|
|
2760
|
+
};
|
|
2761
|
+
}
|
|
2762
|
+
|
|
2763
|
+
// src/common/command-runtime/help.ts
|
|
2764
|
+
function formatSlashCommands(commands) {
|
|
2765
|
+
return commands.map((command) => ` ${command.command.padEnd(24)} ${command.description}`).join("\n");
|
|
2766
|
+
}
|
|
2767
|
+
function createCliHelpText(commands, currentService, binaryName = "ju-hong-e") {
|
|
2768
|
+
const serviceSection = "";
|
|
2769
|
+
return `
|
|
2770
|
+
Usage: ${binaryName} [options]
|
|
2771
|
+
|
|
2772
|
+
Options:
|
|
2773
|
+
--service <codex|gemini|claude> \uC2DC\uC791 \uC11C\uBE44\uC2A4 \uC9C0\uC815
|
|
2774
|
+
--model <name> \uC11C\uBE44\uC2A4\uBCC4 \uBAA8\uB378 \uC9C0\uC815
|
|
2775
|
+
--reasoning-effort <level> minimal|low|medium|high
|
|
2776
|
+
--test \uC2E4\uC81C AI \uD638\uCD9C \uC5C6\uC774 \uC804\uCCB4 \uD750\uB984 \uD14C\uC2A4\uD2B8
|
|
2777
|
+
--trace trace \uB85C\uADF8\uB97C \uCF58\uC194\uC5D0 \uCD9C\uB825
|
|
2778
|
+
--help \uB3C4\uC6C0\uB9D0 \uCD9C\uB825
|
|
2779
|
+
|
|
2780
|
+
Slash Commands:
|
|
2781
|
+
${formatSlashCommands(commands)}
|
|
2782
|
+
|
|
2783
|
+
Service Native Slash Commands:
|
|
2784
|
+
/<command> ... \uB0B4\uC7A5 \uBA85\uB839\uC774 \uC544\uB2C8\uBA74 \uD604\uC7AC \uC11C\uBE44\uC2A4 CLI\uC758 slash command\uB85C \uC704\uC784
|
|
2785
|
+
[service] /<command> ... \uC774\uB984\uC774 \uACB9\uCE58\uB294 \uACBD\uC6B0 \uD604\uC7AC \uC11C\uBE44\uC2A4 native command\uB97C \uBA85\uC2DC\uC801\uC73C\uB85C \uC2E4\uD589
|
|
2786
|
+
|
|
2787
|
+
Shortcuts:
|
|
2788
|
+
ESC \uD604\uC7AC \uC791\uC5C5 \uB610\uB294 \uC785\uB825\uB9CC \uCDE8\uC18C
|
|
2789
|
+
Ctrl+C \uC138\uC158 \uC885\uB8CC
|
|
2790
|
+
Shift+Up/Down \uC785\uB825 \uD788\uC2A4\uD1A0\uB9AC \uC774\uB3D9
|
|
2791
|
+
|
|
2792
|
+
${serviceSection}`.trim();
|
|
2793
|
+
}
|
|
2794
|
+
|
|
2795
|
+
// src/common/runtime-profile.ts
|
|
2796
|
+
var runtimeState = globalThis.__JU_HONG_E_RUNTIME_PROFILE__ ?? (globalThis.__JU_HONG_E_RUNTIME_PROFILE__ = {
|
|
2797
|
+
binaryName: "ju-hong-e",
|
|
2798
|
+
profile: "all"
|
|
2799
|
+
});
|
|
2800
|
+
var ALL_PROFILE_CONFIGS = {
|
|
2801
|
+
all: {
|
|
2802
|
+
enableLocalServer: true
|
|
2803
|
+
},
|
|
2804
|
+
be: {
|
|
2805
|
+
allowedBuiltinCommands: /* @__PURE__ */ new Set(["/review", "/commit", "/change-service", "/security-policy", "/show-stream", "/help", "/exit"]),
|
|
2806
|
+
enableLocalServer: false
|
|
2807
|
+
},
|
|
2808
|
+
fe: {
|
|
2809
|
+
enableLocalServer: true
|
|
2810
|
+
}
|
|
2811
|
+
};
|
|
2812
|
+
function getProfileConfig(profile = runtimeState.profile) {
|
|
2813
|
+
return ALL_PROFILE_CONFIGS[profile];
|
|
2814
|
+
}
|
|
2815
|
+
function configureCliRuntime(options) {
|
|
2816
|
+
runtimeState.binaryName = options.binaryName;
|
|
2817
|
+
runtimeState.profile = options.profile;
|
|
2818
|
+
}
|
|
2819
|
+
function shouldEnableLocalServer(profile = runtimeState.profile) {
|
|
2820
|
+
return getProfileConfig(profile).enableLocalServer;
|
|
2821
|
+
}
|
|
2822
|
+
function getAllowedBuiltinCommands(profile = runtimeState.profile) {
|
|
2823
|
+
return getProfileConfig(profile).allowedBuiltinCommands;
|
|
2824
|
+
}
|
|
2825
|
+
|
|
2826
|
+
// src/common/actions/native-command.ts
|
|
2827
|
+
async function runNativeCommandAction(options) {
|
|
2828
|
+
const status = createSessionActionStatus("\uB124\uC774\uD2F0\uBE0C \uBA85\uB839");
|
|
2829
|
+
status.step("1/4 \uC11C\uBE44\uC2A4 \uB124\uC774\uD2F0\uBE0C \uBA85\uB839 \uC2E4\uD589\uC744 \uC900\uBE44\uD558\uB294 \uC911\uC785\uB2C8\uB2E4.");
|
|
2830
|
+
const workspaceBefore = captureWorkspaceSnapshot(options.paths.rootDir);
|
|
2831
|
+
const historyContext = loadRecentHistoryContext(options.paths);
|
|
2832
|
+
status.step("2/4 \uC120\uD0DD\uB41C \uC11C\uBE44\uC2A4 CLI\uB85C \uC6D0\uBCF8 slash command\uB97C \uC804\uB2EC\uD558\uB294 \uC911\uC785\uB2C8\uB2E4.");
|
|
2833
|
+
const result = await runAIServiceWithStatus({
|
|
2834
|
+
loadingMessage: `3/4 ${options.service} \uB124\uC774\uD2F0\uBE0C \uBA85\uB839 \uC751\uB2F5\uC744 \uAE30\uB2E4\uB9AC\uB294 \uC911\uC785\uB2C8\uB2E4`,
|
|
2835
|
+
mode: "native-command",
|
|
2836
|
+
parsedArgs: options.parsedArgs,
|
|
2837
|
+
paths: options.paths,
|
|
2838
|
+
prompt: options.rawCommand,
|
|
2839
|
+
service: options.service,
|
|
2840
|
+
status,
|
|
2841
|
+
streamingMessage: `3/4 ${options.service} \uB124\uC774\uD2F0\uBE0C \uBA85\uB839 \uC2E4\uC2DC\uAC04 \uC751\uB2F5\uC744 \uCD9C\uB825\uD558\uB294 \uC911\uC785\uB2C8\uB2E4. ESC \uCDE8\uC18C, Ctrl+C \uC138\uC158 \uC885\uB8CC`,
|
|
2842
|
+
trace: options.trace
|
|
2843
|
+
});
|
|
2844
|
+
if (result.blocked) {
|
|
2845
|
+
status.step("4/4 \uBCF4\uC548 \uC815\uCC45\uC73C\uB85C \uC778\uD574 \uB124\uC774\uD2F0\uBE0C \uBA85\uB839 \uC2E4\uD589\uC744 \uC911\uB2E8\uD588\uC2B5\uB2C8\uB2E4.");
|
|
2846
|
+
return void 0;
|
|
2847
|
+
}
|
|
2848
|
+
status.step("4/4 \uC751\uB2F5\uACFC \uC791\uC5C5 \uC774\uB825\uC744 \uC800\uC7A5\uD558\uB294 \uC911\uC785\uB2C8\uB2E4.");
|
|
2849
|
+
const workspaceAfter = captureWorkspaceSnapshot(options.paths.rootDir);
|
|
2850
|
+
const workspaceDelta = diffWorkspaceSnapshots(workspaceBefore, workspaceAfter);
|
|
2851
|
+
const output = result.stdout || result.stderr || "(\uC751\uB2F5 \uC5C6\uC74C)";
|
|
2852
|
+
const historyPath = writeTurnHistory(options.paths, {
|
|
2853
|
+
extraSections: [
|
|
2854
|
+
{
|
|
2855
|
+
heading: "\uC11C\uBE44\uC2A4 \uB124\uC774\uD2F0\uBE0C \uBA85\uB839",
|
|
2856
|
+
markdown: [`- \uC0AC\uC6A9\uC790 \uC785\uB825: \`${options.input}\``, `- \uC2E4\uC81C \uC804\uB2EC \uBA85\uB839: \`${options.rawCommand}\``].join("\n")
|
|
2857
|
+
}
|
|
2858
|
+
],
|
|
2859
|
+
historyContext,
|
|
2860
|
+
input: options.input,
|
|
2861
|
+
output,
|
|
2862
|
+
previewCommand: result.preview,
|
|
2863
|
+
scope: "native-command",
|
|
2864
|
+
service: options.service,
|
|
2865
|
+
skipped: result.skipped,
|
|
2866
|
+
title: `\uB124\uC774\uD2F0\uBE0C \uBA85\uB839: ${safeExcerpt(options.rawCommand, 80)}`,
|
|
2867
|
+
workspaceAfter,
|
|
2868
|
+
workspaceBefore,
|
|
2869
|
+
workspaceDelta
|
|
2870
|
+
});
|
|
2871
|
+
appendConversationHistory(options.paths, {
|
|
2872
|
+
input: options.input,
|
|
2873
|
+
output,
|
|
2874
|
+
scope: "native-command",
|
|
2875
|
+
service: options.service,
|
|
2876
|
+
summary: `\uC11C\uBE44\uC2A4 \uB124\uC774\uD2F0\uBE0C \uBA85\uB839 \uC2E4\uD589: ${safeExcerpt(options.rawCommand, 60)}`,
|
|
2877
|
+
title: `\uB124\uC774\uD2F0\uBE0C \uBA85\uB839: ${safeExcerpt(options.rawCommand, 80)}`
|
|
2878
|
+
});
|
|
2879
|
+
printBufferedAIOutput(output, result);
|
|
2880
|
+
return {
|
|
2881
|
+
historyPath,
|
|
2882
|
+
previewCommand: result.preview,
|
|
2883
|
+
scope: "native-command",
|
|
2884
|
+
service: options.service,
|
|
2885
|
+
skipped: result.skipped,
|
|
2886
|
+
summary: `\uC11C\uBE44\uC2A4 \uB124\uC774\uD2F0\uBE0C \uBA85\uB839 \uC2E4\uD589: ${safeExcerpt(options.rawCommand, 60)}`,
|
|
2887
|
+
title: "\uB124\uC774\uD2F0\uBE0C \uBA85\uB839",
|
|
2888
|
+
traceMessages: []
|
|
2889
|
+
};
|
|
2890
|
+
}
|
|
2891
|
+
|
|
2892
|
+
// src/common/command-runtime/registry.ts
|
|
2893
|
+
var registryState = globalThis.__JU_HONG_E_COMMAND_REGISTRY__ ?? (globalThis.__JU_HONG_E_COMMAND_REGISTRY__ = {
|
|
2894
|
+
juHongECommands: /* @__PURE__ */ new Map(),
|
|
2895
|
+
nativeCommands: /* @__PURE__ */ new Map()
|
|
2896
|
+
});
|
|
2897
|
+
var JU_HONG_E_COMMANDS = registryState.juHongECommands;
|
|
2898
|
+
var NATIVE_COMMANDS = registryState.nativeCommands;
|
|
2899
|
+
function sortByOrder(commands) {
|
|
2900
|
+
return [...commands].sort((left, right) => {
|
|
2901
|
+
const leftOrder = left.order ?? Number.MAX_SAFE_INTEGER;
|
|
2902
|
+
const rightOrder = right.order ?? Number.MAX_SAFE_INTEGER;
|
|
2903
|
+
if (leftOrder !== rightOrder) {
|
|
2904
|
+
return leftOrder - rightOrder;
|
|
2905
|
+
}
|
|
2906
|
+
return left.command.localeCompare(right.command);
|
|
2907
|
+
});
|
|
2908
|
+
}
|
|
2909
|
+
function toBuiltinSlashCommandOption(command) {
|
|
2910
|
+
return {
|
|
2911
|
+
command: command.command,
|
|
2912
|
+
description: command.description,
|
|
2913
|
+
insertValue: command.insertValue || command.command,
|
|
2914
|
+
matchKeywords: command.matchKeywords
|
|
2915
|
+
};
|
|
2916
|
+
}
|
|
2917
|
+
function toNativeSlashCommandOption(service, command) {
|
|
2918
|
+
const prefixedCommand = `[${service}] ${command.command}`;
|
|
2919
|
+
return {
|
|
2920
|
+
command: prefixedCommand,
|
|
2921
|
+
description: command.description,
|
|
2922
|
+
insertValue: prefixedCommand,
|
|
2923
|
+
matchKeywords: [command.command, prefixedCommand, ...command.matchKeywords || []]
|
|
2924
|
+
};
|
|
2925
|
+
}
|
|
2926
|
+
function resolveTelemetryStatusFromError(error) {
|
|
2927
|
+
if (isUserCancelledError(error)) {
|
|
2928
|
+
return "cancelled";
|
|
2929
|
+
}
|
|
2930
|
+
if (isUserTerminatedError(error)) {
|
|
2931
|
+
return "terminated";
|
|
2932
|
+
}
|
|
2933
|
+
return "error";
|
|
2934
|
+
}
|
|
2935
|
+
async function validateCommand(context, security) {
|
|
2936
|
+
const result = await guardSecurityPayload({
|
|
2937
|
+
config: security,
|
|
2938
|
+
paths: context.paths,
|
|
2939
|
+
payload: {
|
|
2940
|
+
args: context.args,
|
|
2941
|
+
command: context.command,
|
|
2942
|
+
input: context.input,
|
|
2943
|
+
rawArgs: context.rawArgs,
|
|
2944
|
+
rawCommand: context.rawCommand,
|
|
2945
|
+
requestedService: "requestedService" in context ? context.requestedService : void 0,
|
|
2946
|
+
service: context.service
|
|
2947
|
+
},
|
|
2948
|
+
reportScope: `security:${context.command}`,
|
|
2949
|
+
trace: context.trace,
|
|
2950
|
+
traceLabel: context.command
|
|
2951
|
+
});
|
|
2952
|
+
return result.ok;
|
|
2953
|
+
}
|
|
2954
|
+
function clearCommandRegistry() {
|
|
2955
|
+
JU_HONG_E_COMMANDS.clear();
|
|
2956
|
+
NATIVE_COMMANDS.clear();
|
|
2957
|
+
}
|
|
2958
|
+
function applyBuiltinCommandFilter(allowedCommands) {
|
|
2959
|
+
if (!allowedCommands) {
|
|
2960
|
+
return;
|
|
2961
|
+
}
|
|
2962
|
+
for (const commandName of [...JU_HONG_E_COMMANDS.keys()]) {
|
|
2963
|
+
if (!allowedCommands.has(commandName)) {
|
|
2964
|
+
JU_HONG_E_COMMANDS.delete(commandName);
|
|
2965
|
+
}
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
function getBuiltinCommands() {
|
|
2969
|
+
return sortByOrder([...JU_HONG_E_COMMANDS.values()]);
|
|
2970
|
+
}
|
|
2971
|
+
function getBuiltinCommandNames() {
|
|
2972
|
+
return getBuiltinCommands().map((command) => command.command);
|
|
2973
|
+
}
|
|
2974
|
+
function getNativeCommands(service) {
|
|
2975
|
+
return sortByOrder([...NATIVE_COMMANDS.get(service)?.values() || []]);
|
|
2976
|
+
}
|
|
2977
|
+
function getBuiltinSlashCommands() {
|
|
2978
|
+
return getBuiltinCommands().map(toBuiltinSlashCommandOption);
|
|
2979
|
+
}
|
|
2980
|
+
function getSessionCommandCatalog(service) {
|
|
2981
|
+
const builtinCommands = getBuiltinCommands();
|
|
2982
|
+
const nativeCommands = getNativeCommands(service);
|
|
2983
|
+
return {
|
|
2984
|
+
builtinCommands,
|
|
2985
|
+
nativeCommands,
|
|
2986
|
+
slashCommands: [...builtinCommands.map(toBuiltinSlashCommandOption), ...nativeCommands.map((command) => toNativeSlashCommandOption(service, command))]
|
|
2987
|
+
};
|
|
2988
|
+
}
|
|
2989
|
+
function findJuHongECommand(command) {
|
|
2990
|
+
return JU_HONG_E_COMMANDS.get(command);
|
|
2991
|
+
}
|
|
2992
|
+
function findNativeCommand(service, command) {
|
|
2993
|
+
return NATIVE_COMMANDS.get(service)?.get(command);
|
|
2994
|
+
}
|
|
2995
|
+
async function executeJuHongECommand(definition, context) {
|
|
2996
|
+
beginSlashCommandTelemetryInputCollection(context.args);
|
|
2997
|
+
const resolveTelemetryArgs = () => endSlashCommandTelemetryInputCollection();
|
|
2998
|
+
try {
|
|
2999
|
+
const canExecute = await validateCommand(context, definition.security);
|
|
3000
|
+
if (!canExecute) {
|
|
3001
|
+
trackSlashCommandExecution({
|
|
3002
|
+
args: resolveTelemetryArgs(),
|
|
3003
|
+
command: context.command,
|
|
3004
|
+
kind: "builtin",
|
|
3005
|
+
rootDir: context.paths.rootDir,
|
|
3006
|
+
service: context.service,
|
|
3007
|
+
status: "blocked",
|
|
3008
|
+
trace: context.trace
|
|
3009
|
+
});
|
|
3010
|
+
return {};
|
|
3011
|
+
}
|
|
3012
|
+
const result = await definition.execute({
|
|
3013
|
+
...context,
|
|
3014
|
+
definition
|
|
3015
|
+
});
|
|
3016
|
+
trackSlashCommandExecution({
|
|
3017
|
+
args: resolveTelemetryArgs(),
|
|
3018
|
+
command: context.command,
|
|
3019
|
+
kind: "builtin",
|
|
3020
|
+
rootDir: context.paths.rootDir,
|
|
3021
|
+
service: context.service,
|
|
3022
|
+
status: "success",
|
|
3023
|
+
trace: context.trace
|
|
3024
|
+
});
|
|
3025
|
+
return result;
|
|
3026
|
+
} catch (error) {
|
|
3027
|
+
trackSlashCommandExecution({
|
|
3028
|
+
args: resolveTelemetryArgs(),
|
|
3029
|
+
command: context.command,
|
|
3030
|
+
kind: "builtin",
|
|
3031
|
+
rootDir: context.paths.rootDir,
|
|
3032
|
+
service: context.service,
|
|
3033
|
+
status: resolveTelemetryStatusFromError(error),
|
|
3034
|
+
trace: context.trace
|
|
3035
|
+
});
|
|
3036
|
+
throw error;
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
async function executeNativeCommand(definition, context) {
|
|
3040
|
+
beginSlashCommandTelemetryInputCollection(context.args);
|
|
3041
|
+
const resolveTelemetryArgs = () => endSlashCommandTelemetryInputCollection();
|
|
3042
|
+
try {
|
|
3043
|
+
const canExecute = await validateCommand(context, definition?.security);
|
|
3044
|
+
if (!canExecute) {
|
|
3045
|
+
trackSlashCommandExecution({
|
|
3046
|
+
args: resolveTelemetryArgs(),
|
|
3047
|
+
command: context.command,
|
|
3048
|
+
kind: "native",
|
|
3049
|
+
requestedService: context.requestedService,
|
|
3050
|
+
rootDir: context.paths.rootDir,
|
|
3051
|
+
service: context.service,
|
|
3052
|
+
status: "blocked",
|
|
3053
|
+
trace: context.trace
|
|
3054
|
+
});
|
|
3055
|
+
return {};
|
|
3056
|
+
}
|
|
3057
|
+
if (definition) {
|
|
3058
|
+
const result = await definition.execute(context);
|
|
3059
|
+
trackSlashCommandExecution({
|
|
3060
|
+
args: resolveTelemetryArgs(),
|
|
3061
|
+
command: context.command,
|
|
3062
|
+
kind: "native",
|
|
3063
|
+
requestedService: context.requestedService,
|
|
3064
|
+
rootDir: context.paths.rootDir,
|
|
3065
|
+
service: context.service,
|
|
3066
|
+
status: "success",
|
|
3067
|
+
trace: context.trace
|
|
3068
|
+
});
|
|
3069
|
+
return result;
|
|
3070
|
+
}
|
|
3071
|
+
const record = await runNativeCommandAction({
|
|
3072
|
+
input: context.input,
|
|
3073
|
+
parsedArgs: context.parsedArgs,
|
|
3074
|
+
paths: context.paths,
|
|
3075
|
+
rawCommand: context.rawCommand,
|
|
3076
|
+
service: context.service,
|
|
3077
|
+
trace: context.trace
|
|
3078
|
+
});
|
|
3079
|
+
trackSlashCommandExecution({
|
|
3080
|
+
args: resolveTelemetryArgs(),
|
|
3081
|
+
command: context.command,
|
|
3082
|
+
kind: "native",
|
|
3083
|
+
requestedService: context.requestedService,
|
|
3084
|
+
rootDir: context.paths.rootDir,
|
|
3085
|
+
service: context.service,
|
|
3086
|
+
status: "success",
|
|
3087
|
+
trace: context.trace
|
|
3088
|
+
});
|
|
3089
|
+
return {
|
|
3090
|
+
record
|
|
3091
|
+
};
|
|
3092
|
+
} catch (error) {
|
|
3093
|
+
trackSlashCommandExecution({
|
|
3094
|
+
args: resolveTelemetryArgs(),
|
|
3095
|
+
command: context.command,
|
|
3096
|
+
kind: "native",
|
|
3097
|
+
requestedService: context.requestedService,
|
|
3098
|
+
rootDir: context.paths.rootDir,
|
|
3099
|
+
service: context.service,
|
|
3100
|
+
status: resolveTelemetryStatusFromError(error),
|
|
3101
|
+
trace: context.trace
|
|
3102
|
+
});
|
|
3103
|
+
throw error;
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
|
|
3107
|
+
// src/common/command-runtime/loader.ts
|
|
3108
|
+
var loadPromise;
|
|
3109
|
+
async function collectCommandModulePaths(directoryPath) {
|
|
3110
|
+
const entries = await fs2__default.default.readdir(directoryPath, { withFileTypes: true });
|
|
3111
|
+
const modulePaths = [];
|
|
3112
|
+
for (const entry of entries) {
|
|
3113
|
+
const nextPath = path6__default.default.join(directoryPath, entry.name);
|
|
3114
|
+
if (entry.isDirectory()) {
|
|
3115
|
+
modulePaths.push(...await collectCommandModulePaths(nextPath));
|
|
3116
|
+
continue;
|
|
3117
|
+
}
|
|
3118
|
+
if (entry.isFile() && entry.name === "index.js") {
|
|
3119
|
+
modulePaths.push(nextPath);
|
|
3120
|
+
}
|
|
3121
|
+
}
|
|
3122
|
+
return modulePaths;
|
|
3123
|
+
}
|
|
3124
|
+
async function isCommandModuleRootDirectory(directoryPath) {
|
|
3125
|
+
try {
|
|
3126
|
+
const entries = await fs2__default.default.readdir(directoryPath, { withFileTypes: true });
|
|
3127
|
+
const directoryNames = new Set(entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name));
|
|
3128
|
+
return directoryNames.has("ju-hong-e") || directoryNames.has("native");
|
|
3129
|
+
} catch {
|
|
3130
|
+
return false;
|
|
3131
|
+
}
|
|
3132
|
+
}
|
|
3133
|
+
async function resolveCommandModuleRootDirectoryPath() {
|
|
3134
|
+
let currentDirectoryPath = path6__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('be.cjs', document.baseURI).href))));
|
|
3135
|
+
for (let depth = 0; depth < 8; depth += 1) {
|
|
3136
|
+
const candidatePath = path6__default.default.join(currentDirectoryPath, "command-modules");
|
|
3137
|
+
if (await isCommandModuleRootDirectory(candidatePath)) {
|
|
3138
|
+
return candidatePath;
|
|
3139
|
+
}
|
|
3140
|
+
const parentDirectoryPath = path6__default.default.dirname(currentDirectoryPath);
|
|
3141
|
+
if (parentDirectoryPath === currentDirectoryPath) {
|
|
3142
|
+
break;
|
|
3143
|
+
}
|
|
3144
|
+
currentDirectoryPath = parentDirectoryPath;
|
|
3145
|
+
}
|
|
3146
|
+
throw new Error(`command-modules \uB8E8\uD2B8\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. start=${url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('be.cjs', document.baseURI).href)))}`);
|
|
3147
|
+
}
|
|
3148
|
+
async function loadCommandModulesInternal() {
|
|
3149
|
+
clearCommandRegistry();
|
|
3150
|
+
const commandModuleRootDirectoryPath = await resolveCommandModuleRootDirectoryPath();
|
|
3151
|
+
const modulePaths = (await collectCommandModulePaths(commandModuleRootDirectoryPath)).sort(
|
|
3152
|
+
(left, right) => left.localeCompare(right)
|
|
3153
|
+
);
|
|
3154
|
+
for (const modulePath of modulePaths) {
|
|
3155
|
+
await import(url.pathToFileURL(modulePath).href);
|
|
3156
|
+
}
|
|
3157
|
+
applyBuiltinCommandFilter(getAllowedBuiltinCommands());
|
|
3158
|
+
}
|
|
3159
|
+
async function loadCommandModules() {
|
|
3160
|
+
if (!loadPromise) {
|
|
3161
|
+
loadPromise = loadCommandModulesInternal();
|
|
3162
|
+
}
|
|
3163
|
+
return loadPromise;
|
|
3164
|
+
}
|
|
3165
|
+
|
|
3166
|
+
// src/common/command-runtime/parser.ts
|
|
3167
|
+
var SERVICE_PREFIX_PATTERN = /^\[(codex|gemini|claude)\]\s+(\/\S+)(?:\s+(.*))?$/i;
|
|
3168
|
+
function splitCommandParts(commandLine) {
|
|
3169
|
+
const trimmed = commandLine.trim();
|
|
3170
|
+
if (!trimmed.startsWith("/")) {
|
|
3171
|
+
return void 0;
|
|
3172
|
+
}
|
|
3173
|
+
const [command = "", ...args] = trimmed.split(/\s+/);
|
|
3174
|
+
const rawArgs = trimmed.slice(command.length).trim();
|
|
3175
|
+
return {
|
|
3176
|
+
args,
|
|
3177
|
+
command,
|
|
3178
|
+
rawArgs,
|
|
3179
|
+
rawCommand: trimmed
|
|
3180
|
+
};
|
|
3181
|
+
}
|
|
3182
|
+
function resolveSlashCommandInput(input, builtinCommands) {
|
|
3183
|
+
const trimmed = input.trim();
|
|
3184
|
+
if (!trimmed) {
|
|
3185
|
+
return void 0;
|
|
3186
|
+
}
|
|
3187
|
+
const prefixedMatch = trimmed.match(SERVICE_PREFIX_PATTERN);
|
|
3188
|
+
if (prefixedMatch) {
|
|
3189
|
+
const [, requestedService = "", command = "", rawArgs = ""] = prefixedMatch;
|
|
3190
|
+
const normalized2 = splitCommandParts([command, rawArgs].filter(Boolean).join(" "));
|
|
3191
|
+
if (!normalized2) {
|
|
3192
|
+
return void 0;
|
|
3193
|
+
}
|
|
3194
|
+
return {
|
|
3195
|
+
...normalized2,
|
|
3196
|
+
kind: "native",
|
|
3197
|
+
prefixed: true,
|
|
3198
|
+
requestedService: requestedService.toLowerCase()
|
|
3199
|
+
};
|
|
3200
|
+
}
|
|
3201
|
+
const normalized = splitCommandParts(trimmed);
|
|
3202
|
+
if (!normalized) {
|
|
3203
|
+
return void 0;
|
|
3204
|
+
}
|
|
3205
|
+
if (builtinCommands.includes(normalized.command)) {
|
|
3206
|
+
return {
|
|
3207
|
+
...normalized,
|
|
3208
|
+
kind: "builtin"
|
|
3209
|
+
};
|
|
3210
|
+
}
|
|
3211
|
+
return {
|
|
3212
|
+
...normalized,
|
|
3213
|
+
kind: "native",
|
|
3214
|
+
prefixed: false
|
|
3215
|
+
};
|
|
3216
|
+
}
|
|
3217
|
+
var STORY_FILE_PATTERN = /\.stories\.[cm]?[jt]sx?$/i;
|
|
3218
|
+
function normalizeOutputRelativePath(outputDirectory, filePath) {
|
|
3219
|
+
return path6__default.default.relative(outputDirectory, filePath).split(path6__default.default.sep).join("/");
|
|
3220
|
+
}
|
|
3221
|
+
function listOutputFiles(outputDirectory) {
|
|
3222
|
+
if (!fs7__default.default.existsSync(outputDirectory) || !fs7__default.default.statSync(outputDirectory).isDirectory()) {
|
|
3223
|
+
return [];
|
|
3224
|
+
}
|
|
3225
|
+
const files = [];
|
|
3226
|
+
const visitDirectory = (directoryPath) => {
|
|
3227
|
+
const entries = fs7__default.default.readdirSync(directoryPath, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
|
|
3228
|
+
for (const entry of entries) {
|
|
3229
|
+
const entryPath = path6__default.default.join(directoryPath, entry.name);
|
|
3230
|
+
if (entry.isDirectory()) {
|
|
3231
|
+
visitDirectory(entryPath);
|
|
3232
|
+
continue;
|
|
3233
|
+
}
|
|
3234
|
+
if (entry.isFile()) {
|
|
3235
|
+
files.push(normalizeOutputRelativePath(outputDirectory, entryPath));
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
};
|
|
3239
|
+
visitDirectory(outputDirectory);
|
|
3240
|
+
return files.sort((left, right) => left.localeCompare(right));
|
|
3241
|
+
}
|
|
3242
|
+
function isGenHtmlPreviewDirectoryCandidate(outputDirectory) {
|
|
3243
|
+
return listOutputFiles(outputDirectory).some((filePath) => STORY_FILE_PATTERN.test(filePath));
|
|
3244
|
+
}
|
|
3245
|
+
|
|
3246
|
+
// src/common/local-server/index.ts
|
|
3247
|
+
var LOCAL_SERVER_NPM_VIEW_TIMEOUT_MS = 1e4;
|
|
3248
|
+
var LOCAL_SERVER_SASS_VERSION = "^1.86.3";
|
|
3249
|
+
var STORYBOOK_VERSION = "8.6.14";
|
|
3250
|
+
var LOCAL_SERVER_LEGACY_FILE_NAMES = ["server.mjs"];
|
|
3251
|
+
var LOCAL_SERVER_STYLE_EXPORT_FILE_PATTERN = /\.(?:css|s[ac]ss)$/i;
|
|
3252
|
+
var REQUIRED_LOCAL_SERVER_PUBLISHED_DEPENDENCIES = {
|
|
3253
|
+
"sales-frontend-assets": "0.0.22",
|
|
3254
|
+
"sales-frontend-components": "1.0.7",
|
|
3255
|
+
"sales-frontend-design-system": "0.1.5"
|
|
3256
|
+
};
|
|
3257
|
+
var WORKSPACE_PACKAGE_GROUP_NAMES = ["apps", "packages"];
|
|
3258
|
+
var WORKSPACE_ROOT_MARKER_FILE_NAMES = ["pnpm-workspace.yaml", "pnpm-workspace.yml"];
|
|
3259
|
+
var LOCAL_SERVER_RUNTIME_PACKAGE_NAMES = ["react", "react-dom"];
|
|
3260
|
+
new Set(
|
|
3261
|
+
module$1.builtinModules.flatMap((moduleName) => [moduleName, moduleName.replace(/^node:/, ""), `node:${moduleName.replace(/^node:/, "")}`])
|
|
3262
|
+
);
|
|
3263
|
+
var LOCAL_SERVER_DEFAULT_PACKAGE_JSON = {
|
|
3264
|
+
dependencies: {
|
|
3265
|
+
"@storybook/addon-essentials": STORYBOOK_VERSION,
|
|
3266
|
+
"@storybook/react": STORYBOOK_VERSION,
|
|
3267
|
+
"@storybook/react-vite": STORYBOOK_VERSION,
|
|
3268
|
+
react: "^19.1.0",
|
|
3269
|
+
"react-dom": "^19.1.0",
|
|
3270
|
+
sass: LOCAL_SERVER_SASS_VERSION,
|
|
3271
|
+
storybook: STORYBOOK_VERSION
|
|
3272
|
+
},
|
|
3273
|
+
name: "ju-hong-e-local-server",
|
|
3274
|
+
private: true,
|
|
3275
|
+
scripts: {
|
|
3276
|
+
dev: "storybook dev -c ./.storybook",
|
|
3277
|
+
start: "storybook dev -c ./.storybook"
|
|
3278
|
+
},
|
|
3279
|
+
type: "commonjs"
|
|
3280
|
+
};
|
|
3281
|
+
function getLocalServerPaths(paths) {
|
|
3282
|
+
const directoryPath = path6__default.default.join(paths.baseDir, "local-server");
|
|
3283
|
+
const storybookConfigDirectoryPath = path6__default.default.join(directoryPath, ".storybook");
|
|
3284
|
+
return {
|
|
3285
|
+
directoryPath,
|
|
3286
|
+
logPath: path6__default.default.join(directoryPath, "server.log"),
|
|
3287
|
+
packageJsonPath: path6__default.default.join(directoryPath, "package.json"),
|
|
3288
|
+
previewTargetPath: path6__default.default.join(directoryPath, "preview-target.json"),
|
|
3289
|
+
statePath: path6__default.default.join(directoryPath, "server-state.json"),
|
|
3290
|
+
storybookConfigDirectoryPath,
|
|
3291
|
+
storybookMainConfigPath: path6__default.default.join(storybookConfigDirectoryPath, "main.ts"),
|
|
3292
|
+
storybookPreviewConfigPath: path6__default.default.join(storybookConfigDirectoryPath, "preview.ts")
|
|
3293
|
+
};
|
|
3294
|
+
}
|
|
3295
|
+
function isRecord(value) {
|
|
3296
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3297
|
+
}
|
|
3298
|
+
function normalizeDependencies(value) {
|
|
3299
|
+
if (!isRecord(value)) {
|
|
3300
|
+
return {};
|
|
3301
|
+
}
|
|
3302
|
+
return Object.entries(value).reduce((accumulator, [key, dependencyValue]) => {
|
|
3303
|
+
if (typeof dependencyValue === "string" && dependencyValue.trim()) {
|
|
3304
|
+
accumulator[key] = dependencyValue;
|
|
3305
|
+
}
|
|
3306
|
+
return accumulator;
|
|
3307
|
+
}, {});
|
|
3308
|
+
}
|
|
3309
|
+
function mergeLocalServerPackageJson(value, extraDependencies = {}) {
|
|
3310
|
+
const current = isRecord(value) ? value : {};
|
|
3311
|
+
const dependencies = normalizeDependencies(current.dependencies);
|
|
3312
|
+
const scripts = normalizeDependencies(current.scripts);
|
|
3313
|
+
return {
|
|
3314
|
+
...current,
|
|
3315
|
+
dependencies: {
|
|
3316
|
+
...LOCAL_SERVER_DEFAULT_PACKAGE_JSON.dependencies,
|
|
3317
|
+
...extraDependencies,
|
|
3318
|
+
...dependencies
|
|
3319
|
+
},
|
|
3320
|
+
name: typeof current.name === "string" && current.name.trim() ? current.name : LOCAL_SERVER_DEFAULT_PACKAGE_JSON.name,
|
|
3321
|
+
private: typeof current.private === "boolean" ? current.private : LOCAL_SERVER_DEFAULT_PACKAGE_JSON.private,
|
|
3322
|
+
scripts: {
|
|
3323
|
+
...scripts,
|
|
3324
|
+
...LOCAL_SERVER_DEFAULT_PACKAGE_JSON.scripts
|
|
3325
|
+
},
|
|
3326
|
+
type: LOCAL_SERVER_DEFAULT_PACKAGE_JSON.type
|
|
3327
|
+
};
|
|
3328
|
+
}
|
|
3329
|
+
function normalizePublishedPackageVersion(value) {
|
|
3330
|
+
if (typeof value === "string" && value.trim()) {
|
|
3331
|
+
return value.trim();
|
|
3332
|
+
}
|
|
3333
|
+
if (Array.isArray(value)) {
|
|
3334
|
+
return value.find((entry) => typeof entry === "string" && entry.trim().length > 0)?.trim() || "";
|
|
3335
|
+
}
|
|
3336
|
+
return "";
|
|
3337
|
+
}
|
|
3338
|
+
function fetchLatestPublishedPackageVersion(packageName, trace) {
|
|
3339
|
+
const result = child_process.spawnSync("npm", ["view", packageName, "version", "--json"], {
|
|
3340
|
+
encoding: "utf8",
|
|
3341
|
+
maxBuffer: 1024 * 1024,
|
|
3342
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3343
|
+
timeout: LOCAL_SERVER_NPM_VIEW_TIMEOUT_MS
|
|
3344
|
+
});
|
|
3345
|
+
if (result.error || result.status !== 0) {
|
|
3346
|
+
trace("local-server:setup:published-version:failed", `${packageName} ${getErrorSummary(result.error || result.stderr || result.status)}`);
|
|
3347
|
+
return "";
|
|
3348
|
+
}
|
|
3349
|
+
try {
|
|
3350
|
+
const parsed = JSON.parse(result.stdout || '""');
|
|
3351
|
+
const normalizedVersion = normalizePublishedPackageVersion(parsed);
|
|
3352
|
+
if (normalizedVersion) {
|
|
3353
|
+
trace("local-server:setup:published-version:resolved", `${packageName}@${normalizedVersion}`);
|
|
3354
|
+
}
|
|
3355
|
+
return normalizedVersion;
|
|
3356
|
+
} catch (error) {
|
|
3357
|
+
trace("local-server:setup:published-version:parse-failed", `${packageName} ${getErrorSummary(error)}`);
|
|
3358
|
+
return "";
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3361
|
+
function resolveRequiredLocalServerPublishedDependencies(paths, trace, options = {}) {
|
|
3362
|
+
const packageJsonPaths = collectWorkspacePackageJsonPaths(paths.rootDir);
|
|
3363
|
+
const workspaceVersions = /* @__PURE__ */ new Map();
|
|
3364
|
+
for (const packageJsonPath of packageJsonPaths) {
|
|
3365
|
+
const manifest = readJsonFile(packageJsonPath);
|
|
3366
|
+
const packageName = typeof manifest?.name === "string" ? manifest.name.trim() : "";
|
|
3367
|
+
const packageVersion = typeof manifest?.version === "string" ? manifest.version.trim() : "";
|
|
3368
|
+
if (!packageName || !packageVersion) {
|
|
3369
|
+
continue;
|
|
3370
|
+
}
|
|
3371
|
+
workspaceVersions.set(packageName, packageVersion);
|
|
3372
|
+
}
|
|
3373
|
+
const resolvedDependencies = {};
|
|
3374
|
+
const unresolvedDependencyNames = [];
|
|
3375
|
+
for (const [dependencyName, fallbackVersion] of Object.entries(REQUIRED_LOCAL_SERVER_PUBLISHED_DEPENDENCIES)) {
|
|
3376
|
+
const latestVersion = options.refreshPublishedDependencies ? fetchLatestPublishedPackageVersion(dependencyName, trace) : "";
|
|
3377
|
+
const resolvedVersion = latestVersion || workspaceVersions.get(dependencyName) || fallbackVersion;
|
|
3378
|
+
if (!resolvedVersion) {
|
|
3379
|
+
unresolvedDependencyNames.push(dependencyName);
|
|
3380
|
+
continue;
|
|
3381
|
+
}
|
|
3382
|
+
resolvedDependencies[dependencyName] = resolvedVersion;
|
|
3383
|
+
}
|
|
3384
|
+
if (Object.keys(resolvedDependencies).length > 0) {
|
|
3385
|
+
trace(
|
|
3386
|
+
"local-server:setup:required-published-dependencies",
|
|
3387
|
+
Object.entries(resolvedDependencies).map(([dependencyName, version]) => `${dependencyName}@${version}`).join(", ")
|
|
3388
|
+
);
|
|
3389
|
+
}
|
|
3390
|
+
if (unresolvedDependencyNames.length > 0) {
|
|
3391
|
+
trace("local-server:setup:missing-required-workspace-dependencies", unresolvedDependencyNames.join(", "));
|
|
3392
|
+
}
|
|
3393
|
+
return resolvedDependencies;
|
|
3394
|
+
}
|
|
3395
|
+
function readJsonFile(filePath) {
|
|
3396
|
+
if (!fs7__default.default.existsSync(filePath)) {
|
|
3397
|
+
return void 0;
|
|
3398
|
+
}
|
|
3399
|
+
const raw = fs7__default.default.readFileSync(filePath, "utf8").trim();
|
|
3400
|
+
if (!raw) {
|
|
3401
|
+
return void 0;
|
|
3402
|
+
}
|
|
3403
|
+
return JSON.parse(raw);
|
|
3404
|
+
}
|
|
3405
|
+
function writeJsonFile(filePath, value) {
|
|
3406
|
+
fs7__default.default.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}
|
|
3407
|
+
`, "utf8");
|
|
3408
|
+
}
|
|
3409
|
+
function resolveLocalServerStyleExportTarget(value) {
|
|
3410
|
+
if (typeof value === "string") {
|
|
3411
|
+
return LOCAL_SERVER_STYLE_EXPORT_FILE_PATTERN.test(value) ? value : "";
|
|
3412
|
+
}
|
|
3413
|
+
if (Array.isArray(value)) {
|
|
3414
|
+
for (const entry of value) {
|
|
3415
|
+
const resolvedTarget = resolveLocalServerStyleExportTarget(entry);
|
|
3416
|
+
if (resolvedTarget) {
|
|
3417
|
+
return resolvedTarget;
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
return "";
|
|
3421
|
+
}
|
|
3422
|
+
if (isRecord(value)) {
|
|
3423
|
+
return resolveLocalServerStyleExportTarget(value.default) || resolveLocalServerStyleExportTarget(value.import) || resolveLocalServerStyleExportTarget(value.require);
|
|
3424
|
+
}
|
|
3425
|
+
return "";
|
|
3426
|
+
}
|
|
3427
|
+
function buildLocalServerStyleExportBridgeContent(bridgeFilePath, targetFilePath) {
|
|
3428
|
+
const relativeTargetPath = path6__default.default.relative(path6__default.default.dirname(bridgeFilePath), targetFilePath).split(path6__default.default.sep).join("/");
|
|
3429
|
+
const normalizedTargetPath = relativeTargetPath.startsWith(".") ? relativeTargetPath : `./${relativeTargetPath}`;
|
|
3430
|
+
if (path6__default.default.extname(targetFilePath).toLowerCase() === ".css") {
|
|
3431
|
+
return `@import '${normalizedTargetPath}';
|
|
3432
|
+
`;
|
|
3433
|
+
}
|
|
3434
|
+
return `@forward '${normalizedTargetPath}';
|
|
3435
|
+
`;
|
|
3436
|
+
}
|
|
3437
|
+
function synchronizeLocalServerStyleExportBridges(paths, trace) {
|
|
3438
|
+
const { directoryPath, packageJsonPath } = getLocalServerPaths(paths);
|
|
3439
|
+
const localServerPackageJson = readJsonFile(packageJsonPath);
|
|
3440
|
+
const dependencyNames = Object.keys(normalizeDependencies(localServerPackageJson?.dependencies));
|
|
3441
|
+
for (const dependencyName of dependencyNames) {
|
|
3442
|
+
const dependencyDirectoryPath = path6__default.default.join(directoryPath, "node_modules", ...dependencyName.split("/"));
|
|
3443
|
+
const dependencyPackageJsonPath = path6__default.default.join(dependencyDirectoryPath, "package.json");
|
|
3444
|
+
const dependencyPackageJson = readJsonFile(dependencyPackageJsonPath);
|
|
3445
|
+
const dependencyExports = isRecord(dependencyPackageJson?.exports) ? dependencyPackageJson.exports : {};
|
|
3446
|
+
for (const [exportKey, exportValue] of Object.entries(dependencyExports)) {
|
|
3447
|
+
if (!exportKey.startsWith("./")) {
|
|
3448
|
+
continue;
|
|
3449
|
+
}
|
|
3450
|
+
const exportTargetPath = resolveLocalServerStyleExportTarget(exportValue);
|
|
3451
|
+
if (!exportTargetPath) {
|
|
3452
|
+
continue;
|
|
3453
|
+
}
|
|
3454
|
+
const exportSubpath = exportKey.slice(2);
|
|
3455
|
+
if (!exportSubpath) {
|
|
3456
|
+
continue;
|
|
3457
|
+
}
|
|
3458
|
+
const targetFilePath = path6__default.default.resolve(dependencyDirectoryPath, exportTargetPath);
|
|
3459
|
+
if (!fs7__default.default.existsSync(targetFilePath) || !fs7__default.default.statSync(targetFilePath).isFile()) {
|
|
3460
|
+
trace("local-server:style-export-bridge:missing-target", `${dependencyName} ${exportKey} -> ${targetFilePath}`);
|
|
3461
|
+
continue;
|
|
3462
|
+
}
|
|
3463
|
+
const bridgeFilePath = path6__default.default.join(
|
|
3464
|
+
dependencyDirectoryPath,
|
|
3465
|
+
...exportSubpath.split("/").filter(Boolean)
|
|
3466
|
+
) + path6__default.default.extname(targetFilePath);
|
|
3467
|
+
const bridgeContent = buildLocalServerStyleExportBridgeContent(bridgeFilePath, targetFilePath);
|
|
3468
|
+
const currentBridgeContent = fs7__default.default.existsSync(bridgeFilePath) ? fs7__default.default.readFileSync(bridgeFilePath, "utf8") : "";
|
|
3469
|
+
if (currentBridgeContent === bridgeContent) {
|
|
3470
|
+
continue;
|
|
3471
|
+
}
|
|
3472
|
+
fs7__default.default.mkdirSync(path6__default.default.dirname(bridgeFilePath), { recursive: true });
|
|
3473
|
+
fs7__default.default.writeFileSync(bridgeFilePath, bridgeContent, "utf8");
|
|
3474
|
+
trace("local-server:style-export-bridge:written", `${dependencyName} ${exportKey} -> ${bridgeFilePath}`);
|
|
3475
|
+
}
|
|
3476
|
+
}
|
|
3477
|
+
}
|
|
3478
|
+
function normalizePreviewTargetDirectory(directoryPath) {
|
|
3479
|
+
if (typeof directoryPath !== "string" || !directoryPath.trim()) {
|
|
3480
|
+
return void 0;
|
|
3481
|
+
}
|
|
3482
|
+
return path6__default.default.resolve(directoryPath);
|
|
3483
|
+
}
|
|
3484
|
+
function normalizePreviewTargetDirectories(directoryPaths) {
|
|
3485
|
+
if (!Array.isArray(directoryPaths)) {
|
|
3486
|
+
return [];
|
|
3487
|
+
}
|
|
3488
|
+
return Array.from(
|
|
3489
|
+
new Set(
|
|
3490
|
+
directoryPaths.map((directoryPath) => normalizePreviewTargetDirectory(directoryPath)).filter((directoryPath) => Boolean(directoryPath))
|
|
3491
|
+
)
|
|
3492
|
+
).sort((left, right) => left.localeCompare(right));
|
|
3493
|
+
}
|
|
3494
|
+
function toLocalServerPreviewMode(target) {
|
|
3495
|
+
return target.mode === "directory" ? "latest" : "all";
|
|
3496
|
+
}
|
|
3497
|
+
function buildPreviewTargetConfig(options = {}) {
|
|
3498
|
+
const resolvedOutputDirectory = normalizePreviewTargetDirectory(options.outputDirectory);
|
|
3499
|
+
const resolvedPreviousDirectory = normalizePreviewTargetDirectory(options.previousTarget?.directory);
|
|
3500
|
+
const resolvedDirectories = normalizePreviewTargetDirectories(options.directories ?? options.previousTarget?.directories);
|
|
3501
|
+
const targetDirectory = resolvedOutputDirectory || resolvedPreviousDirectory;
|
|
3502
|
+
const targetMode = options.mode || toLocalServerPreviewMode(options.previousTarget || { mode: "all" });
|
|
3503
|
+
if (targetMode === "all") {
|
|
3504
|
+
return targetDirectory || resolvedDirectories.length > 0 ? {
|
|
3505
|
+
...targetDirectory ? { directory: targetDirectory } : {},
|
|
3506
|
+
...resolvedDirectories.length > 0 ? { directories: resolvedDirectories } : {},
|
|
3507
|
+
mode: "all"
|
|
3508
|
+
} : { mode: "all" };
|
|
3509
|
+
}
|
|
3510
|
+
if (!targetDirectory) {
|
|
3511
|
+
return { mode: "all" };
|
|
3512
|
+
}
|
|
3513
|
+
return {
|
|
3514
|
+
directory: targetDirectory,
|
|
3515
|
+
mode: "directory"
|
|
3516
|
+
};
|
|
3517
|
+
}
|
|
3518
|
+
function readPreviewTargetConfig(filePath) {
|
|
3519
|
+
const parsed = readJsonFile(filePath);
|
|
3520
|
+
const normalizedDirectory = normalizePreviewTargetDirectory(parsed?.directory);
|
|
3521
|
+
const normalizedDirectories = normalizePreviewTargetDirectories(parsed?.directories);
|
|
3522
|
+
if (parsed?.mode === "directory" && normalizedDirectory) {
|
|
3523
|
+
return {
|
|
3524
|
+
directory: normalizedDirectory,
|
|
3525
|
+
mode: "directory"
|
|
3526
|
+
};
|
|
3527
|
+
}
|
|
3528
|
+
if (parsed?.mode === "all") {
|
|
3529
|
+
return normalizedDirectory ? {
|
|
3530
|
+
directory: normalizedDirectory,
|
|
3531
|
+
...normalizedDirectories.length > 0 ? { directories: normalizedDirectories } : {},
|
|
3532
|
+
mode: "all"
|
|
3533
|
+
} : normalizedDirectories.length > 0 ? {
|
|
3534
|
+
directories: normalizedDirectories,
|
|
3535
|
+
mode: "all"
|
|
3536
|
+
} : { mode: "all" };
|
|
3537
|
+
}
|
|
3538
|
+
return { mode: "all" };
|
|
3539
|
+
}
|
|
3540
|
+
function listGeneratedOutputDirectories(paths) {
|
|
3541
|
+
const generatedRootPath = path6__default.default.join(paths.baseDir, "generated");
|
|
3542
|
+
if (!fs7__default.default.existsSync(generatedRootPath) || !fs7__default.default.statSync(generatedRootPath).isDirectory()) {
|
|
3543
|
+
return [];
|
|
3544
|
+
}
|
|
3545
|
+
return fs7__default.default.readdirSync(generatedRootPath, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path6__default.default.join(generatedRootPath, entry.name)).sort((left, right) => left.localeCompare(right));
|
|
3546
|
+
}
|
|
3547
|
+
function resolveValidGeneratedPreviewDirectories(paths) {
|
|
3548
|
+
return listGeneratedOutputDirectories(paths).filter((directoryPath) => isGenHtmlPreviewDirectoryCandidate(directoryPath));
|
|
3549
|
+
}
|
|
3550
|
+
function selectPreviewTargetDirectory(preferredDirectory, validDirectories) {
|
|
3551
|
+
if (preferredDirectory && validDirectories.includes(preferredDirectory)) {
|
|
3552
|
+
return preferredDirectory;
|
|
3553
|
+
}
|
|
3554
|
+
return validDirectories.length > 0 ? validDirectories[validDirectories.length - 1] : void 0;
|
|
3555
|
+
}
|
|
3556
|
+
function setLocalServerPreviewTarget(paths, options = {}) {
|
|
3557
|
+
const localServerPaths = getLocalServerPaths(paths);
|
|
3558
|
+
const currentTarget = fs7__default.default.existsSync(localServerPaths.previewTargetPath) ? readPreviewTargetConfig(localServerPaths.previewTargetPath) : buildPreviewTargetConfig();
|
|
3559
|
+
const resolvedMode = options.mode || toLocalServerPreviewMode(currentTarget);
|
|
3560
|
+
const validDirectories = resolvedMode === "all" ? resolveValidGeneratedPreviewDirectories(paths) : [];
|
|
3561
|
+
const resolvedOutputDirectory = normalizePreviewTargetDirectory(options.outputDirectory);
|
|
3562
|
+
const effectiveDirectory = resolvedMode === "all" ? selectPreviewTargetDirectory(resolvedOutputDirectory || currentTarget.directory, validDirectories) : resolvedOutputDirectory || currentTarget.directory;
|
|
3563
|
+
const nextTarget = buildPreviewTargetConfig({
|
|
3564
|
+
directories: validDirectories,
|
|
3565
|
+
mode: resolvedMode,
|
|
3566
|
+
outputDirectory: effectiveDirectory,
|
|
3567
|
+
previousTarget: currentTarget
|
|
3568
|
+
});
|
|
3569
|
+
writeJsonFile(localServerPaths.previewTargetPath, nextTarget);
|
|
3570
|
+
}
|
|
3571
|
+
function collectWorkspacePackageJsonPaths(rootDir) {
|
|
3572
|
+
const workspaceRootDir = resolveWorkspaceRootDirectory(rootDir);
|
|
3573
|
+
const packageJsonPaths = [path6__default.default.join(workspaceRootDir, "package.json")].filter((filePath) => fs7__default.default.existsSync(filePath));
|
|
3574
|
+
for (const groupName of WORKSPACE_PACKAGE_GROUP_NAMES) {
|
|
3575
|
+
const groupDirectoryPath = path6__default.default.join(workspaceRootDir, groupName);
|
|
3576
|
+
if (!fs7__default.default.existsSync(groupDirectoryPath) || !fs7__default.default.statSync(groupDirectoryPath).isDirectory()) {
|
|
3577
|
+
continue;
|
|
3578
|
+
}
|
|
3579
|
+
for (const entry of fs7__default.default.readdirSync(groupDirectoryPath, { withFileTypes: true })) {
|
|
3580
|
+
if (!entry.isDirectory()) {
|
|
3581
|
+
continue;
|
|
3582
|
+
}
|
|
3583
|
+
const packageJsonPath = path6__default.default.join(groupDirectoryPath, entry.name, "package.json");
|
|
3584
|
+
if (fs7__default.default.existsSync(packageJsonPath)) {
|
|
3585
|
+
packageJsonPaths.push(packageJsonPath);
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
}
|
|
3589
|
+
return packageJsonPaths.sort((left, right) => left.localeCompare(right));
|
|
3590
|
+
}
|
|
3591
|
+
function isWorkspaceRootDirectory(directoryPath) {
|
|
3592
|
+
if (!directoryPath) {
|
|
3593
|
+
return false;
|
|
3594
|
+
}
|
|
3595
|
+
if (WORKSPACE_ROOT_MARKER_FILE_NAMES.some((fileName) => fs7__default.default.existsSync(path6__default.default.join(directoryPath, fileName)))) {
|
|
3596
|
+
return true;
|
|
3597
|
+
}
|
|
3598
|
+
const packageJsonPath = path6__default.default.join(directoryPath, "package.json");
|
|
3599
|
+
const manifest = readJsonFile(packageJsonPath);
|
|
3600
|
+
if (!isRecord(manifest)) {
|
|
3601
|
+
return false;
|
|
3602
|
+
}
|
|
3603
|
+
return Boolean(manifest.workspaces);
|
|
3604
|
+
}
|
|
3605
|
+
function resolveWorkspaceRootDirectory(startDirectory) {
|
|
3606
|
+
let currentDirectoryPath = path6__default.default.resolve(startDirectory);
|
|
3607
|
+
while (true) {
|
|
3608
|
+
if (isWorkspaceRootDirectory(currentDirectoryPath)) {
|
|
3609
|
+
return currentDirectoryPath;
|
|
3610
|
+
}
|
|
3611
|
+
const parentDirectoryPath = path6__default.default.dirname(currentDirectoryPath);
|
|
3612
|
+
if (parentDirectoryPath === currentDirectoryPath) {
|
|
3613
|
+
return path6__default.default.resolve(startDirectory);
|
|
3614
|
+
}
|
|
3615
|
+
currentDirectoryPath = parentDirectoryPath;
|
|
3616
|
+
}
|
|
3617
|
+
}
|
|
3618
|
+
function listAncestorDirectories(startDirectory) {
|
|
3619
|
+
const directories = [];
|
|
3620
|
+
let currentDirectoryPath = path6__default.default.resolve(startDirectory);
|
|
3621
|
+
while (true) {
|
|
3622
|
+
directories.push(currentDirectoryPath);
|
|
3623
|
+
const parentDirectoryPath = path6__default.default.dirname(currentDirectoryPath);
|
|
3624
|
+
if (parentDirectoryPath === currentDirectoryPath) {
|
|
3625
|
+
break;
|
|
3626
|
+
}
|
|
3627
|
+
currentDirectoryPath = parentDirectoryPath;
|
|
3628
|
+
}
|
|
3629
|
+
return directories;
|
|
3630
|
+
}
|
|
3631
|
+
function resolveInstalledPackageVersion(dependencyName, startDirectories) {
|
|
3632
|
+
const candidateDirectories = Array.from(
|
|
3633
|
+
new Set(
|
|
3634
|
+
startDirectories.flatMap((directoryPath) => {
|
|
3635
|
+
if (!directoryPath) {
|
|
3636
|
+
return [];
|
|
3637
|
+
}
|
|
3638
|
+
return listAncestorDirectories(directoryPath);
|
|
3639
|
+
})
|
|
3640
|
+
)
|
|
3641
|
+
);
|
|
3642
|
+
for (const directoryPath of candidateDirectories) {
|
|
3643
|
+
const packageJsonPath = path6__default.default.join(directoryPath, "node_modules", ...dependencyName.split("/"), "package.json");
|
|
3644
|
+
const manifest = readJsonFile(packageJsonPath);
|
|
3645
|
+
const version = typeof manifest?.version === "string" ? manifest.version.trim() : "";
|
|
3646
|
+
if (version) {
|
|
3647
|
+
return version;
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3650
|
+
return "";
|
|
3651
|
+
}
|
|
3652
|
+
function resolvePreferredManifestDependencyEntry(manifest, dependencyName) {
|
|
3653
|
+
if (!isRecord(manifest)) {
|
|
3654
|
+
return void 0;
|
|
3655
|
+
}
|
|
3656
|
+
const dependencyFields = [
|
|
3657
|
+
{ fieldName: "dependencies", priority: 1 },
|
|
3658
|
+
{ fieldName: "devDependencies", priority: 2 },
|
|
3659
|
+
{ fieldName: "optionalDependencies", priority: 3 },
|
|
3660
|
+
{ fieldName: "peerDependencies", priority: 4 }
|
|
3661
|
+
];
|
|
3662
|
+
for (const { fieldName, priority } of dependencyFields) {
|
|
3663
|
+
const dependencies = normalizeDependencies(manifest[fieldName]);
|
|
3664
|
+
const version = dependencies[dependencyName]?.trim();
|
|
3665
|
+
if (!version || version.startsWith("workspace:") || version.startsWith("file:")) {
|
|
3666
|
+
continue;
|
|
3667
|
+
}
|
|
3668
|
+
return {
|
|
3669
|
+
priority,
|
|
3670
|
+
version
|
|
3671
|
+
};
|
|
3672
|
+
}
|
|
3673
|
+
return void 0;
|
|
3674
|
+
}
|
|
3675
|
+
function resolvePreferredWorkspaceDependencyVersions(rootDir, dependencyNames) {
|
|
3676
|
+
const packageJsonPaths = collectWorkspacePackageJsonPaths(rootDir);
|
|
3677
|
+
const resolvedEntries = /* @__PURE__ */ new Map();
|
|
3678
|
+
for (const packageJsonPath of packageJsonPaths) {
|
|
3679
|
+
const manifest = readJsonFile(packageJsonPath);
|
|
3680
|
+
for (const dependencyName of dependencyNames) {
|
|
3681
|
+
const candidate = resolvePreferredManifestDependencyEntry(manifest, dependencyName);
|
|
3682
|
+
const current = resolvedEntries.get(dependencyName);
|
|
3683
|
+
if (!candidate) {
|
|
3684
|
+
continue;
|
|
3685
|
+
}
|
|
3686
|
+
if (!current || candidate.priority < current.priority) {
|
|
3687
|
+
resolvedEntries.set(dependencyName, candidate);
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
}
|
|
3691
|
+
return Object.fromEntries(
|
|
3692
|
+
dependencyNames.map((dependencyName) => [dependencyName, resolvedEntries.get(dependencyName)?.version || ""]).filter(([, version]) => Boolean(version))
|
|
3693
|
+
);
|
|
3694
|
+
}
|
|
3695
|
+
function resolveLocalServerRuntimeDependencies(paths, localServerDirectoryPath, trace) {
|
|
3696
|
+
const dependencyNames = [...LOCAL_SERVER_RUNTIME_PACKAGE_NAMES];
|
|
3697
|
+
const workspaceResolved = resolvePreferredWorkspaceDependencyVersions(paths.rootDir, dependencyNames);
|
|
3698
|
+
const workspaceRootDir = resolveWorkspaceRootDirectory(paths.rootDir);
|
|
3699
|
+
const resolvedDependencies = dependencyNames.reduce((accumulator, dependencyName) => {
|
|
3700
|
+
const version = workspaceResolved[dependencyName] || resolveInstalledPackageVersion(dependencyName, [paths.rootDir, workspaceRootDir, localServerDirectoryPath]);
|
|
3701
|
+
if (version) {
|
|
3702
|
+
accumulator[dependencyName] = version;
|
|
3703
|
+
}
|
|
3704
|
+
return accumulator;
|
|
3705
|
+
}, {});
|
|
3706
|
+
if (Object.keys(resolvedDependencies).length > 0) {
|
|
3707
|
+
trace(
|
|
3708
|
+
"local-server:setup:runtime-dependencies",
|
|
3709
|
+
Object.entries(resolvedDependencies).map(([dependencyName, version]) => `${dependencyName}@${version}`).join(", ")
|
|
3710
|
+
);
|
|
3711
|
+
}
|
|
3712
|
+
return resolvedDependencies;
|
|
3713
|
+
}
|
|
3714
|
+
function installLocalServerDependencies(directoryPath, trace) {
|
|
3715
|
+
trace("local-server:install:start", directoryPath);
|
|
3716
|
+
const result = child_process.spawnSync("npm", ["install"], {
|
|
3717
|
+
cwd: directoryPath,
|
|
3718
|
+
encoding: "utf8",
|
|
3719
|
+
maxBuffer: 1024 * 1024 * 20,
|
|
3720
|
+
stdio: "inherit"
|
|
3721
|
+
});
|
|
3722
|
+
if (result.error || result.status !== 0) {
|
|
3723
|
+
trace("local-server:install:failed", getErrorSummary(result.error || result.stderr || result.status));
|
|
3724
|
+
throw new Error("\uB85C\uCEEC \uD504\uB9AC\uBDF0 \uC11C\uBC84 \uC758\uC874\uC131 \uC124\uCE58\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
|
|
3725
|
+
}
|
|
3726
|
+
trace("local-server:install:done", directoryPath);
|
|
3727
|
+
}
|
|
3728
|
+
function hasAllLocalServerDependencies(directoryPath) {
|
|
3729
|
+
const packageJsonPath = path6__default.default.join(directoryPath, "package.json");
|
|
3730
|
+
const packageJson = readJsonFile(packageJsonPath);
|
|
3731
|
+
const dependencies = Object.keys(normalizeDependencies(packageJson?.dependencies));
|
|
3732
|
+
if (dependencies.length === 0) {
|
|
3733
|
+
return true;
|
|
3734
|
+
}
|
|
3735
|
+
return dependencies.every((dependencyName) => fs7__default.default.existsSync(path6__default.default.join(directoryPath, "node_modules", ...dependencyName.split("/"))));
|
|
3736
|
+
}
|
|
3737
|
+
function ensureLocalServerDependencies(paths, trace, forceInstall = false) {
|
|
3738
|
+
const { directoryPath } = getLocalServerPaths(paths);
|
|
3739
|
+
const nodeModulesPath = path6__default.default.join(directoryPath, "node_modules");
|
|
3740
|
+
const hasNodeModules = fs7__default.default.existsSync(nodeModulesPath);
|
|
3741
|
+
const hasRequiredDependencies = hasNodeModules ? hasAllLocalServerDependencies(directoryPath) : false;
|
|
3742
|
+
const shouldInstall = forceInstall || !hasNodeModules || !hasRequiredDependencies;
|
|
3743
|
+
if (hasNodeModules && !hasRequiredDependencies) {
|
|
3744
|
+
trace("local-server:install:repair", directoryPath);
|
|
3745
|
+
}
|
|
3746
|
+
if (shouldInstall) {
|
|
3747
|
+
installLocalServerDependencies(directoryPath, trace);
|
|
3748
|
+
}
|
|
3749
|
+
synchronizeLocalServerStyleExportBridges(paths, trace);
|
|
3750
|
+
}
|
|
3751
|
+
function toSortedDependencies(dependencies) {
|
|
3752
|
+
return Object.fromEntries(Object.entries(dependencies).sort(([left], [right]) => left.localeCompare(right)));
|
|
3753
|
+
}
|
|
3754
|
+
function getStorybookMainConfigContent() {
|
|
3755
|
+
return `import fs from 'node:fs';
|
|
3756
|
+
import path from 'node:path';
|
|
3757
|
+
import type { StorybookConfig } from '@storybook/react-vite';
|
|
3758
|
+
import type { Alias, Plugin } from 'vite';
|
|
3759
|
+
|
|
3760
|
+
const localServerRoot = path.resolve(__dirname, '..');
|
|
3761
|
+
const localServerPackageJsonPath = path.resolve(localServerRoot, 'package.json');
|
|
3762
|
+
const previewTargetPath = path.resolve(__dirname, '../preview-target.json');
|
|
3763
|
+
const workspaceRoot = path.resolve(__dirname, '..', '..');
|
|
3764
|
+
const generatedFileEndpointPath = '/__ju_hong_e__/generated-file';
|
|
3765
|
+
const dependencyAliasExcludeNames = new Set(['react', 'react-dom', 'sass', 'storybook']);
|
|
3766
|
+
const dependencyAliasExcludePrefixes = ['@storybook/'];
|
|
3767
|
+
|
|
3768
|
+
function toPosix(value: string) {
|
|
3769
|
+
return value.split(path.sep).join('/');
|
|
3770
|
+
}
|
|
3771
|
+
|
|
3772
|
+
function readLocalServerDependencies() {
|
|
3773
|
+
try {
|
|
3774
|
+
const raw = fs.readFileSync(localServerPackageJsonPath, 'utf8').trim();
|
|
3775
|
+
|
|
3776
|
+
if (!raw) {
|
|
3777
|
+
return {};
|
|
3778
|
+
}
|
|
3779
|
+
|
|
3780
|
+
const parsed = JSON.parse(raw) as { dependencies?: Record<string, string> };
|
|
3781
|
+
|
|
3782
|
+
return parsed.dependencies && typeof parsed.dependencies === 'object' ? parsed.dependencies : {};
|
|
3783
|
+
} catch {
|
|
3784
|
+
return {};
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3787
|
+
|
|
3788
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
3789
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
3790
|
+
}
|
|
3791
|
+
|
|
3792
|
+
function resolveExportTarget(value: unknown): string {
|
|
3793
|
+
if (typeof value === 'string' && value.trim()) {
|
|
3794
|
+
return value.trim();
|
|
3795
|
+
}
|
|
3796
|
+
|
|
3797
|
+
if (Array.isArray(value)) {
|
|
3798
|
+
for (const entry of value) {
|
|
3799
|
+
const resolvedTarget = resolveExportTarget(entry);
|
|
3800
|
+
|
|
3801
|
+
if (resolvedTarget) {
|
|
3802
|
+
return resolvedTarget;
|
|
3803
|
+
}
|
|
3804
|
+
}
|
|
3805
|
+
|
|
3806
|
+
return '';
|
|
3807
|
+
}
|
|
3808
|
+
|
|
3809
|
+
if (isRecord(value)) {
|
|
3810
|
+
return resolveExportTarget(value.import) || resolveExportTarget(value.default) || resolveExportTarget(value.require);
|
|
3811
|
+
}
|
|
3812
|
+
|
|
3813
|
+
return '';
|
|
3814
|
+
}
|
|
3815
|
+
|
|
3816
|
+
function escapeRegExp(value: string) {
|
|
3817
|
+
return value.replace(/[][\\\\^$.*+?(){}|]/g, '\\\\$&');
|
|
3818
|
+
}
|
|
3819
|
+
|
|
3820
|
+
function buildDependencyAliases() {
|
|
3821
|
+
return Object.keys(readLocalServerDependencies()).reduce<Alias[]>((accumulator, dependencyName) => {
|
|
3822
|
+
if (dependencyAliasExcludeNames.has(dependencyName) || dependencyAliasExcludePrefixes.some((prefix) => dependencyName.startsWith(prefix))) {
|
|
3823
|
+
return accumulator;
|
|
3824
|
+
}
|
|
3825
|
+
|
|
3826
|
+
const dependencyPath = path.resolve(localServerRoot, 'node_modules', ...dependencyName.split('/'));
|
|
3827
|
+
const dependencyPackageJsonPath = path.resolve(dependencyPath, 'package.json');
|
|
3828
|
+
|
|
3829
|
+
if (fs.existsSync(dependencyPath)) {
|
|
3830
|
+
try {
|
|
3831
|
+
const dependencyPackageJson = JSON.parse(fs.readFileSync(dependencyPackageJsonPath, 'utf8')) as { exports?: Record<string, unknown> };
|
|
3832
|
+
const dependencyExports = isRecord(dependencyPackageJson.exports) ? dependencyPackageJson.exports : {};
|
|
3833
|
+
const rootExportTarget = resolveExportTarget(dependencyExports['.']);
|
|
3834
|
+
const rootReplacement = rootExportTarget ? path.resolve(dependencyPath, rootExportTarget) : dependencyPath;
|
|
3835
|
+
const dependencyNamePattern = escapeRegExp(dependencyName);
|
|
3836
|
+
|
|
3837
|
+
accumulator.push({
|
|
3838
|
+
find: new RegExp(\`^\${dependencyNamePattern}$\`),
|
|
3839
|
+
replacement: rootReplacement
|
|
3840
|
+
});
|
|
3841
|
+
|
|
3842
|
+
for (const [exportKey, exportValue] of Object.entries(dependencyExports)) {
|
|
3843
|
+
if (!exportKey.startsWith('./')) {
|
|
3844
|
+
continue;
|
|
3845
|
+
}
|
|
3846
|
+
|
|
3847
|
+
const exportTarget = resolveExportTarget(exportValue);
|
|
3848
|
+
|
|
3849
|
+
if (!exportTarget) {
|
|
3850
|
+
continue;
|
|
3851
|
+
}
|
|
3852
|
+
|
|
3853
|
+
accumulator.push({
|
|
3854
|
+
find: new RegExp(\`^\${escapeRegExp(\`\${dependencyName}/\${exportKey.slice(2)}\`)}$\`),
|
|
3855
|
+
replacement: path.resolve(dependencyPath, exportTarget)
|
|
3856
|
+
});
|
|
3857
|
+
}
|
|
3858
|
+
} catch {
|
|
3859
|
+
accumulator.push({
|
|
3860
|
+
find: new RegExp(\`^\${escapeRegExp(dependencyName)}$\`),
|
|
3861
|
+
replacement: dependencyPath
|
|
3862
|
+
});
|
|
3863
|
+
}
|
|
3864
|
+
|
|
3865
|
+
accumulator.push({
|
|
3866
|
+
find: new RegExp(\`^\${escapeRegExp(dependencyName)}/(.+)$\`),
|
|
3867
|
+
replacement: \`\${dependencyPath}/$1\`
|
|
3868
|
+
});
|
|
3869
|
+
}
|
|
3870
|
+
|
|
3871
|
+
return accumulator;
|
|
3872
|
+
}, []);
|
|
3873
|
+
}
|
|
3874
|
+
|
|
3875
|
+
function readPreviewTarget() {
|
|
3876
|
+
try {
|
|
3877
|
+
const raw = fs.readFileSync(previewTargetPath, 'utf8').trim();
|
|
3878
|
+
|
|
3879
|
+
if (!raw) {
|
|
3880
|
+
return { mode: 'all' } as const;
|
|
3881
|
+
}
|
|
3882
|
+
|
|
3883
|
+
const parsed = JSON.parse(raw) as { directory?: string; directories?: string[]; mode?: string };
|
|
3884
|
+
const directory = typeof parsed.directory === 'string' && parsed.directory.trim() ? path.resolve(parsed.directory) : undefined;
|
|
3885
|
+
const directories = Array.isArray(parsed.directories)
|
|
3886
|
+
? Array.from(
|
|
3887
|
+
new Set(
|
|
3888
|
+
parsed.directories
|
|
3889
|
+
.filter((value): value is string => typeof value === 'string' && value.trim())
|
|
3890
|
+
.map((value) => path.resolve(value))
|
|
3891
|
+
)
|
|
3892
|
+
).sort((left, right) => left.localeCompare(right))
|
|
3893
|
+
: undefined;
|
|
3894
|
+
|
|
3895
|
+
if (parsed.mode === 'directory' && directory) {
|
|
3896
|
+
return {
|
|
3897
|
+
directory,
|
|
3898
|
+
mode: 'directory'
|
|
3899
|
+
} as const;
|
|
3900
|
+
}
|
|
3901
|
+
|
|
3902
|
+
if (parsed.mode === 'all') {
|
|
3903
|
+
return {
|
|
3904
|
+
...(directory ? { directory } : {}),
|
|
3905
|
+
...(directories ? { directories } : {}),
|
|
3906
|
+
mode: 'all'
|
|
3907
|
+
} as const;
|
|
3908
|
+
}
|
|
3909
|
+
} catch {
|
|
3910
|
+
return { mode: 'all' } as const;
|
|
3911
|
+
}
|
|
3912
|
+
|
|
3913
|
+
return { mode: 'all' } as const;
|
|
3914
|
+
}
|
|
3915
|
+
|
|
3916
|
+
function getAllowedGeneratedDirectories() {
|
|
3917
|
+
const target = readPreviewTarget();
|
|
3918
|
+
|
|
3919
|
+
if (target.mode === 'all') {
|
|
3920
|
+
if (Array.isArray(target.directories) && target.directories.length > 0) {
|
|
3921
|
+
return target.directories;
|
|
3922
|
+
}
|
|
3923
|
+
|
|
3924
|
+
return [path.resolve(localServerRoot, '..', 'generated')];
|
|
3925
|
+
}
|
|
3926
|
+
|
|
3927
|
+
if (target.directory) {
|
|
3928
|
+
return [target.directory];
|
|
3929
|
+
}
|
|
3930
|
+
|
|
3931
|
+
return [path.resolve(localServerRoot, '..', 'generated')];
|
|
3932
|
+
}
|
|
3933
|
+
|
|
3934
|
+
function isPathInsideDirectory(directoryPath: string, filePath: string) {
|
|
3935
|
+
const relativePath = path.relative(directoryPath, filePath);
|
|
3936
|
+
|
|
3937
|
+
return relativePath === '' || (!relativePath.startsWith('..') && !path.isAbsolute(relativePath));
|
|
3938
|
+
}
|
|
3939
|
+
|
|
3940
|
+
function resolveGeneratedFilePath(source: string) {
|
|
3941
|
+
const normalizedSource = source.trim();
|
|
3942
|
+
|
|
3943
|
+
if (!normalizedSource) {
|
|
3944
|
+
return '';
|
|
3945
|
+
}
|
|
3946
|
+
|
|
3947
|
+
let candidate = normalizedSource;
|
|
3948
|
+
|
|
3949
|
+
try {
|
|
3950
|
+
if (/^https?:\\/\\//i.test(candidate)) {
|
|
3951
|
+
candidate = new URL(candidate).pathname;
|
|
3952
|
+
}
|
|
3953
|
+
} catch {
|
|
3954
|
+
return '';
|
|
3955
|
+
}
|
|
3956
|
+
|
|
3957
|
+
if (candidate.startsWith('/@fs/')) {
|
|
3958
|
+
return path.resolve('/', candidate.slice('/@fs/'.length));
|
|
3959
|
+
}
|
|
3960
|
+
|
|
3961
|
+
if (candidate.startsWith('/')) {
|
|
3962
|
+
return path.resolve(candidate);
|
|
3963
|
+
}
|
|
3964
|
+
|
|
3965
|
+
return path.resolve(workspaceRoot, candidate);
|
|
3966
|
+
}
|
|
3967
|
+
|
|
3968
|
+
function createGeneratedFileProxyPlugin(): Plugin {
|
|
3969
|
+
return {
|
|
3970
|
+
configureServer(server) {
|
|
3971
|
+
server.middlewares.use((request, response, next) => {
|
|
3972
|
+
if (!request.url) {
|
|
3973
|
+
next();
|
|
3974
|
+
|
|
3975
|
+
return;
|
|
3976
|
+
}
|
|
3977
|
+
|
|
3978
|
+
const requestUrl = new URL(request.url, 'http://127.0.0.1');
|
|
3979
|
+
|
|
3980
|
+
if (requestUrl.pathname !== generatedFileEndpointPath) {
|
|
3981
|
+
next();
|
|
3982
|
+
|
|
3983
|
+
return;
|
|
3984
|
+
}
|
|
3985
|
+
|
|
3986
|
+
const filePath = resolveGeneratedFilePath(requestUrl.searchParams.get('source') || '');
|
|
3987
|
+
const allowedDirectories = getAllowedGeneratedDirectories();
|
|
3988
|
+
const isAllowedFile =
|
|
3989
|
+
Boolean(filePath) &&
|
|
3990
|
+
fs.existsSync(filePath) &&
|
|
3991
|
+
fs.statSync(filePath).isFile() &&
|
|
3992
|
+
allowedDirectories.some((directoryPath) => isPathInsideDirectory(directoryPath, filePath));
|
|
3993
|
+
|
|
3994
|
+
if (!isAllowedFile) {
|
|
3995
|
+
response.statusCode = 404;
|
|
3996
|
+
response.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
|
3997
|
+
response.end('\uC0DD\uC131\uB41C \uD30C\uC77C\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.');
|
|
3998
|
+
|
|
3999
|
+
return;
|
|
4000
|
+
}
|
|
4001
|
+
|
|
4002
|
+
const shouldDownload = requestUrl.searchParams.get('download') === '1';
|
|
4003
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
4004
|
+
|
|
4005
|
+
response.statusCode = 200;
|
|
4006
|
+
response.setHeader('Cache-Control', 'no-store');
|
|
4007
|
+
response.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
|
4008
|
+
response.setHeader('X-Content-Type-Options', 'nosniff');
|
|
4009
|
+
response.setHeader(
|
|
4010
|
+
'Content-Disposition',
|
|
4011
|
+
\`\${shouldDownload ? 'attachment' : 'inline'}; filename*=UTF-8''\${encodeURIComponent(path.basename(filePath))}\`
|
|
4012
|
+
);
|
|
4013
|
+
response.end(content);
|
|
4014
|
+
});
|
|
4015
|
+
},
|
|
4016
|
+
name: 'ju-hong-e-generated-file-proxy'
|
|
4017
|
+
};
|
|
4018
|
+
}
|
|
4019
|
+
|
|
4020
|
+
function resolveStories() {
|
|
4021
|
+
const target = readPreviewTarget();
|
|
4022
|
+
|
|
4023
|
+
if (target.mode === 'directory' && target.directory) {
|
|
4024
|
+
const relativePattern = toPosix(path.relative(__dirname, target.directory));
|
|
4025
|
+
|
|
4026
|
+
return [\`\${relativePattern}/**/*.stories.@(js|jsx|ts|tsx)\`];
|
|
4027
|
+
}
|
|
4028
|
+
|
|
4029
|
+
if (target.mode === 'all' && Array.isArray(target.directories)) {
|
|
4030
|
+
return target.directories.map((directory) => \`\${toPosix(path.relative(__dirname, directory))}/**/*.stories.@(js|jsx|ts|tsx)\`);
|
|
4031
|
+
}
|
|
4032
|
+
|
|
4033
|
+
return ['../../generated/**/*.stories.@(js|jsx|ts|tsx)'];
|
|
4034
|
+
}
|
|
4035
|
+
|
|
4036
|
+
const dependencyAliases = buildDependencyAliases();
|
|
4037
|
+
|
|
4038
|
+
const config: StorybookConfig = {
|
|
4039
|
+
stories: resolveStories(),
|
|
4040
|
+
addons: ['@storybook/addon-essentials'],
|
|
4041
|
+
framework: {
|
|
4042
|
+
name: '@storybook/react-vite',
|
|
4043
|
+
options: {}
|
|
4044
|
+
},
|
|
4045
|
+
docs: {
|
|
4046
|
+
autodocs: false
|
|
4047
|
+
},
|
|
4048
|
+
viteFinal: async (config) => {
|
|
4049
|
+
const currentAllow = Array.isArray(config.server?.fs?.allow) ? config.server.fs.allow : [];
|
|
4050
|
+
const currentPlugins = Array.isArray(config.plugins) ? config.plugins : config.plugins ? [config.plugins] : [];
|
|
4051
|
+
const currentResolveAlias = config.resolve?.alias;
|
|
4052
|
+
const currentResolveAliasArray = Array.isArray(currentResolveAlias)
|
|
4053
|
+
? currentResolveAlias
|
|
4054
|
+
: Object.entries(currentResolveAlias || {}).map(([find, replacement]) => ({ find, replacement }));
|
|
4055
|
+
const mergedAlias = [...dependencyAliases, ...currentResolveAliasArray];
|
|
4056
|
+
|
|
4057
|
+
config.server = {
|
|
4058
|
+
...(config.server || {}),
|
|
4059
|
+
fs: {
|
|
4060
|
+
...(config.server?.fs || {}),
|
|
4061
|
+
allow: Array.from(new Set([...currentAllow, workspaceRoot]))
|
|
4062
|
+
}
|
|
4063
|
+
};
|
|
4064
|
+
config.resolve = {
|
|
4065
|
+
...(config.resolve || {}),
|
|
4066
|
+
alias: mergedAlias
|
|
4067
|
+
};
|
|
4068
|
+
config.plugins = [...currentPlugins, createGeneratedFileProxyPlugin()];
|
|
4069
|
+
|
|
4070
|
+
return config;
|
|
4071
|
+
}
|
|
4072
|
+
};
|
|
4073
|
+
|
|
4074
|
+
export default config;
|
|
4075
|
+
`;
|
|
4076
|
+
}
|
|
4077
|
+
function getStorybookPreviewConfigContent() {
|
|
4078
|
+
return `import type { Preview } from '@storybook/react';
|
|
4079
|
+
import 'sales-frontend-design-system/core-styles';
|
|
4080
|
+
|
|
4081
|
+
const preview: Preview = {
|
|
4082
|
+
parameters: {
|
|
4083
|
+
layout: 'fullscreen',
|
|
4084
|
+
controls: {
|
|
4085
|
+
matchers: {
|
|
4086
|
+
color: /(background|color)$/i,
|
|
4087
|
+
date: /Date$/i
|
|
4088
|
+
}
|
|
4089
|
+
},
|
|
4090
|
+
options: {
|
|
4091
|
+
storySort: {
|
|
4092
|
+
method: 'alphabetical'
|
|
4093
|
+
}
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
};
|
|
4097
|
+
|
|
4098
|
+
export default preview;
|
|
4099
|
+
`;
|
|
4100
|
+
}
|
|
4101
|
+
function removeLegacyLocalServerFiles(localServerPaths, trace) {
|
|
4102
|
+
for (const fileName of LOCAL_SERVER_LEGACY_FILE_NAMES) {
|
|
4103
|
+
const targetPath = path6__default.default.join(localServerPaths.directoryPath, fileName);
|
|
4104
|
+
if (!fs7__default.default.existsSync(targetPath)) {
|
|
4105
|
+
continue;
|
|
4106
|
+
}
|
|
4107
|
+
fs7__default.default.unlinkSync(targetPath);
|
|
4108
|
+
trace("local-server:cleanup:legacy-file", targetPath);
|
|
4109
|
+
}
|
|
4110
|
+
}
|
|
4111
|
+
function ensureLocalServerSetup(paths, trace, outputDirectory, options = {}) {
|
|
4112
|
+
const localServerPaths = getLocalServerPaths(paths);
|
|
4113
|
+
const requiredWorkspaceDependencies = resolveRequiredLocalServerPublishedDependencies(paths, trace, options);
|
|
4114
|
+
const runtimeDependencies = resolveLocalServerRuntimeDependencies(paths, localServerPaths.directoryPath, trace);
|
|
4115
|
+
const generatedDependencies = {};
|
|
4116
|
+
fs7__default.default.mkdirSync(localServerPaths.directoryPath, { recursive: true });
|
|
4117
|
+
fs7__default.default.mkdirSync(localServerPaths.storybookConfigDirectoryPath, { recursive: true });
|
|
4118
|
+
removeLegacyLocalServerFiles(localServerPaths, trace);
|
|
4119
|
+
trace("local-server:setup:dir", localServerPaths.directoryPath);
|
|
4120
|
+
let packageJson = mergeLocalServerPackageJson(void 0, generatedDependencies);
|
|
4121
|
+
if (fs7__default.default.existsSync(localServerPaths.packageJsonPath)) {
|
|
4122
|
+
try {
|
|
4123
|
+
packageJson = mergeLocalServerPackageJson(readJsonFile(localServerPaths.packageJsonPath), generatedDependencies);
|
|
4124
|
+
} catch (error) {
|
|
4125
|
+
throw new Error(`\uB85C\uCEEC \uD504\uB9AC\uBDF0 \uC11C\uBC84 package.json\uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. ${getErrorSummary(error)}`);
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
4128
|
+
packageJson = {
|
|
4129
|
+
...packageJson,
|
|
4130
|
+
dependencies: {
|
|
4131
|
+
...normalizeDependencies(packageJson.dependencies),
|
|
4132
|
+
...requiredWorkspaceDependencies,
|
|
4133
|
+
...runtimeDependencies
|
|
4134
|
+
}
|
|
4135
|
+
};
|
|
4136
|
+
const normalizedPackageJson = {
|
|
4137
|
+
...packageJson,
|
|
4138
|
+
dependencies: toSortedDependencies(normalizeDependencies(packageJson.dependencies))
|
|
4139
|
+
};
|
|
4140
|
+
const nextPackageJsonContent = `${JSON.stringify(normalizedPackageJson, null, 2)}
|
|
4141
|
+
`;
|
|
4142
|
+
const previousPackageJsonContent = fs7__default.default.existsSync(localServerPaths.packageJsonPath) ? fs7__default.default.readFileSync(localServerPaths.packageJsonPath, "utf8") : "";
|
|
4143
|
+
const packageJsonChanged = previousPackageJsonContent !== nextPackageJsonContent;
|
|
4144
|
+
if (packageJsonChanged) {
|
|
4145
|
+
fs7__default.default.writeFileSync(localServerPaths.packageJsonPath, nextPackageJsonContent, "utf8");
|
|
4146
|
+
trace("local-server:setup:package-json-updated", localServerPaths.packageJsonPath);
|
|
4147
|
+
}
|
|
4148
|
+
fs7__default.default.writeFileSync(localServerPaths.storybookMainConfigPath, getStorybookMainConfigContent(), "utf8");
|
|
4149
|
+
fs7__default.default.writeFileSync(localServerPaths.storybookPreviewConfigPath, getStorybookPreviewConfigContent(), "utf8");
|
|
4150
|
+
setLocalServerPreviewTarget(paths, { outputDirectory });
|
|
4151
|
+
trace("local-server:setup:files", localServerPaths.storybookConfigDirectoryPath);
|
|
4152
|
+
ensureLocalServerDependencies(paths, trace, packageJsonChanged);
|
|
4153
|
+
return {
|
|
4154
|
+
dependencies: toSortedDependencies(normalizeDependencies(normalizedPackageJson.dependencies)),
|
|
4155
|
+
packageJsonPath: localServerPaths.packageJsonPath
|
|
4156
|
+
};
|
|
4157
|
+
}
|
|
4158
|
+
|
|
4159
|
+
// src/common/app/service-selection.ts
|
|
4160
|
+
async function chooseService(title) {
|
|
4161
|
+
return selectSingleOption(
|
|
4162
|
+
title,
|
|
4163
|
+
AI_SERVICES.map((service) => ({
|
|
4164
|
+
description: service === "codex" ? "OpenAI Codex CLI" : service === "gemini" ? "Google Gemini CLI" : "Anthropic Claude CLI",
|
|
4165
|
+
label: service,
|
|
4166
|
+
value: service
|
|
4167
|
+
}))
|
|
4168
|
+
);
|
|
4169
|
+
}
|
|
4170
|
+
async function resolveInitialService(paths, parsedArgs, trace) {
|
|
4171
|
+
const savedService = readSavedService(paths);
|
|
4172
|
+
if (parsedArgs.service) {
|
|
4173
|
+
trace("service:from-args", parsedArgs.service);
|
|
4174
|
+
saveService(paths, parsedArgs.service);
|
|
4175
|
+
if (savedService !== parsedArgs.service) {
|
|
4176
|
+
const summary = savedService ? `\uC11C\uBE44\uC2A4 \uBCC0\uACBD: ${savedService} -> ${parsedArgs.service}` : `\uCD08\uAE30 \uC11C\uBE44\uC2A4 \uC120\uD0DD: ${parsedArgs.service}`;
|
|
4177
|
+
writeServiceChangeHistory(paths, savedService, parsedArgs.service, loadRecentHistoryContext(paths));
|
|
4178
|
+
appendConversationHistory(paths, {
|
|
4179
|
+
nextService: parsedArgs.service,
|
|
4180
|
+
previousService: savedService,
|
|
4181
|
+
scope: "service-change",
|
|
4182
|
+
service: parsedArgs.service,
|
|
4183
|
+
summary,
|
|
4184
|
+
title: "\uC11C\uBE44\uC2A4 \uBCC0\uACBD"
|
|
4185
|
+
});
|
|
4186
|
+
}
|
|
4187
|
+
return parsedArgs.service;
|
|
4188
|
+
}
|
|
4189
|
+
if (savedService) {
|
|
4190
|
+
trace("service:from-file", savedService);
|
|
4191
|
+
return savedService;
|
|
4192
|
+
}
|
|
4193
|
+
trace("service:interactive:start");
|
|
4194
|
+
const selected = await chooseService("\uC0AC\uC6A9\uD560 AI \uC11C\uBE44\uC2A4\uB97C \uC120\uD0DD\uD558\uC138\uC694.");
|
|
4195
|
+
trace("service:interactive:end", selected);
|
|
4196
|
+
saveService(paths, selected);
|
|
4197
|
+
writeServiceChangeHistory(paths, "", selected, loadRecentHistoryContext(paths));
|
|
4198
|
+
appendConversationHistory(paths, {
|
|
4199
|
+
nextService: selected,
|
|
4200
|
+
previousService: "",
|
|
4201
|
+
scope: "service-change",
|
|
4202
|
+
service: selected,
|
|
4203
|
+
summary: `\uCD08\uAE30 \uC11C\uBE44\uC2A4 \uC120\uD0DD: ${selected}`,
|
|
4204
|
+
title: "\uC11C\uBE44\uC2A4 \uBCC0\uACBD"
|
|
4205
|
+
});
|
|
4206
|
+
return selected;
|
|
4207
|
+
}
|
|
4208
|
+
|
|
4209
|
+
// src/common/app/run-cli-app.ts
|
|
4210
|
+
async function runCliApp(options) {
|
|
4211
|
+
configureCliRuntime({
|
|
4212
|
+
binaryName: options.binaryName,
|
|
4213
|
+
profile: options.profile
|
|
4214
|
+
});
|
|
4215
|
+
try {
|
|
4216
|
+
await runCliAppInternal(options);
|
|
4217
|
+
} catch (error) {
|
|
4218
|
+
if (isUserTerminatedError(error)) {
|
|
4219
|
+
console.log("\uC0AC\uC6A9\uC790 \uC694\uCCAD\uC73C\uB85C \uC138\uC158\uC744 \uC885\uB8CC\uD569\uB2C8\uB2E4.");
|
|
4220
|
+
return;
|
|
4221
|
+
}
|
|
4222
|
+
if (isUserCancelledError(error)) {
|
|
4223
|
+
console.log("\uC0AC\uC6A9\uC790 \uCDE8\uC18C\uB85C \uC138\uC158\uC744 \uC885\uB8CC\uD569\uB2C8\uB2E4.");
|
|
4224
|
+
return;
|
|
4225
|
+
}
|
|
4226
|
+
const paths = getStoragePaths(process.cwd());
|
|
4227
|
+
ensureStorageDirectories(paths);
|
|
4228
|
+
const errorReportPath = writeErrorReport(paths, error, {
|
|
4229
|
+
args: process.argv.slice(2),
|
|
4230
|
+
scope: `${options.traceScope}:main`,
|
|
4231
|
+
traceMessages: getTraceMessages()
|
|
4232
|
+
});
|
|
4233
|
+
console.error("\uC8FC\uD64D\uC774 \uC2E4\uD589 \uC911 \uCE58\uBA85\uC801\uC778 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.");
|
|
4234
|
+
console.error(formatActionError(error));
|
|
4235
|
+
console.error(`\uC5D0\uB7EC \uB9AC\uD3EC\uD2B8: ${errorReportPath}`);
|
|
4236
|
+
process.exitCode = 1;
|
|
4237
|
+
} finally {
|
|
4238
|
+
await shutdownPostHogClient();
|
|
4239
|
+
}
|
|
4240
|
+
}
|
|
4241
|
+
async function runCliAppInternal(options) {
|
|
4242
|
+
const parsedArgs = parseCliArgs();
|
|
4243
|
+
await loadCommandModules();
|
|
4244
|
+
const builtinCommands = getBuiltinCommands();
|
|
4245
|
+
const builtinSlashCommands = getBuiltinSlashCommands();
|
|
4246
|
+
const builtinCommandNames = getBuiltinCommandNames();
|
|
4247
|
+
if (parsedArgs.help) {
|
|
4248
|
+
console.log(createCliHelpText(builtinCommands, void 0, options.binaryName));
|
|
4249
|
+
return;
|
|
4250
|
+
}
|
|
4251
|
+
clearTraceMessages();
|
|
4252
|
+
const trace = createTraceLogger(options.traceScope, parsedArgs);
|
|
4253
|
+
const paths = getStoragePaths(process.cwd());
|
|
4254
|
+
ensureStorageDirectories(paths);
|
|
4255
|
+
trace("main:start", JSON.stringify({ ...parsedArgs, profile: options.profile }));
|
|
4256
|
+
hydrateStreamOutputState(paths);
|
|
4257
|
+
if (shouldEnableLocalServer(options.profile)) {
|
|
4258
|
+
try {
|
|
4259
|
+
ensureLocalServerSetup(paths, trace, void 0, {
|
|
4260
|
+
refreshPublishedDependencies: true
|
|
4261
|
+
});
|
|
4262
|
+
} catch (error) {
|
|
4263
|
+
trace("local-server:setup:skip", formatActionError(error));
|
|
4264
|
+
}
|
|
4265
|
+
}
|
|
4266
|
+
const sessionRecords = [];
|
|
4267
|
+
let currentService = await resolveInitialService(paths, parsedArgs, trace);
|
|
4268
|
+
let promptHistory = loadPromptHistory(paths);
|
|
4269
|
+
printSessionBanner({
|
|
4270
|
+
commands: builtinSlashCommands,
|
|
4271
|
+
service: currentService,
|
|
4272
|
+
streamOutputEnabled: isStreamOutputEnabled(),
|
|
4273
|
+
testMode: parsedArgs.testMode,
|
|
4274
|
+
traceMode: parsedArgs.traceMode
|
|
4275
|
+
});
|
|
4276
|
+
let isRunning = true;
|
|
4277
|
+
while (isRunning) {
|
|
4278
|
+
const sessionCatalog = getSessionCommandCatalog(currentService);
|
|
4279
|
+
let input = "";
|
|
4280
|
+
try {
|
|
4281
|
+
input = await promptSessionInput({
|
|
4282
|
+
commands: sessionCatalog.slashCommands,
|
|
4283
|
+
history: promptHistory,
|
|
4284
|
+
service: currentService,
|
|
4285
|
+
testMode: parsedArgs.testMode,
|
|
4286
|
+
traceMode: parsedArgs.traceMode
|
|
4287
|
+
});
|
|
4288
|
+
} catch (error) {
|
|
4289
|
+
if (isUserTerminatedError(error)) {
|
|
4290
|
+
trace("session:input:terminated");
|
|
4291
|
+
console.log("\uC138\uC158\uC744 \uC885\uB8CC\uD569\uB2C8\uB2E4.");
|
|
4292
|
+
break;
|
|
4293
|
+
}
|
|
4294
|
+
throw error;
|
|
4295
|
+
}
|
|
4296
|
+
if (!input) {
|
|
4297
|
+
continue;
|
|
4298
|
+
}
|
|
4299
|
+
promptHistory = appendPromptHistory(paths, input);
|
|
4300
|
+
const resolvedSlashInput = resolveSlashCommandInput(input, builtinCommandNames);
|
|
4301
|
+
const traceStartIndex = getTraceMessages().length;
|
|
4302
|
+
try {
|
|
4303
|
+
let record;
|
|
4304
|
+
let shouldPrintBanner = false;
|
|
4305
|
+
let shouldExit = false;
|
|
4306
|
+
if (resolvedSlashInput?.kind === "builtin") {
|
|
4307
|
+
const definition = findJuHongECommand(resolvedSlashInput.command);
|
|
4308
|
+
if (!definition) {
|
|
4309
|
+
throw new Error(`\uB4F1\uB85D\uB41C \uC8FC\uD64D\uC774 \uBA85\uB839\uC5B4\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. command=${resolvedSlashInput.command}`);
|
|
4310
|
+
}
|
|
4311
|
+
const result = await executeJuHongECommand(definition, {
|
|
4312
|
+
args: resolvedSlashInput.args,
|
|
4313
|
+
catalog: sessionCatalog,
|
|
4314
|
+
command: resolvedSlashInput.command,
|
|
4315
|
+
input,
|
|
4316
|
+
parsedArgs,
|
|
4317
|
+
paths,
|
|
4318
|
+
rawArgs: resolvedSlashInput.rawArgs,
|
|
4319
|
+
rawCommand: resolvedSlashInput.rawCommand,
|
|
4320
|
+
service: currentService,
|
|
4321
|
+
trace
|
|
4322
|
+
});
|
|
4323
|
+
if (result.nextService) {
|
|
4324
|
+
currentService = result.nextService;
|
|
4325
|
+
}
|
|
4326
|
+
record = result.record;
|
|
4327
|
+
shouldExit = Boolean(result.shouldExit);
|
|
4328
|
+
shouldPrintBanner = Boolean(result.shouldPrintBanner);
|
|
4329
|
+
} else if (resolvedSlashInput?.kind === "native") {
|
|
4330
|
+
if (resolvedSlashInput.requestedService && resolvedSlashInput.requestedService !== currentService) {
|
|
4331
|
+
console.log(`\uD604\uC7AC \uC120\uD0DD\uB41C \uC11C\uBE44\uC2A4\uB294 ${currentService} \uC785\uB2C8\uB2E4. [${currentService}] prefix\uB85C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.`);
|
|
4332
|
+
} else {
|
|
4333
|
+
const result = await executeNativeCommand(findNativeCommand(currentService, resolvedSlashInput.command), {
|
|
4334
|
+
args: resolvedSlashInput.args,
|
|
4335
|
+
catalog: sessionCatalog,
|
|
4336
|
+
command: resolvedSlashInput.command,
|
|
4337
|
+
input,
|
|
4338
|
+
parsedArgs,
|
|
4339
|
+
paths,
|
|
4340
|
+
rawArgs: resolvedSlashInput.rawArgs,
|
|
4341
|
+
rawCommand: resolvedSlashInput.rawCommand,
|
|
4342
|
+
requestedService: resolvedSlashInput.requestedService,
|
|
4343
|
+
service: currentService,
|
|
4344
|
+
trace
|
|
4345
|
+
});
|
|
4346
|
+
record = result.record;
|
|
4347
|
+
shouldExit = Boolean(result.shouldExit);
|
|
4348
|
+
shouldPrintBanner = Boolean(result.shouldPrintBanner);
|
|
4349
|
+
}
|
|
4350
|
+
} else {
|
|
4351
|
+
record = await runChatAction({
|
|
4352
|
+
input,
|
|
4353
|
+
parsedArgs,
|
|
4354
|
+
paths,
|
|
4355
|
+
service: currentService,
|
|
4356
|
+
trace
|
|
4357
|
+
});
|
|
4358
|
+
}
|
|
4359
|
+
if (shouldPrintBanner) {
|
|
4360
|
+
printSessionBanner({
|
|
4361
|
+
commands: builtinSlashCommands,
|
|
4362
|
+
service: currentService,
|
|
4363
|
+
streamOutputEnabled: isStreamOutputEnabled(),
|
|
4364
|
+
testMode: parsedArgs.testMode,
|
|
4365
|
+
traceMode: parsedArgs.traceMode
|
|
4366
|
+
});
|
|
4367
|
+
}
|
|
4368
|
+
if (shouldExit) {
|
|
4369
|
+
trace("session:exit");
|
|
4370
|
+
isRunning = false;
|
|
4371
|
+
}
|
|
4372
|
+
if (record) {
|
|
4373
|
+
record.traceMessages = getTraceMessages().slice(traceStartIndex);
|
|
4374
|
+
sessionRecords.push(record);
|
|
4375
|
+
}
|
|
4376
|
+
} catch (error) {
|
|
4377
|
+
if (isUserTerminatedError(error)) {
|
|
4378
|
+
trace("session:terminated", input);
|
|
4379
|
+
console.log("\uC138\uC158\uC744 \uC885\uB8CC\uD569\uB2C8\uB2E4.");
|
|
4380
|
+
break;
|
|
4381
|
+
}
|
|
4382
|
+
if (isUserCancelledError(error)) {
|
|
4383
|
+
trace("action:cancelled", input);
|
|
4384
|
+
console.log("\uD604\uC7AC \uC791\uC5C5\uC744 \uCDE8\uC18C\uD558\uACE0 \uB300\uD654 \uC785\uB825 \uC0C1\uD0DC\uB85C \uB3CC\uC544\uAC11\uB2C8\uB2E4.");
|
|
4385
|
+
continue;
|
|
4386
|
+
}
|
|
4387
|
+
const errorTrace = getTraceMessages().slice(traceStartIndex);
|
|
4388
|
+
const errorReportPath = writeErrorReport(paths, error, {
|
|
4389
|
+
args: process.argv.slice(2),
|
|
4390
|
+
extraSections: [
|
|
4391
|
+
{
|
|
4392
|
+
heading: "Current Service",
|
|
4393
|
+
markdown: `\`${currentService}\``
|
|
4394
|
+
}
|
|
4395
|
+
],
|
|
4396
|
+
scope: `${options.traceScope}:action`,
|
|
4397
|
+
traceMessages: errorTrace
|
|
4398
|
+
});
|
|
4399
|
+
console.error("\uC791\uC5C5 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.");
|
|
4400
|
+
console.error(formatActionError(error));
|
|
4401
|
+
console.error(`\uC5D0\uB7EC \uB9AC\uD3EC\uD2B8: ${errorReportPath}`);
|
|
4402
|
+
}
|
|
4403
|
+
}
|
|
4404
|
+
if (parsedArgs.testMode) {
|
|
4405
|
+
const reportPath = writeTestReport(
|
|
4406
|
+
paths,
|
|
4407
|
+
options.testReportTitle,
|
|
4408
|
+
[
|
|
4409
|
+
`- \uC2DC\uC791 \uC11C\uBE44\uC2A4: \`${readSavedService(paths) || currentService}\``,
|
|
4410
|
+
`- \uCD1D \uC561\uC158 \uC218: \`${sessionRecords.length}\``,
|
|
4411
|
+
"",
|
|
4412
|
+
...sessionRecords.map(
|
|
4413
|
+
(record, index) => [
|
|
4414
|
+
`## ${index + 1}. ${record.title}`,
|
|
4415
|
+
"",
|
|
4416
|
+
`- scope: \`${record.scope}\``,
|
|
4417
|
+
`- service: \`${record.service}\``,
|
|
4418
|
+
`- skipped: \`${record.skipped}\``,
|
|
4419
|
+
`- summary: ${record.summary}`,
|
|
4420
|
+
record.historyPath ? `- history: \`${record.historyPath}\`` : "",
|
|
4421
|
+
record.previewCommand ? `- preview:
|
|
4422
|
+
\`\`\`sh
|
|
4423
|
+
${record.previewCommand}
|
|
4424
|
+
\`\`\`` : "",
|
|
4425
|
+
`- trace:
|
|
4426
|
+
\`\`\`json
|
|
4427
|
+
${JSON.stringify(record.traceMessages, null, 2)}
|
|
4428
|
+
\`\`\``
|
|
4429
|
+
].filter(Boolean).join("\n")
|
|
4430
|
+
)
|
|
4431
|
+
].join("\n")
|
|
4432
|
+
);
|
|
4433
|
+
console.log(`\uD14C\uC2A4\uD2B8 \uB9AC\uD3EC\uD2B8: ${reportPath}`);
|
|
4434
|
+
}
|
|
4435
|
+
trace("main:end", `service=${currentService}`);
|
|
4436
|
+
}
|
|
4437
|
+
|
|
4438
|
+
// src/be.ts
|
|
4439
|
+
void runCliApp({
|
|
4440
|
+
binaryName: "ju-hong-be",
|
|
4441
|
+
profile: "be",
|
|
4442
|
+
testReportTitle: "Ju-hong-be Test Session",
|
|
4443
|
+
traceScope: "be"
|
|
4444
|
+
});
|
|
4445
|
+
//# sourceMappingURL=be.cjs.map
|
|
4446
|
+
//# sourceMappingURL=be.cjs.map
|