octocode-cli 1.2.8 → 1.2.9
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/README.md +42 -35
- package/out/octocode-cli.js +36 -11767
- package/package.json +36 -36
- package/skills/README.md +42 -114
- package/skills/{octocode-code-engineer → octocode-engineer}/.claude/settings.local.json +2 -1
- package/skills/octocode-engineer/README.md +99 -0
- package/skills/octocode-engineer/SKILL.md +499 -0
- package/skills/octocode-engineer/build.mjs +29 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/eslint.config.mjs +3 -13
- package/skills/{octocode-code-engineer → octocode-engineer}/package.json +28 -27
- package/skills/octocode-engineer/references/ast-reference.md +166 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/references/cli-reference.md +80 -6
- package/skills/octocode-engineer/references/externals.md +86 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/references/output-files.md +46 -6
- package/skills/octocode-engineer/references/quality-indicators.md +202 -0
- package/skills/octocode-engineer/references/tool-workflows.md +298 -0
- package/skills/octocode-engineer/references/validation-playbooks.md +99 -0
- package/skills/octocode-engineer/scripts/ast/search.js +45 -0
- package/skills/octocode-engineer/scripts/ast/tree-search.js +27 -0
- package/skills/octocode-engineer/scripts/index.js +173 -0
- package/skills/octocode-engineer/scripts/run.js +179 -0
- package/skills/octocode-engineer/src/analysis/dependencies.ts +378 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/discovery.test.ts +57 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/discovery.ts +43 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/search.test.ts +113 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/search.ts +64 -1
- package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/tree-sitter.test.ts +118 -2
- package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/tree-sitter.ts +65 -3
- package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/ts-analyzer.test.ts +281 -1
- package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/ts-analyzer.ts +173 -3
- package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/security.test.ts +73 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/security.ts +62 -4
- package/skills/octocode-engineer/src/detector-gating.test.ts +59 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/code-quality.ts +342 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/index.ts +8 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/index.test.ts +565 -11
- package/skills/octocode-engineer/src/index.ts +468 -0
- package/skills/octocode-engineer/src/pipeline/affected.test.ts +147 -0
- package/skills/octocode-engineer/src/pipeline/affected.ts +68 -0
- package/skills/octocode-engineer/src/pipeline/baseline.test.ts +276 -0
- package/skills/octocode-engineer/src/pipeline/baseline.ts +76 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/pipeline/cli.test.ts +300 -53
- package/skills/{octocode-code-engineer → octocode-engineer}/src/pipeline/cli.ts +180 -36
- package/skills/octocode-engineer/src/pipeline/config-loader.test.ts +264 -0
- package/skills/octocode-engineer/src/pipeline/config-loader.ts +109 -0
- package/skills/octocode-engineer/src/pipeline/create-options.ts +55 -0
- package/skills/octocode-engineer/src/pipeline/health-score.test.ts +65 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/pipeline/main.ts +130 -17
- package/skills/octocode-engineer/src/pipeline/progress.ts +51 -0
- package/skills/octocode-engineer/src/pipeline/reporters.test.ts +155 -0
- package/skills/octocode-engineer/src/pipeline/reporters.ts +64 -0
- package/skills/octocode-engineer/src/reporting/graph-features.test.ts +279 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/reporting/output-contract.test.ts +6 -0
- package/skills/octocode-engineer/src/reporting/summary-md.test.ts +1066 -0
- package/skills/octocode-engineer/src/reporting/summary-md.ts +1604 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/reporting/writer.ts +136 -13
- package/skills/octocode-engineer/src/run.ts +78 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/sanity.test.ts +1 -1
- package/skills/octocode-engineer/src/types/analysis.ts +25 -0
- package/skills/octocode-engineer/src/types/collectors.ts +134 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/types/constants.ts +75 -41
- package/skills/octocode-engineer/src/types/core.ts +203 -0
- package/skills/octocode-engineer/src/types/dependency.ts +215 -0
- package/skills/octocode-engineer/src/types/file-entry.ts +108 -0
- package/skills/octocode-engineer/src/types/findings.ts +105 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/types/index.ts +60 -30
- package/skills/octocode-engineer/src/types/tree-sitter.ts +38 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/tsconfig.json +1 -0
- package/skills/octocode-research/.octocode/scan/.cache/analysis-cache.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/architecture.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/ast-trees.txt +5566 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/code-quality.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/dead-code.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/file-inventory.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/findings.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/graph.md +189 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/security.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/summary.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/summary.md +265 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/architecture.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/ast-trees.txt +5555 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/code-quality.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/dead-code.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/file-inventory.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/findings.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/graph.md +190 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/security.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/summary.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/summary.md +265 -0
- package/skills/octocode-research/CHANGELOG.md +60 -0
- package/skills/octocode-research/README.md +102 -388
- package/skills/octocode-research/SKILL.md +169 -498
- package/skills/octocode-research/package.json +19 -31
- package/skills/octocode-research/references/PARALLEL_AGENT_PROTOCOL.md +19 -0
- package/skills/octocode-research/references/SESSION_MANAGEMENT.md +38 -0
- package/skills/octocode-research/scripts/server-init.js +1 -1
- package/skills/octocode-research/scripts/server.d.ts +2 -1
- package/skills/octocode-research/scripts/server.js +329 -233
- package/skills/octocode-research/src/__tests__/integration/promptsRoutes.test.ts +180 -0
- package/skills/octocode-research/src/__tests__/integration/serverHttp.test.ts +221 -0
- package/skills/octocode-research/src/__tests__/integration/serverLifecycle.test.ts +194 -0
- package/skills/octocode-research/src/__tests__/integration/toolsRoutes.test.ts +501 -0
- package/skills/octocode-research/src/__tests__/unit/readiness.test.ts +61 -0
- package/skills/octocode-research/src/__tests__/unit/resilience.test.ts +192 -0
- package/skills/octocode-research/src/__tests__/unit/responseFactory.test.ts +172 -0
- package/skills/octocode-research/src/__tests__/unit/responseParser.test.ts +288 -0
- package/skills/octocode-research/src/__tests__/unit/schemas.test.ts +509 -0
- package/skills/octocode-research/src/index.ts +4 -124
- package/skills/octocode-research/src/middleware/queryParser.ts +0 -26
- package/skills/octocode-research/src/routes/lsp.ts +58 -59
- package/skills/octocode-research/src/routes/package.ts +35 -65
- package/skills/octocode-research/src/routes/prompts.ts +3 -3
- package/skills/octocode-research/src/routes/tools.ts +8 -20
- package/skills/octocode-research/src/server-init.ts +30 -237
- package/skills/octocode-research/src/server.ts +50 -23
- package/skills/octocode-research/src/types/errorGuards.ts +9 -80
- package/skills/octocode-research/src/types/guards.ts +0 -28
- package/skills/octocode-research/src/types/mcp.ts +11 -66
- package/skills/octocode-research/src/types/responses.ts +11 -129
- package/skills/octocode-research/src/utils/circuitBreaker.ts +0 -21
- package/skills/octocode-research/src/utils/logger.ts +1 -97
- package/skills/octocode-research/src/utils/resilience.ts +2 -12
- package/skills/octocode-research/src/utils/responseFactory.ts +0 -42
- package/skills/octocode-research/src/utils/responseParser.ts +3 -25
- package/skills/octocode-research/src/utils/retry.ts +0 -63
- package/skills/octocode-research/src/utils/routeFactory.ts +1 -1
- package/skills/octocode-research/src/validation/httpPreprocess.ts +0 -3
- package/skills/octocode-research/src/validation/index.ts +0 -1
- package/skills/octocode-research/src/validation/schemas.ts +0 -63
- package/skills/octocode-research/src/validation/toolCallSchema.ts +3 -3
- package/skills/octocode-research/tsdown.config.ts +4 -0
- package/skills/octocode-research/vitest.config.ts +3 -0
- package/skills/octocode-code-engineer/.plan/VALIDATED_PLAN.md +0 -223
- package/skills/octocode-code-engineer/README.md +0 -178
- package/skills/octocode-code-engineer/SKILL.md +0 -418
- package/skills/octocode-code-engineer/minify-scripts.mjs +0 -32
- package/skills/octocode-code-engineer/references/agent-ast-reading-rfc.md +0 -95
- package/skills/octocode-code-engineer/references/architecture-techniques.md +0 -121
- package/skills/octocode-code-engineer/references/ast-search.md +0 -210
- package/skills/octocode-code-engineer/references/ast-tree-search.md +0 -151
- package/skills/octocode-code-engineer/references/concepts.md +0 -107
- package/skills/octocode-code-engineer/references/finding-categories.md +0 -128
- package/skills/octocode-code-engineer/references/improvement-roadmap.md +0 -304
- package/skills/octocode-code-engineer/references/playbooks.md +0 -204
- package/skills/octocode-code-engineer/references/present-results.md +0 -136
- package/skills/octocode-code-engineer/references/tool-workflows.md +0 -566
- package/skills/octocode-code-engineer/references/validate-investigate.md +0 -225
- package/skills/octocode-code-engineer/scripts/analysis/dependencies.js +0 -1
- package/skills/octocode-code-engineer/scripts/analysis/dependency-summary.js +0 -1
- package/skills/octocode-code-engineer/scripts/analysis/discovery.js +0 -1
- package/skills/octocode-code-engineer/scripts/analysis/graph-analytics.js +0 -1
- package/skills/octocode-code-engineer/scripts/analysis/semantic.js +0 -1
- package/skills/octocode-code-engineer/scripts/ast/helpers.js +0 -1
- package/skills/octocode-code-engineer/scripts/ast/metrics.js +0 -1
- package/skills/octocode-code-engineer/scripts/ast/search.js +0 -2
- package/skills/octocode-code-engineer/scripts/ast/tree-search.js +0 -2
- package/skills/octocode-code-engineer/scripts/ast/tree-sitter.js +0 -1
- package/skills/octocode-code-engineer/scripts/ast/ts-analyzer.js +0 -1
- package/skills/octocode-code-engineer/scripts/collectors/chains.js +0 -1
- package/skills/octocode-code-engineer/scripts/collectors/effects.js +0 -1
- package/skills/octocode-code-engineer/scripts/collectors/input-sources.js +0 -1
- package/skills/octocode-code-engineer/scripts/collectors/performance.js +0 -1
- package/skills/octocode-code-engineer/scripts/collectors/prototype-pollution.js +0 -1
- package/skills/octocode-code-engineer/scripts/collectors/security.js +0 -1
- package/skills/octocode-code-engineer/scripts/collectors/test-profile.js +0 -1
- package/skills/octocode-code-engineer/scripts/common/is-direct-run.js +0 -1
- package/skills/octocode-code-engineer/scripts/common/utils.js +0 -1
- package/skills/octocode-code-engineer/scripts/detectors/code-quality.js +0 -1
- package/skills/octocode-code-engineer/scripts/detectors/cohesion.js +0 -1
- package/skills/octocode-code-engineer/scripts/detectors/coupling.js +0 -1
- package/skills/octocode-code-engineer/scripts/detectors/cycle.js +0 -1
- package/skills/octocode-code-engineer/scripts/detectors/dead-code.js +0 -1
- package/skills/octocode-code-engineer/scripts/detectors/import-style.js +0 -1
- package/skills/octocode-code-engineer/scripts/detectors/index.js +0 -1
- package/skills/octocode-code-engineer/scripts/detectors/security.js +0 -1
- package/skills/octocode-code-engineer/scripts/detectors/semantic.js +0 -1
- package/skills/octocode-code-engineer/scripts/detectors/shared.js +0 -1
- package/skills/octocode-code-engineer/scripts/detectors/test-quality.js +0 -1
- package/skills/octocode-code-engineer/scripts/index.js +0 -1
- package/skills/octocode-code-engineer/scripts/pipeline/cache.js +0 -1
- package/skills/octocode-code-engineer/scripts/pipeline/cli.js +0 -1
- package/skills/octocode-code-engineer/scripts/pipeline/main.js +0 -2
- package/skills/octocode-code-engineer/scripts/reporting/analysis.js +0 -1
- package/skills/octocode-code-engineer/scripts/reporting/summary-md.js +0 -1
- package/skills/octocode-code-engineer/scripts/reporting/writer.js +0 -1
- package/skills/octocode-code-engineer/scripts/types/constants.js +0 -1
- package/skills/octocode-code-engineer/scripts/types/index.js +0 -1
- package/skills/octocode-code-engineer/scripts/types/interfaces.js +0 -1
- package/skills/octocode-code-engineer/src/analysis/dependencies.ts +0 -406
- package/skills/octocode-code-engineer/src/index.ts +0 -403
- package/skills/octocode-code-engineer/src/reporting/summary-md.test.ts +0 -421
- package/skills/octocode-code-engineer/src/reporting/summary-md.ts +0 -714
- package/skills/octocode-code-engineer/src/types/interfaces.ts +0 -682
- package/skills/octocode-research/src/types/toolTypes.ts +0 -33
- package/skills/octocode-research/src/utils/logEmoji.ts +0 -103
- /package/skills/{octocode-code-engineer → octocode-engineer}/.octocode/rfc/RFC-code-engineer-weakness-fixes.md +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/architecture.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/ast-helpers.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/ast-search.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/base.css +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/block-navigation.js +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/cache.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/cli.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/clover.xml +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/collect-effects.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/collect-input-sources.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/collect-performance.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/collect-prototype-pollution.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/collect-security.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/collect-test-profile.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/coverage-final.json +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/dependencies.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/dependency-summary.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/discovery.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/favicon.png +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/graph-analytics.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/index.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/index.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/metrics.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/pipeline.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/prettify.css +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/prettify.js +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/report-analysis.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/report-writer.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/security-detectors.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/semantic-detectors.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/semantic.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/sort-arrow-sprite.png +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/sorter.js +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/summary-md.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/test-quality-detectors.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/tree-sitter-analyzer.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/ts-analyzer.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/types.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/utils.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/dependencies.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/dependency-summary.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/dependency-summary.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/graph-analytics.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/graph-analytics.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/semantic.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/semantic.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/helpers.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/helpers.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/metrics.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/metrics.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/tree-search.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/tree-search.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/chains.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/effects.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/effects.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/input-sources.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/input-sources.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/performance.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/performance.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/prototype-pollution.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/prototype-pollution.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/test-profile.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/test-profile.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/common/is-direct-run.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/common/is-direct-run.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/common/utils.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/common/utils.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/cohesion.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/coupling.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/cycle.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/dead-code.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/import-style.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/index.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/security.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/security.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/semantic.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/shared.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/test-quality.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/test-quality.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/pipeline/cache.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/pipeline/cache.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/pipeline/main.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/pipeline.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/reporting/analysis.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/reporting/analysis.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/vitest.config.ts +0 -0
|
@@ -1,175 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Server Init Script
|
|
2
|
+
* Server Init Script — Detached Daemon Launcher
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* 3. Starts server if not running
|
|
8
|
-
* 4. Waits for status "ok"
|
|
9
|
-
* 5. Outputs "ok" on success
|
|
4
|
+
* 1. Checks if server is already running (health check)
|
|
5
|
+
* 2. If running → prints "ok" and exits
|
|
6
|
+
* 3. If not → starts server as **detached** process, waits for health, prints "ok", exits
|
|
10
7
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* - Environment variable configuration
|
|
15
|
-
* - Spawn error handling
|
|
16
|
-
* - Better health check error logging
|
|
8
|
+
* Lifecycle: The server runs as an independent daemon (detached, unref'd).
|
|
9
|
+
* No client owns the server — it self-terminates after 30 minutes of idle.
|
|
10
|
+
* Every invocation of this script exits after confirming the server is healthy.
|
|
17
11
|
*
|
|
18
12
|
* Usage: npm run server-init
|
|
19
13
|
* Exit codes: 0 = success, 1 = error
|
|
20
14
|
*/
|
|
21
15
|
|
|
22
16
|
import { spawn } from 'child_process';
|
|
23
|
-
import {
|
|
24
|
-
existsSync,
|
|
25
|
-
mkdirSync,
|
|
26
|
-
unlinkSync,
|
|
27
|
-
readFileSync,
|
|
28
|
-
constants,
|
|
29
|
-
openSync,
|
|
30
|
-
writeSync,
|
|
31
|
-
closeSync,
|
|
32
|
-
statSync,
|
|
33
|
-
} from 'fs';
|
|
34
17
|
import { join } from 'path';
|
|
35
|
-
import { homedir } from 'os';
|
|
36
18
|
|
|
37
19
|
// =============================================================================
|
|
38
20
|
// Configuration (Environment Variables)
|
|
39
21
|
// =============================================================================
|
|
40
22
|
|
|
41
|
-
const PORT = parseInt(process.env.OCTOCODE_PORT || '1987', 10);
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
const LOCK_FILE = join(LOCK_DIR, 'server-init.lock');
|
|
45
|
-
const LOCK_TIMEOUT_MS = parseInt(process.env.OCTOCODE_LOCK_TIMEOUT || '60000', 10);
|
|
23
|
+
const PORT = parseInt(process.env.OCTOCODE_RESEARCH_PORT || process.env.OCTOCODE_PORT || '1987', 10);
|
|
24
|
+
const HOST = process.env.OCTOCODE_RESEARCH_HOST || 'localhost';
|
|
25
|
+
const HEALTH_URL = `http://${HOST}:${PORT}/health`;
|
|
46
26
|
const MAX_WAIT_MS = parseInt(process.env.OCTOCODE_INIT_TIMEOUT || '30000', 10);
|
|
47
27
|
const POLL_INTERVAL_MS = parseInt(process.env.OCTOCODE_POLL_INTERVAL || '500', 10);
|
|
48
28
|
|
|
49
|
-
interface LockData {
|
|
50
|
-
pid: number;
|
|
51
|
-
timestamp: number;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
29
|
interface HealthResponse {
|
|
55
30
|
status: 'ok' | 'initializing' | string;
|
|
56
31
|
}
|
|
57
32
|
|
|
58
33
|
// =============================================================================
|
|
59
|
-
//
|
|
60
|
-
// =============================================================================
|
|
61
|
-
|
|
62
|
-
function ensureLockDir(): void {
|
|
63
|
-
if (!existsSync(LOCK_DIR)) {
|
|
64
|
-
mkdirSync(LOCK_DIR, { recursive: true });
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function isLockStale(lockPath: string): boolean {
|
|
69
|
-
if (!existsSync(lockPath)) return true;
|
|
70
|
-
|
|
71
|
-
try {
|
|
72
|
-
const content = readFileSync(lockPath, 'utf-8');
|
|
73
|
-
if (content && content.trim().length > 0) {
|
|
74
|
-
const data: LockData = JSON.parse(content);
|
|
75
|
-
|
|
76
|
-
// Check timestamp
|
|
77
|
-
if (Date.now() - data.timestamp > LOCK_TIMEOUT_MS) {
|
|
78
|
-
return true;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Check if process exists
|
|
82
|
-
try {
|
|
83
|
-
process.kill(data.pid, 0);
|
|
84
|
-
return false; // Process exists, lock is valid
|
|
85
|
-
} catch {
|
|
86
|
-
return true; // Process doesn't exist
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
} catch {
|
|
90
|
-
// Fall back to mtime check
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Fallback: check file modification time
|
|
94
|
-
try {
|
|
95
|
-
const stats = statSync(lockPath);
|
|
96
|
-
return Date.now() - stats.mtimeMs > LOCK_TIMEOUT_MS;
|
|
97
|
-
} catch {
|
|
98
|
-
return true;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function safeUnlink(filePath: string): void {
|
|
103
|
-
try {
|
|
104
|
-
if (existsSync(filePath)) {
|
|
105
|
-
unlinkSync(filePath);
|
|
106
|
-
}
|
|
107
|
-
} catch {
|
|
108
|
-
// Ignore errors
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
async function acquireLock(): Promise<boolean> {
|
|
113
|
-
ensureLockDir();
|
|
114
|
-
|
|
115
|
-
// Only retry for stale lock removal race condition, not for valid locks
|
|
116
|
-
const STALE_RETRY_LIMIT = 3;
|
|
117
|
-
|
|
118
|
-
for (let attempt = 0; attempt < STALE_RETRY_LIMIT; attempt++) {
|
|
119
|
-
try {
|
|
120
|
-
// Atomic create - fails with EEXIST if file exists
|
|
121
|
-
const fd = openSync(LOCK_FILE, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY);
|
|
122
|
-
|
|
123
|
-
const lockData: LockData = {
|
|
124
|
-
pid: process.pid,
|
|
125
|
-
timestamp: Date.now(),
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
writeSync(fd, JSON.stringify(lockData));
|
|
129
|
-
closeSync(fd);
|
|
130
|
-
return true;
|
|
131
|
-
} catch (err: unknown) {
|
|
132
|
-
const nodeErr = err as NodeJS.ErrnoException;
|
|
133
|
-
|
|
134
|
-
if (nodeErr.code === 'EEXIST') {
|
|
135
|
-
// Lock exists - check if stale
|
|
136
|
-
if (isLockStale(LOCK_FILE)) {
|
|
137
|
-
safeUnlink(LOCK_FILE);
|
|
138
|
-
// Small delay to avoid tight loop on stale lock race
|
|
139
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
140
|
-
continue; // Retry after removing stale lock
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Lock is VALID - another process is starting server
|
|
144
|
-
// Don't retry, let caller fall back to waiting for server
|
|
145
|
-
return false;
|
|
146
|
-
} else {
|
|
147
|
-
// Unexpected error
|
|
148
|
-
console.error(`[server-init] Lock error: ${nodeErr.message}`);
|
|
149
|
-
return false;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return false; // Failed after stale lock retries
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function releaseLock(): void {
|
|
158
|
-
try {
|
|
159
|
-
if (existsSync(LOCK_FILE)) {
|
|
160
|
-
const data: LockData = JSON.parse(readFileSync(LOCK_FILE, 'utf-8'));
|
|
161
|
-
// Only release if we own the lock
|
|
162
|
-
if (data.pid === process.pid) {
|
|
163
|
-
unlinkSync(LOCK_FILE);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
} catch {
|
|
167
|
-
// Ignore errors during cleanup
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// =============================================================================
|
|
172
|
-
// Health Check (Improved error handling)
|
|
34
|
+
// Health Check
|
|
173
35
|
// =============================================================================
|
|
174
36
|
|
|
175
37
|
async function checkHealth(): Promise<HealthResponse | null> {
|
|
@@ -184,7 +46,6 @@ async function checkHealth(): Promise<HealthResponse | null> {
|
|
|
184
46
|
|
|
185
47
|
return (await response.json()) as HealthResponse;
|
|
186
48
|
} catch (error: unknown) {
|
|
187
|
-
// Only log unexpected errors, not connection refused (expected when server down)
|
|
188
49
|
if (error instanceof Error && !error.message.includes('ECONNREFUSED')) {
|
|
189
50
|
console.error(`[server-init] Health check error: ${error.message}`);
|
|
190
51
|
}
|
|
@@ -193,7 +54,7 @@ async function checkHealth(): Promise<HealthResponse | null> {
|
|
|
193
54
|
}
|
|
194
55
|
|
|
195
56
|
// =============================================================================
|
|
196
|
-
// Server Start (
|
|
57
|
+
// Server Start — detached daemon (survives parent exit)
|
|
197
58
|
// =============================================================================
|
|
198
59
|
|
|
199
60
|
function startServer(): Promise<void> {
|
|
@@ -202,21 +63,20 @@ function startServer(): Promise<void> {
|
|
|
202
63
|
const serverScript = join(scriptDir, 'server.js');
|
|
203
64
|
|
|
204
65
|
const child = spawn('node', [serverScript], {
|
|
205
|
-
detached: true,
|
|
206
66
|
stdio: 'ignore',
|
|
207
67
|
cwd: scriptDir,
|
|
68
|
+
detached: true,
|
|
208
69
|
});
|
|
209
70
|
|
|
210
|
-
// Handle spawn errors (e.g., ENOENT if node not found)
|
|
211
71
|
child.on('error', (err) => {
|
|
212
72
|
console.error(`[server-init] Failed to start server: ${err.message}`);
|
|
213
73
|
reject(err);
|
|
214
74
|
});
|
|
215
75
|
|
|
216
|
-
|
|
76
|
+
child.unref();
|
|
77
|
+
|
|
217
78
|
setTimeout(() => {
|
|
218
|
-
child.
|
|
219
|
-
console.log(`[server-init] Started server process (pid: ${child.pid})`);
|
|
79
|
+
console.log(`[server-init] Spawned detached server process (pid: ${child.pid})`);
|
|
220
80
|
resolve();
|
|
221
81
|
}, 100);
|
|
222
82
|
});
|
|
@@ -242,7 +102,6 @@ async function waitForReady(): Promise<boolean> {
|
|
|
242
102
|
}
|
|
243
103
|
|
|
244
104
|
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
245
|
-
// Gradual backoff up to 2s
|
|
246
105
|
pollInterval = Math.min(pollInterval * 1.5, 2000);
|
|
247
106
|
}
|
|
248
107
|
|
|
@@ -254,19 +113,15 @@ async function waitForReady(): Promise<boolean> {
|
|
|
254
113
|
// =============================================================================
|
|
255
114
|
|
|
256
115
|
async function main(): Promise<void> {
|
|
257
|
-
//
|
|
258
|
-
// FAST PATH: Check if server is already running (no lock needed)
|
|
259
|
-
// ==========================================================================
|
|
116
|
+
// Fast path: server already running → exit immediately
|
|
260
117
|
const initialHealth = await checkHealth();
|
|
261
118
|
|
|
262
119
|
if (initialHealth?.status === 'ok') {
|
|
263
|
-
// Server is running - immediate success, no lock needed
|
|
264
120
|
console.log('ok');
|
|
265
121
|
process.exit(0);
|
|
266
122
|
}
|
|
267
123
|
|
|
268
124
|
if (initialHealth?.status === 'initializing') {
|
|
269
|
-
// Server is starting - just wait for it, no lock needed
|
|
270
125
|
console.log('[server-init] Server is initializing, waiting...');
|
|
271
126
|
const ready = await waitForReady();
|
|
272
127
|
if (ready) {
|
|
@@ -278,86 +133,24 @@ async function main(): Promise<void> {
|
|
|
278
133
|
}
|
|
279
134
|
}
|
|
280
135
|
|
|
281
|
-
//
|
|
282
|
-
|
|
283
|
-
// ==========================================================================
|
|
284
|
-
const lockAcquired = await acquireLock();
|
|
285
|
-
|
|
286
|
-
if (!lockAcquired) {
|
|
287
|
-
// Another process is starting the server - wait for it
|
|
288
|
-
console.log('[server-init] Another process is starting server, waiting...');
|
|
289
|
-
const ready = await waitForReady();
|
|
290
|
-
if (ready) {
|
|
291
|
-
console.log('ok');
|
|
292
|
-
process.exit(0);
|
|
293
|
-
} else {
|
|
294
|
-
console.error('[server-init] ERROR: Timeout waiting for server');
|
|
295
|
-
process.exit(1);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
136
|
+
// Server not running — start it (detached)
|
|
137
|
+
console.log('[server-init] Server not running, starting detached daemon...');
|
|
298
138
|
|
|
299
139
|
try {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
if (health?.status === 'ok') {
|
|
304
|
-
console.log('ok');
|
|
305
|
-
releaseLock();
|
|
306
|
-
process.exit(0);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
if (health?.status === 'initializing') {
|
|
310
|
-
console.log('[server-init] Server is initializing, waiting...');
|
|
311
|
-
const ready = await waitForReady();
|
|
312
|
-
if (ready) {
|
|
313
|
-
console.log('ok');
|
|
314
|
-
releaseLock();
|
|
315
|
-
process.exit(0);
|
|
316
|
-
} else {
|
|
317
|
-
console.error('[server-init] ERROR: Server stuck in initializing state');
|
|
318
|
-
releaseLock();
|
|
319
|
-
process.exit(1);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Server not running - start it
|
|
324
|
-
console.log('[server-init] Server not running, starting...');
|
|
325
|
-
|
|
326
|
-
try {
|
|
327
|
-
await startServer();
|
|
328
|
-
} catch {
|
|
329
|
-
console.error('[server-init] ERROR: Failed to spawn server process');
|
|
330
|
-
releaseLock();
|
|
331
|
-
process.exit(1);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// Wait for server to be ready
|
|
335
|
-
const ready = await waitForReady();
|
|
336
|
-
if (ready) {
|
|
337
|
-
console.log('ok');
|
|
338
|
-
releaseLock();
|
|
339
|
-
process.exit(0);
|
|
340
|
-
} else {
|
|
341
|
-
console.error('[server-init] ERROR: Server failed to start within timeout');
|
|
342
|
-
releaseLock();
|
|
343
|
-
process.exit(1);
|
|
344
|
-
}
|
|
345
|
-
} catch (error) {
|
|
346
|
-
console.error('[server-init] ERROR:', error instanceof Error ? error.message : error);
|
|
347
|
-
releaseLock();
|
|
140
|
+
await startServer();
|
|
141
|
+
} catch {
|
|
142
|
+
console.error('[server-init] ERROR: Failed to spawn server process');
|
|
348
143
|
process.exit(1);
|
|
349
144
|
}
|
|
350
|
-
}
|
|
351
145
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
}
|
|
146
|
+
const ready = await waitForReady();
|
|
147
|
+
if (!ready) {
|
|
148
|
+
console.error('[server-init] ERROR: Server failed to start within timeout');
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
357
151
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
});
|
|
152
|
+
console.log('ok');
|
|
153
|
+
process.exit(0);
|
|
154
|
+
}
|
|
362
155
|
|
|
363
156
|
main();
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import express, { type Express, type Request, type Response, type NextFunction } from 'express';
|
|
2
2
|
import type { Server } from 'http';
|
|
3
|
+
import { writeFileSync, unlinkSync, mkdirSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { homedir } from 'os';
|
|
3
6
|
import { toolsRoutes } from './routes/tools.js';
|
|
4
7
|
import { promptsRoutes } from './routes/prompts.js';
|
|
5
8
|
import { errorHandler } from './middleware/errorHandler.js';
|
|
@@ -12,6 +15,8 @@ import { agentLog, successLog, errorLog, dimLog, warnLog } from './utils/colors.
|
|
|
12
15
|
import { fireAndForgetWithTimeout } from './utils/asyncTimeout.js';
|
|
13
16
|
import { errorQueue } from './utils/errorQueue.js';
|
|
14
17
|
|
|
18
|
+
declare const __PACKAGE_VERSION__: string;
|
|
19
|
+
|
|
15
20
|
const HOST = process.env.OCTOCODE_RESEARCH_HOST || 'localhost';
|
|
16
21
|
|
|
17
22
|
const getPort = (raw?: string): number => {
|
|
@@ -31,7 +36,7 @@ const getPort = (raw?: string): number => {
|
|
|
31
36
|
return port;
|
|
32
37
|
};
|
|
33
38
|
|
|
34
|
-
const PORT = getPort(process.env.OCTOCODE_RESEARCH_PORT);
|
|
39
|
+
const PORT = getPort(process.env.OCTOCODE_RESEARCH_PORT || process.env.OCTOCODE_PORT);
|
|
35
40
|
|
|
36
41
|
const MAX_IDLE_TIME_MS = 30 * 60 * 1000; // 30 minutes idle before restart
|
|
37
42
|
const IDLE_CHECK_INTERVAL_MS = 120 * 1000; // Check every 2 minute
|
|
@@ -41,6 +46,27 @@ let lastRequestTime: number = Date.now();
|
|
|
41
46
|
let idleCheckInterval: NodeJS.Timeout | null = null;
|
|
42
47
|
let isShuttingDown = false;
|
|
43
48
|
|
|
49
|
+
// PID file for daemon lifecycle management
|
|
50
|
+
const OCTOCODE_DIR = process.env.OCTOCODE_HOME || join(homedir(), '.octocode');
|
|
51
|
+
export const PID_FILE = join(OCTOCODE_DIR, `research-server-${PORT}.pid`);
|
|
52
|
+
|
|
53
|
+
function writePidFile(): void {
|
|
54
|
+
try {
|
|
55
|
+
mkdirSync(OCTOCODE_DIR, { recursive: true, mode: 0o700 });
|
|
56
|
+
writeFileSync(PID_FILE, String(process.pid), { mode: 0o600 });
|
|
57
|
+
} catch {
|
|
58
|
+
// Non-fatal — server works without PID file
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function removePidFile(): void {
|
|
63
|
+
try {
|
|
64
|
+
unlinkSync(PID_FILE);
|
|
65
|
+
} catch {
|
|
66
|
+
// Already removed or never written
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
44
70
|
/**
|
|
45
71
|
* Check if server has been idle and should restart
|
|
46
72
|
*/
|
|
@@ -52,7 +78,6 @@ function checkIdleRestart(): void {
|
|
|
52
78
|
console.log(warnLog(`⚠️ Server idle for ${idleSeconds}s (>${MAX_IDLE_TIME_MS / 1000}s). Initiating restart...`));
|
|
53
79
|
gracefulShutdown('IDLE_TIMEOUT');
|
|
54
80
|
} else if (idleTime > MAX_IDLE_TIME_MS / 2) {
|
|
55
|
-
// Log warning at 50% threshold (30 minutes)
|
|
56
81
|
console.log(dimLog(`⏰ Idle: ${idleSeconds}s / ${MAX_IDLE_TIME_MS / 1000}s`));
|
|
57
82
|
}
|
|
58
83
|
}
|
|
@@ -110,9 +135,10 @@ export async function createServer(): Promise<Express> {
|
|
|
110
135
|
status: initialized ? 'ok' : 'initializing',
|
|
111
136
|
host: HOST,
|
|
112
137
|
port: PORT,
|
|
113
|
-
version:
|
|
138
|
+
version: __PACKAGE_VERSION__,
|
|
114
139
|
uptime: Math.floor(process.uptime()),
|
|
115
|
-
processManager: '
|
|
140
|
+
processManager: 'self (detached daemon)',
|
|
141
|
+
pid: process.pid,
|
|
116
142
|
// Idle tracking info
|
|
117
143
|
idle: {
|
|
118
144
|
currentMs: idleTimeMs,
|
|
@@ -150,11 +176,13 @@ export async function createServer(): Promise<Express> {
|
|
|
150
176
|
code: 'NOT_FOUND',
|
|
151
177
|
availableRoutes: [
|
|
152
178
|
'GET /health',
|
|
179
|
+
'GET /tools/initContext',
|
|
153
180
|
'GET /tools/list',
|
|
181
|
+
'GET /tools/info',
|
|
154
182
|
'GET /tools/info/:toolName',
|
|
183
|
+
'GET /tools/metadata',
|
|
155
184
|
'GET /tools/schemas',
|
|
156
185
|
'GET /tools/system',
|
|
157
|
-
'GET /tools/initContext',
|
|
158
186
|
'POST /tools/call/:toolName',
|
|
159
187
|
'GET /prompts/list',
|
|
160
188
|
'GET /prompts/info/:promptName',
|
|
@@ -170,11 +198,11 @@ export async function createServer(): Promise<Express> {
|
|
|
170
198
|
}
|
|
171
199
|
|
|
172
200
|
/**
|
|
173
|
-
* Graceful shutdown handler
|
|
174
|
-
*
|
|
201
|
+
* Graceful shutdown handler.
|
|
202
|
+
* Triggered by SIGTERM, SIGINT, or idle timeout. Cleans up all resources
|
|
203
|
+
* including PID file, circuit breakers, and HTTP connections.
|
|
175
204
|
*/
|
|
176
205
|
function gracefulShutdown(signal: string): void {
|
|
177
|
-
// Prevent double-shutdown (e.g., SIGINT + SIGTERM in quick succession)
|
|
178
206
|
if (isShuttingDown) {
|
|
179
207
|
console.log(dimLog(`Already shutting down, ignoring ${signal}`));
|
|
180
208
|
return;
|
|
@@ -183,25 +211,27 @@ function gracefulShutdown(signal: string): void {
|
|
|
183
211
|
|
|
184
212
|
console.log(agentLog(`\n🛑 Received ${signal}. Starting graceful shutdown...`));
|
|
185
213
|
|
|
186
|
-
|
|
187
|
-
const FORCE_EXIT_TIMEOUT_MS = 110 * 1000;
|
|
214
|
+
const FORCE_EXIT_TIMEOUT_MS = 30 * 1000;
|
|
188
215
|
setTimeout(() => {
|
|
189
216
|
console.log(warnLog('⚠️ Force exiting due to drain timeout'));
|
|
190
217
|
process.exit(1);
|
|
191
218
|
}, FORCE_EXIT_TIMEOUT_MS).unref();
|
|
192
219
|
|
|
193
|
-
// 1.
|
|
220
|
+
// 1. Remove PID file immediately
|
|
221
|
+
removePidFile();
|
|
222
|
+
|
|
223
|
+
// 2. Stop idle check interval
|
|
194
224
|
stopIdleCheck();
|
|
195
225
|
|
|
196
|
-
//
|
|
226
|
+
// 3. Stop circuit cleanup interval
|
|
197
227
|
stopCircuitCleanup();
|
|
198
228
|
console.log(successLog('✅ Circuit cleanup interval stopped'));
|
|
199
229
|
|
|
200
|
-
//
|
|
230
|
+
// 4. Clear circuit breakers
|
|
201
231
|
clearAllCircuits();
|
|
202
232
|
console.log(successLog('✅ Circuit breakers cleared'));
|
|
203
233
|
|
|
204
|
-
//
|
|
234
|
+
// 5. Close HTTP server (waits for connections to drain)
|
|
205
235
|
if (server) {
|
|
206
236
|
console.log(dimLog('⏳ Waiting for connections to drain...'));
|
|
207
237
|
server.close((err) => {
|
|
@@ -210,7 +240,7 @@ function gracefulShutdown(signal: string): void {
|
|
|
210
240
|
process.exit(1);
|
|
211
241
|
}
|
|
212
242
|
console.log(successLog('✅ HTTP server closed'));
|
|
213
|
-
process.exit(0);
|
|
243
|
+
process.exit(0);
|
|
214
244
|
});
|
|
215
245
|
} else {
|
|
216
246
|
process.exit(0);
|
|
@@ -223,7 +253,8 @@ export async function startServer(): Promise<void> {
|
|
|
223
253
|
await new Promise<void>((resolve) => {
|
|
224
254
|
const httpServer = app.listen(PORT, HOST, () => {
|
|
225
255
|
server = httpServer;
|
|
226
|
-
|
|
256
|
+
writePidFile();
|
|
257
|
+
console.log(agentLog(`🔍 Octocode Research Server running on http://${HOST}:${PORT} (pid: ${process.pid})`));
|
|
227
258
|
console.log(dimLog(`⏳ initializing context...`));
|
|
228
259
|
|
|
229
260
|
// Start background initialization (Warm Start)
|
|
@@ -244,18 +275,14 @@ export async function startServer(): Promise<void> {
|
|
|
244
275
|
console.log(dimLog(` GET /tools/initContext - System prompt + schemas (LOAD FIRST)`));
|
|
245
276
|
console.log(dimLog(` GET /tools/system - System prompt only`));
|
|
246
277
|
console.log(dimLog(` GET /tools/list - List all tools`));
|
|
278
|
+
console.log(dimLog(` GET /tools/info - All tools with details`));
|
|
247
279
|
console.log(dimLog(` GET /tools/info/:toolName - Tool schema (BEFORE calling)`));
|
|
280
|
+
console.log(dimLog(` GET /tools/metadata - Raw MCP metadata`));
|
|
248
281
|
console.log(dimLog(` GET /tools/schemas - All tools schemas`));
|
|
249
282
|
console.log(dimLog(` POST /tools/call/:toolName - Execute tool`));
|
|
250
283
|
console.log(dimLog(` GET /prompts/list - List prompts`));
|
|
251
284
|
console.log(dimLog(` GET /prompts/info/:name - Get prompt content`));
|
|
252
285
|
|
|
253
|
-
// Signal PM2 that we're ready
|
|
254
|
-
if (process.send) {
|
|
255
|
-
process.send('ready');
|
|
256
|
-
console.log(dimLog('📡 PM2 ready signal sent'));
|
|
257
|
-
}
|
|
258
|
-
|
|
259
286
|
// Log session initialization after server is ready
|
|
260
287
|
fireAndForgetWithTimeout(
|
|
261
288
|
() => logSessionInit(),
|
|
@@ -272,7 +299,7 @@ export async function startServer(): Promise<void> {
|
|
|
272
299
|
});
|
|
273
300
|
}
|
|
274
301
|
|
|
275
|
-
// Signal handlers
|
|
302
|
+
// Signal handlers — server is a detached daemon, handles its own signals
|
|
276
303
|
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
|
277
304
|
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
|
|
278
305
|
|
|
@@ -8,40 +8,20 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
// =============================================================================
|
|
11
|
-
//
|
|
11
|
+
// Internal Interfaces & Guards
|
|
12
12
|
// =============================================================================
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
* Error with HTTP status code (e.g., from API responses)
|
|
16
|
-
*/
|
|
17
|
-
export interface ErrorWithStatus {
|
|
14
|
+
interface ErrorWithStatus {
|
|
18
15
|
status: number;
|
|
19
16
|
message?: string;
|
|
20
17
|
}
|
|
21
18
|
|
|
22
|
-
|
|
23
|
-
* Error with error code (e.g., Node.js system errors)
|
|
24
|
-
*/
|
|
25
|
-
export interface ErrorWithCode {
|
|
19
|
+
interface ErrorWithCode {
|
|
26
20
|
code: string;
|
|
27
21
|
message?: string;
|
|
28
22
|
}
|
|
29
23
|
|
|
30
|
-
|
|
31
|
-
* Error with headers (e.g., rate limit responses)
|
|
32
|
-
*/
|
|
33
|
-
export interface ErrorWithHeaders {
|
|
34
|
-
headers: Record<string, string>;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// =============================================================================
|
|
38
|
-
// Type Guards
|
|
39
|
-
// =============================================================================
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Check if error has a status property (number)
|
|
43
|
-
*/
|
|
44
|
-
export function isErrorWithStatus(err: unknown): err is ErrorWithStatus {
|
|
24
|
+
function isErrorWithStatus(err: unknown): err is ErrorWithStatus {
|
|
45
25
|
return (
|
|
46
26
|
err !== null &&
|
|
47
27
|
typeof err === 'object' &&
|
|
@@ -50,10 +30,7 @@ export function isErrorWithStatus(err: unknown): err is ErrorWithStatus {
|
|
|
50
30
|
);
|
|
51
31
|
}
|
|
52
32
|
|
|
53
|
-
|
|
54
|
-
* Check if error has a code property (string)
|
|
55
|
-
*/
|
|
56
|
-
export function isErrorWithCode(err: unknown): err is ErrorWithCode {
|
|
33
|
+
function isErrorWithCode(err: unknown): err is ErrorWithCode {
|
|
57
34
|
return (
|
|
58
35
|
err !== null &&
|
|
59
36
|
typeof err === 'object' &&
|
|
@@ -62,10 +39,7 @@ export function isErrorWithCode(err: unknown): err is ErrorWithCode {
|
|
|
62
39
|
);
|
|
63
40
|
}
|
|
64
41
|
|
|
65
|
-
|
|
66
|
-
* Check if error has a message property (string)
|
|
67
|
-
*/
|
|
68
|
-
export function hasMessage(err: unknown): err is { message: string } {
|
|
42
|
+
function hasMessage(err: unknown): err is { message: string } {
|
|
69
43
|
return (
|
|
70
44
|
err !== null &&
|
|
71
45
|
typeof err === 'object' &&
|
|
@@ -74,77 +48,32 @@ export function hasMessage(err: unknown): err is { message: string } {
|
|
|
74
48
|
);
|
|
75
49
|
}
|
|
76
50
|
|
|
77
|
-
/**
|
|
78
|
-
* Check if error has headers property (object)
|
|
79
|
-
*/
|
|
80
|
-
export function hasHeaders(err: unknown): err is ErrorWithHeaders {
|
|
81
|
-
return (
|
|
82
|
-
err !== null &&
|
|
83
|
-
typeof err === 'object' &&
|
|
84
|
-
'headers' in err &&
|
|
85
|
-
typeof (err as { headers: unknown }).headers === 'object' &&
|
|
86
|
-
(err as ErrorWithHeaders).headers !== null
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
51
|
// =============================================================================
|
|
91
|
-
//
|
|
52
|
+
// Public API
|
|
92
53
|
// =============================================================================
|
|
93
54
|
|
|
94
|
-
/**
|
|
95
|
-
* Safely get status from error (undefined if not present)
|
|
96
|
-
*/
|
|
97
55
|
export function getErrorStatus(err: unknown): number | undefined {
|
|
98
56
|
return isErrorWithStatus(err) ? err.status : undefined;
|
|
99
57
|
}
|
|
100
58
|
|
|
101
|
-
|
|
102
|
-
* Safely get code from error (undefined if not present)
|
|
103
|
-
*/
|
|
104
|
-
export function getErrorCode(err: unknown): string | undefined {
|
|
59
|
+
function getErrorCode(err: unknown): string | undefined {
|
|
105
60
|
return isErrorWithCode(err) ? err.code : undefined;
|
|
106
61
|
}
|
|
107
62
|
|
|
108
|
-
|
|
109
|
-
* Safely get message from error (undefined if not present)
|
|
110
|
-
*/
|
|
111
|
-
export function getErrorMessage(err: unknown): string | undefined {
|
|
63
|
+
function getErrorMessage(err: unknown): string | undefined {
|
|
112
64
|
return hasMessage(err) ? err.message : undefined;
|
|
113
65
|
}
|
|
114
66
|
|
|
115
|
-
/**
|
|
116
|
-
* Safely get header value from error (undefined if not present)
|
|
117
|
-
*/
|
|
118
|
-
export function getErrorHeader(err: unknown, header: string): string | undefined {
|
|
119
|
-
if (hasHeaders(err)) {
|
|
120
|
-
return err.headers[header];
|
|
121
|
-
}
|
|
122
|
-
return undefined;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// =============================================================================
|
|
126
|
-
// Composite Checks
|
|
127
|
-
// =============================================================================
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Check if error status is in a given list
|
|
131
|
-
*/
|
|
132
67
|
export function hasStatusIn(err: unknown, statuses: readonly number[]): boolean {
|
|
133
68
|
const status = getErrorStatus(err);
|
|
134
69
|
return status !== undefined && statuses.includes(status);
|
|
135
70
|
}
|
|
136
71
|
|
|
137
|
-
/**
|
|
138
|
-
* Check if error code is in a given list
|
|
139
|
-
*/
|
|
140
72
|
export function hasCodeIn(err: unknown, codes: readonly string[]): boolean {
|
|
141
73
|
const code = getErrorCode(err);
|
|
142
74
|
return code !== undefined && codes.includes(code);
|
|
143
75
|
}
|
|
144
76
|
|
|
145
|
-
/**
|
|
146
|
-
* Check if error message matches any pattern
|
|
147
|
-
*/
|
|
148
77
|
export function messageMatches(err: unknown, patterns: readonly RegExp[]): boolean {
|
|
149
78
|
const message = getErrorMessage(err);
|
|
150
79
|
return message !== undefined && patterns.some((p) => p.test(message));
|