monomind 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/design/design-monodesign.md +121 -0
- package/.claude/agents/github/issue-tracker.md +12 -12
- package/.claude/agents/github/pr-manager.md +10 -10
- package/.claude/agents/github/release-manager.md +49 -105
- package/.claude/agents/github/repo-architect.md +73 -92
- package/.claude/agents/github/sync-coordinator.md +55 -123
- package/.claude/agents/marketing/marketing-competitive-content.md +155 -0
- package/.claude/agents/marketing/marketing-content-creator.md +13 -0
- package/.claude/agents/marketing/marketing-cro-specialist.md +147 -0
- package/.claude/agents/marketing/marketing-email-specialist.md +90 -0
- package/.claude/agents/marketing/marketing-launch-strategist.md +129 -0
- package/.claude/agents/marketing/marketing-pricing-strategist.md +127 -0
- package/.claude/agents/specialists/integration-architect.md +94 -0
- package/.claude/commands/agents/README.md +4 -0
- package/.claude/commands/agents/agent-capabilities.md +6 -2
- package/.claude/commands/agents/agent-coordination.md +4 -0
- package/.claude/commands/agents/agent-spawning.md +4 -0
- package/.claude/commands/agents/agent-types.md +6 -2
- package/.claude/commands/analysis/README.md +14 -5
- package/.claude/commands/analysis/bottleneck-detect.md +30 -123
- package/.claude/commands/analysis/performance-bottlenecks.md +14 -14
- package/.claude/commands/analysis/performance-report.md +38 -11
- package/.claude/commands/analysis/token-efficiency.md +13 -16
- package/.claude/commands/analysis/token-usage.md +34 -12
- package/.claude/commands/automation/README.md +15 -5
- package/.claude/commands/automation/auto-agent.md +49 -85
- package/.claude/commands/automation/self-healing.md +20 -18
- package/.claude/commands/automation/session-memory.md +28 -29
- package/.claude/commands/automation/smart-agents.md +17 -9
- package/.claude/commands/automation/smart-spawn.md +52 -11
- package/.claude/commands/automation/workflow-select.md +46 -11
- package/.claude/commands/browse.md +5 -0
- package/.claude/commands/coordination/README.md +9 -5
- package/.claude/commands/coordination/agent-spawn.md +53 -9
- package/.claude/commands/coordination/swarm-init.md +39 -42
- package/.claude/commands/coordination/task-orchestrate.md +65 -11
- package/.claude/commands/github/README.md +21 -8
- package/.claude/commands/github/github-modes.md +9 -5
- package/.claude/commands/github/issue-tracker.md +34 -33
- package/.claude/commands/github/pr-manager.md +20 -17
- package/.claude/commands/github/release-manager.md +37 -49
- package/.claude/commands/github/repo-architect.md +39 -41
- package/.claude/commands/github/sync-coordinator.md +45 -49
- package/.claude/commands/hive-mind/README.md +42 -17
- package/.claude/commands/hive-mind/hive-mind-consensus.md +68 -4
- package/.claude/commands/hive-mind/hive-mind-init.md +55 -5
- package/.claude/commands/hive-mind/hive-mind-memory.md +69 -4
- package/.claude/commands/hive-mind/hive-mind-spawn.md +71 -10
- package/.claude/commands/hive-mind/hive-mind-status.md +52 -4
- package/.claude/commands/hive-mind/hive-mind-stop.md +51 -4
- package/.claude/commands/hive-mind/hive-mind.md +74 -14
- package/.claude/commands/hooks/README.md +62 -7
- package/.claude/commands/hooks/overview.md +94 -35
- package/.claude/commands/hooks/post-edit.md +48 -87
- package/.claude/commands/hooks/post-task.md +37 -87
- package/.claude/commands/hooks/pre-edit.md +52 -84
- package/.claude/commands/hooks/pre-task.md +46 -81
- package/.claude/commands/hooks/session-end.md +49 -85
- package/.claude/commands/hooks/setup.md +87 -58
- package/.claude/commands/mastermind/_repeat.md +308 -0
- package/.claude/commands/mastermind/architect.md +49 -0
- package/.claude/commands/mastermind/brain.md +98 -0
- package/.claude/commands/mastermind/build.md +22 -0
- package/.claude/commands/mastermind/content.md +22 -0
- package/.claude/commands/mastermind/createorg.md +94 -0
- package/.claude/commands/mastermind/finance.md +22 -0
- package/.claude/commands/mastermind/idea.md +22 -0
- package/.claude/commands/mastermind/marketing.md +22 -0
- package/.claude/commands/mastermind/master.md +379 -0
- package/.claude/commands/mastermind/ops.md +22 -0
- package/.claude/commands/mastermind/release.md +22 -0
- package/.claude/commands/mastermind/research.md +22 -0
- package/.claude/commands/mastermind/review.md +22 -0
- package/.claude/commands/mastermind/runorg.md +106 -0
- package/.claude/commands/mastermind/sales.md +22 -0
- package/.claude/commands/mastermind/techport.md +17 -0
- package/.claude/commands/memory/README.md +75 -5
- package/.claude/commands/memory/memory-search.md +63 -11
- package/.claude/commands/monitoring/README.md +64 -4
- package/.claude/commands/monitoring/agent-metrics.md +50 -10
- package/.claude/commands/monitoring/agents.md +59 -32
- package/.claude/commands/monitoring/status.md +96 -34
- package/.claude/commands/monograph/README.md +102 -0
- package/.claude/commands/monograph/monograph-build.md +79 -0
- package/.claude/commands/monograph/monograph-search.md +96 -0
- package/.claude/commands/monograph/monograph-stats.md +53 -0
- package/.claude/commands/monograph/monograph-watch.md +63 -0
- package/.claude/commands/monograph/monograph-wiki.md +91 -0
- package/.claude/commands/monomind/createtask.md +277 -0
- package/.claude/commands/{monomind-do.md → monomind/do.md} +22 -9
- package/.claude/commands/monomind/help.md +118 -0
- package/.claude/commands/{monomind-idea.md → monomind/idea.md} +23 -29
- package/.claude/commands/{monomind-improve.md → monomind/improve.md} +24 -30
- package/.claude/commands/monomind/memory.md +230 -0
- package/.claude/commands/monomind/repeat.md +201 -0
- package/.claude/commands/monomind/review.md +313 -0
- package/.claude/commands/monomind/specialagents.md +125 -0
- package/.claude/commands/monomind/swarm.md +161 -0
- package/.claude/commands/monomind/understand.md +148 -0
- package/.claude/commands/optimization/README.md +69 -5
- package/.claude/commands/optimization/auto-topology.md +66 -43
- package/.claude/commands/optimization/parallel-execution.md +65 -39
- package/.claude/commands/optimization/performance-optimize.md +79 -0
- package/.claude/commands/pair/README.md +48 -230
- package/.claude/commands/pair/examples.md +85 -441
- package/.claude/commands/pair/modes.md +77 -303
- package/.claude/commands/pair/session.md +76 -359
- package/.claude/commands/sparc/analyzer.md +9 -26
- package/.claude/commands/sparc/architect.md +8 -25
- package/.claude/commands/sparc/ask.md +27 -68
- package/.claude/commands/sparc/batch-executor.md +8 -25
- package/.claude/commands/sparc/code.md +12 -53
- package/.claude/commands/sparc/coder.md +8 -25
- package/.claude/commands/sparc/debug.md +12 -53
- package/.claude/commands/sparc/debugger.md +8 -25
- package/.claude/commands/sparc/designer.md +8 -25
- package/.claude/commands/sparc/devops.md +16 -57
- package/.claude/commands/sparc/docs-writer.md +12 -53
- package/.claude/commands/sparc/documenter.md +8 -25
- package/.claude/commands/sparc/innovator.md +8 -25
- package/.claude/commands/sparc/integration.md +12 -53
- package/.claude/commands/sparc/mcp.md +12 -53
- package/.claude/commands/sparc/memory-manager.md +28 -25
- package/.claude/commands/sparc/optimizer.md +8 -25
- package/.claude/commands/sparc/orchestrator.md +35 -97
- package/.claude/commands/sparc/post-deployment-monitoring-mode.md +13 -54
- package/.claude/commands/sparc/refinement-optimization-mode.md +13 -54
- package/.claude/commands/sparc/researcher.md +8 -25
- package/.claude/commands/sparc/reviewer.md +8 -25
- package/.claude/commands/sparc/security-review.md +13 -54
- package/.claude/commands/sparc/sparc-modes.md +97 -151
- package/.claude/commands/sparc/sparc.md +16 -56
- package/.claude/commands/sparc/spec-pseudocode.md +13 -54
- package/.claude/commands/sparc/supabase-admin.md +19 -66
- package/.claude/commands/sparc/swarm-coordinator.md +21 -25
- package/.claude/commands/sparc/tdd.md +8 -25
- package/.claude/commands/sparc/tester.md +8 -25
- package/.claude/commands/sparc/tutorial.md +12 -53
- package/.claude/commands/sparc/workflow-manager.md +8 -25
- package/.claude/commands/sparc.md +76 -130
- package/.claude/commands/stream-chain/pipeline.md +72 -77
- package/.claude/commands/stream-chain/run.md +133 -47
- package/.claude/commands/swarm/README.md +37 -12
- package/.claude/commands/swarm/analysis.md +47 -69
- package/.claude/commands/swarm/development.md +45 -69
- package/.claude/commands/swarm/examples.md +77 -142
- package/.claude/commands/swarm/maintenance.md +47 -74
- package/.claude/commands/swarm/optimization.md +54 -87
- package/.claude/commands/swarm/research.md +47 -107
- package/.claude/commands/swarm/swarm-analysis.md +58 -4
- package/.claude/commands/swarm/swarm-background.md +61 -4
- package/.claude/commands/swarm/swarm-modes.md +63 -4
- package/.claude/commands/swarm/swarm-monitor.md +50 -4
- package/.claude/commands/swarm/swarm-status.md +40 -4
- package/.claude/commands/swarm/swarm-strategies.md +73 -5
- package/.claude/commands/swarm/swarm.md +70 -18
- package/.claude/commands/swarm/testing.md +51 -102
- package/.claude/commands/tokens.md +6 -1
- package/.claude/commands/training/README.md +36 -6
- package/.claude/commands/training/model-update.md +68 -15
- package/.claude/commands/training/neural-patterns.md +54 -55
- package/.claude/commands/training/neural-train.md +70 -16
- package/.claude/commands/training/pattern-learn.md +60 -16
- package/.claude/commands/training/specialization.md +78 -49
- package/.claude/commands/truth/start.md +87 -109
- package/.claude/commands/ts.md +7 -2
- package/.claude/commands/verify/check.md +90 -34
- package/.claude/commands/verify/start.md +71 -94
- package/.claude/commands/workflows/README.md +62 -6
- package/.claude/commands/workflows/development.md +69 -61
- package/.claude/commands/workflows/research.md +73 -47
- package/.claude/commands/workflows/workflow-create.md +75 -16
- package/.claude/commands/workflows/workflow-execute.md +94 -16
- package/.claude/commands/workflows/workflow-export.md +81 -16
- package/.claude/helpers/control-start.cjs +91 -0
- package/.claude/helpers/extras-registry.json +4104 -1991
- package/.claude/helpers/graphify-freshen.cjs +44 -13
- package/.claude/helpers/hook-handler.cjs +256 -1
- package/.claude/helpers/loop-tracker.cjs +107 -0
- package/.claude/helpers/router.cjs +48 -68
- package/.claude/helpers/skill-registry.json +89 -104
- package/.claude/helpers/statusline.cjs +33 -2
- package/.claude/scheduled_tasks.lock +1 -0
- package/.claude/settings.json +15 -0
- package/.claude/skills/.monomind/data/ranked-context.json +5 -0
- package/.claude/skills/.monomind/sessions/current.json +13 -0
- package/.claude/skills/.monomind/sessions/session-1777829336455.json +15 -0
- package/.claude/skills/.monomind/sessions/session-1777831614725.json +15 -0
- package/.claude/skills/.monomind/sessions/session-1777832095857.json +15 -0
- package/.claude/skills/.monomind/sessions/session-1777839814183.json +15 -0
- package/.claude/skills/.monomind/sessions/session-1777841847131.json +15 -0
- package/.claude/skills/.monomind/sessions/session-1777843309463.json +15 -0
- package/.claude/skills/.monomind/sessions/session-1777880867159.json +15 -0
- package/.claude/skills/.monomind/sessions/session-1777881884593.json +15 -0
- package/.claude/skills/.monomind/sessions/session-1777884090471.json +15 -0
- package/.claude/skills/.monomind/sessions/session-1777884808221.json +15 -0
- package/.claude/skills/.monomind/sessions/session-1777885672155.json +15 -0
- package/.claude/skills/.monomind/sessions/session-1777886852818.json +15 -0
- package/.claude/skills/.monomind/sessions/session-1777896532690.json +15 -0
- package/.claude/skills/agentdb-advanced/SKILL.md +11 -12
- package/.claude/skills/agentdb-learning/SKILL.md +20 -21
- package/.claude/skills/agentdb-memory-patterns/SKILL.md +28 -30
- package/.claude/skills/agentdb-optimization/SKILL.md +11 -12
- package/.claude/skills/agentdb-vector-search/SKILL.md +37 -41
- package/.claude/skills/{v3-integration-deep → agentic-integration}/SKILL.md +20 -13
- package/.claude/skills/agentic-jujutsu/SKILL.md +22 -22
- package/.claude/skills/{v3-cli-modernization → cli-modernization}/SKILL.md +17 -8
- package/.claude/skills/{v3-core-implementation → core-implementation}/SKILL.md +33 -8
- package/.claude/skills/{v3-ddd-architecture → ddd-architecture}/SKILL.md +18 -25
- package/.claude/skills/github-code-review/SKILL.md +82 -83
- package/.claude/skills/github-multi-repo/SKILL.md +42 -46
- package/.claude/skills/github-project-management/SKILL.md +83 -88
- package/.claude/skills/github-release-management/SKILL.md +12 -18
- package/.claude/skills/github-workflow-automation/SKILL.md +70 -74
- package/.claude/skills/hooks-automation/SKILL.md +9 -13
- package/.claude/skills/mastermind/_intake.md +83 -0
- package/.claude/skills/mastermind/_protocol.md +275 -0
- package/.claude/skills/mastermind/architect.md +847 -0
- package/.claude/skills/mastermind/build.md +158 -0
- package/.claude/skills/mastermind/content.md +185 -0
- package/.claude/skills/mastermind/createorg.md +318 -0
- package/.claude/skills/mastermind/finance.md +154 -0
- package/.claude/skills/mastermind/idea.md +158 -0
- package/.claude/skills/mastermind/marketing.md +216 -0
- package/.claude/skills/mastermind/monotask.md +350 -0
- package/.claude/skills/mastermind/ops.md +156 -0
- package/.claude/skills/mastermind/references/copywriting-frameworks.md +181 -0
- package/.claude/skills/mastermind/references/persuasion-psychology.md +158 -0
- package/.claude/skills/mastermind/release.md +156 -0
- package/.claude/skills/mastermind/research.md +156 -0
- package/.claude/skills/mastermind/review.md +157 -0
- package/.claude/skills/mastermind/runorg.md +308 -0
- package/.claude/skills/mastermind/sales.md +158 -0
- package/.claude/skills/mastermind/techport.md +743 -0
- package/.claude/skills/{v3-mcp-optimization → mcp-optimization}/SKILL.md +35 -14
- package/.claude/skills/{v3-memory-unification → memory-unification}/SKILL.md +20 -4
- package/.claude/skills/monodesign/SKILL.md +302 -0
- package/.claude/skills/monodesign/reference/adapt.md +190 -0
- package/.claude/skills/monodesign/reference/animate.md +175 -0
- package/.claude/skills/monodesign/reference/antipatterns-catalog.md +187 -0
- package/.claude/skills/monodesign/reference/audit.md +133 -0
- package/.claude/skills/monodesign/reference/bolder.md +113 -0
- package/.claude/skills/monodesign/reference/brand-workflow.md +180 -0
- package/.claude/skills/monodesign/reference/brand.md +114 -0
- package/.claude/skills/monodesign/reference/clarify.md +174 -0
- package/.claude/skills/monodesign/reference/cognitive-load.md +106 -0
- package/.claude/skills/monodesign/reference/color-and-contrast.md +105 -0
- package/.claude/skills/monodesign/reference/colorize.md +154 -0
- package/.claude/skills/monodesign/reference/component-specs.md +260 -0
- package/.claude/skills/monodesign/reference/component-states.md +274 -0
- package/.claude/skills/monodesign/reference/component-system.md +358 -0
- package/.claude/skills/monodesign/reference/copy-formulas.md +160 -0
- package/.claude/skills/monodesign/reference/craft.md +193 -0
- package/.claude/skills/monodesign/reference/critique.md +213 -0
- package/.claude/skills/monodesign/reference/delight.md +302 -0
- package/.claude/skills/monodesign/reference/design-principles.md +246 -0
- package/.claude/skills/monodesign/reference/distill.md +111 -0
- package/.claude/skills/monodesign/reference/document.md +427 -0
- package/.claude/skills/monodesign/reference/extract.md +69 -0
- package/.claude/skills/monodesign/reference/harden.md +347 -0
- package/.claude/skills/monodesign/reference/heuristics-scoring.md +234 -0
- package/.claude/skills/monodesign/reference/image-prompts.md +118 -0
- package/.claude/skills/monodesign/reference/interaction-design.md +195 -0
- package/.claude/skills/monodesign/reference/layout.md +141 -0
- package/.claude/skills/monodesign/reference/live.md +622 -0
- package/.claude/skills/monodesign/reference/motion-design.md +109 -0
- package/.claude/skills/monodesign/reference/onboard.md +234 -0
- package/.claude/skills/monodesign/reference/optimize.md +258 -0
- package/.claude/skills/monodesign/reference/overdrive.md +130 -0
- package/.claude/skills/monodesign/reference/personas.md +179 -0
- package/.claude/skills/monodesign/reference/polish.md +233 -0
- package/.claude/skills/monodesign/reference/pre-delivery-checklist.md +108 -0
- package/.claude/skills/monodesign/reference/product.md +62 -0
- package/.claude/skills/monodesign/reference/quieter.md +99 -0
- package/.claude/skills/monodesign/reference/responsive-design.md +114 -0
- package/.claude/skills/monodesign/reference/shape.md +151 -0
- package/.claude/skills/monodesign/reference/spatial-design.md +100 -0
- package/.claude/skills/monodesign/reference/teach.md +156 -0
- package/.claude/skills/monodesign/reference/token-architecture.md +222 -0
- package/.claude/skills/monodesign/reference/typeset.md +124 -0
- package/.claude/skills/monodesign/reference/typography.md +159 -0
- package/.claude/skills/monodesign/reference/ux-research.md +143 -0
- package/.claude/skills/monodesign/reference/ux-rules.md +211 -0
- package/.claude/skills/monodesign/reference/ux-writing.md +107 -0
- package/.claude/skills/monomotion/SKILL.md +145 -0
- package/.claude/skills/monomotion/rules/api-control.md +139 -0
- package/.claude/skills/monomotion/rules/effects.md +109 -0
- package/.claude/skills/monomotion/rules/integration.md +140 -0
- package/.claude/skills/monomotion/rules/scroll.md +131 -0
- package/.claude/skills/monomotion/rules/sequencing.md +105 -0
- package/.claude/skills/monomotion/rules/svg.md +101 -0
- package/.claude/skills/monomotion/rules/text.md +119 -0
- package/.claude/skills/pair-programming/SKILL.md +1 -1
- package/.claude/skills/performance-analysis/SKILL.md +3 -3
- package/.claude/skills/{v3-performance-optimization → performance-optimization}/SKILL.md +16 -8
- package/.claude/skills/reasoningbank-agentdb/SKILL.md +17 -19
- package/.claude/skills/reasoningbank-intelligence/SKILL.md +4 -6
- package/.claude/skills/{v3-security-overhaul → security-hardening}/SKILL.md +13 -3
- package/.claude/skills/skill-builder/SKILL.md +19 -19
- package/.claude/skills/sparc-methodology/SKILL.md +55 -211
- package/.claude/skills/stop-slop/SKILL.md +67 -0
- package/.claude/skills/stop-slop/references/examples.md +61 -0
- package/.claude/skills/stop-slop/references/phrases.md +130 -0
- package/.claude/skills/stop-slop/references/structures.md +136 -0
- package/.claude/skills/swarm-advanced/SKILL.md +13 -43
- package/.claude/skills/{v3-swarm-coordination → swarm-coordination}/SKILL.md +39 -21
- package/.claude/skills/swarm-orchestration/SKILL.md +12 -12
- package/.claude/skills/verification-quality/SKILL.md +5 -5
- package/README.md +5 -5
- package/package.json +1 -1
- package/packages/@monomind/cli/README.md +5 -5
- package/packages/@monomind/cli/bin/cli.js +78 -13
- package/packages/@monomind/cli/dist/src/agents/halt-signal.js +33 -7
- package/packages/@monomind/cli/dist/src/agents/managed-agent.js +5 -2
- package/packages/@monomind/cli/dist/src/agents/prompt-experiment.d.ts +3 -2
- package/packages/@monomind/cli/dist/src/agents/prompt-experiment.js +1 -1
- package/packages/@monomind/cli/dist/src/agents/prompt-version-manager.d.ts +5 -2
- package/packages/@monomind/cli/dist/src/agents/prompt-version-manager.js +26 -4
- package/packages/@monomind/cli/dist/src/agents/specialization-scorer.js +17 -9
- package/packages/@monomind/cli/dist/src/agents/trigger-scanner.d.ts +5 -3
- package/packages/@monomind/cli/dist/src/agents/trigger-scanner.js +58 -10
- package/packages/@monomind/cli/dist/src/agents/version-store.d.ts +0 -1
- package/packages/@monomind/cli/dist/src/agents/version-store.js +44 -21
- package/packages/@monomind/cli/dist/src/autopilot-state.js +79 -28
- package/packages/@monomind/cli/dist/src/benchmarks/benchmark-runner.d.ts +7 -2
- package/packages/@monomind/cli/dist/src/benchmarks/benchmark-runner.js +20 -8
- package/packages/@monomind/cli/dist/src/benchmarks/metric-evaluators.d.ts +2 -1
- package/packages/@monomind/cli/dist/src/benchmarks/metric-evaluators.js +25 -2
- package/packages/@monomind/cli/dist/src/commands/agent.js +6 -4
- package/packages/@monomind/cli/dist/src/commands/appliance-advanced.js +23 -0
- package/packages/@monomind/cli/dist/src/commands/autopilot.js +3 -3
- package/packages/@monomind/cli/dist/src/commands/benchmark.js +119 -8
- package/packages/@monomind/cli/dist/src/commands/claims.js +22 -14
- package/packages/@monomind/cli/dist/src/commands/config.js +32 -0
- package/packages/@monomind/cli/dist/src/commands/daemon.js +13 -11
- package/packages/@monomind/cli/dist/src/commands/deployment.js +21 -2
- package/packages/@monomind/cli/dist/src/commands/doctor.js +28 -62
- package/packages/@monomind/cli/dist/src/commands/embeddings.js +124 -48
- package/packages/@monomind/cli/dist/src/commands/hive-mind.js +15 -14
- package/packages/@monomind/cli/dist/src/commands/hooks.js +45 -41
- package/packages/@monomind/cli/dist/src/commands/index.d.ts +2 -0
- package/packages/@monomind/cli/dist/src/commands/index.js +20 -7
- package/packages/@monomind/cli/dist/src/commands/init.js +53 -19
- package/packages/@monomind/cli/dist/src/commands/mcp.js +31 -44
- package/packages/@monomind/cli/dist/src/commands/memory.js +47 -15
- package/packages/@monomind/cli/dist/src/commands/migrate.js +156 -108
- package/packages/@monomind/cli/dist/src/commands/monograph.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/commands/monograph.js +526 -0
- package/packages/@monomind/cli/dist/src/commands/neural.js +96 -56
- package/packages/@monomind/cli/dist/src/commands/performance.js +30 -8
- package/packages/@monomind/cli/dist/src/commands/plugins.js +13 -37
- package/packages/@monomind/cli/dist/src/commands/process.js +25 -2
- package/packages/@monomind/cli/dist/src/commands/providers.js +37 -5
- package/packages/@monomind/cli/dist/src/commands/replay.js +4 -4
- package/packages/@monomind/cli/dist/src/commands/route.js +37 -5
- package/packages/@monomind/cli/dist/src/commands/ruvector/import.js +12 -2
- package/packages/@monomind/cli/dist/src/commands/ruvector/init.js +15 -0
- package/packages/@monomind/cli/dist/src/commands/ruvector/status.js +16 -3
- package/packages/@monomind/cli/dist/src/commands/security.js +342 -193
- package/packages/@monomind/cli/dist/src/commands/session.js +51 -8
- package/packages/@monomind/cli/dist/src/commands/start.js +18 -4
- package/packages/@monomind/cli/dist/src/commands/swarm.js +47 -36
- package/packages/@monomind/cli/dist/src/commands/tokens.js +11 -11
- package/packages/@monomind/cli/dist/src/commands/transfer-store.js +1 -1
- package/packages/@monomind/cli/dist/src/commands/workflow.js +31 -4
- package/packages/@monomind/cli/dist/src/config-adapter.d.ts +2 -1
- package/packages/@monomind/cli/dist/src/consensus/audit-writer.js +46 -13
- package/packages/@monomind/cli/dist/src/consensus/vote-signer.d.ts +0 -3
- package/packages/@monomind/cli/dist/src/consensus/vote-signer.js +9 -1
- package/packages/@monomind/cli/dist/src/dlq/dlq-reader.d.ts +4 -2
- package/packages/@monomind/cli/dist/src/dlq/dlq-reader.js +25 -8
- package/packages/@monomind/cli/dist/src/dlq/dlq-replayer.d.ts +10 -3
- package/packages/@monomind/cli/dist/src/dlq/dlq-replayer.js +50 -16
- package/packages/@monomind/cli/dist/src/dlq/dlq-writer.js +27 -5
- package/packages/@monomind/cli/dist/src/eval/dataset-manager.d.ts +2 -2
- package/packages/@monomind/cli/dist/src/eval/dataset-manager.js +26 -16
- package/packages/@monomind/cli/dist/src/eval/trace-collector.js +23 -3
- package/packages/@monomind/cli/dist/src/index.js +12 -10
- package/packages/@monomind/cli/dist/src/init/claudemd-generator.js +8 -8
- package/packages/@monomind/cli/dist/src/init/executor.js +163 -137
- package/packages/@monomind/cli/dist/src/init/helpers-generator.js +49 -36
- package/packages/@monomind/cli/dist/src/init/mcp-generator.js +3 -3
- package/packages/@monomind/cli/dist/src/init/settings-generator.js +10 -3
- package/packages/@monomind/cli/dist/src/init/shared-instructions-generator.js +18 -3
- package/packages/@monomind/cli/dist/src/init/statusline-generator.js +3 -1
- package/packages/@monomind/cli/dist/src/init/types.d.ts +35 -11
- package/packages/@monomind/cli/dist/src/init/types.js +5 -9
- package/packages/@monomind/cli/dist/src/interactive/interrupt.js +8 -3
- package/packages/@monomind/cli/dist/src/mcp/tool-registry.js +38 -4
- package/packages/@monomind/cli/dist/src/mcp-client.js +15 -6
- package/packages/@monomind/cli/dist/src/mcp-server.d.ts +9 -2
- package/packages/@monomind/cli/dist/src/mcp-server.js +182 -35
- package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.js +66 -34
- package/packages/@monomind/cli/dist/src/mcp-tools/agentdb-tools.js +34 -7
- package/packages/@monomind/cli/dist/src/mcp-tools/analyze-tools.js +25 -16
- package/packages/@monomind/cli/dist/src/mcp-tools/auto-install.js +4 -6
- package/packages/@monomind/cli/dist/src/mcp-tools/autopilot-tools.js +12 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/browser-tools.js +199 -20
- package/packages/@monomind/cli/dist/src/mcp-tools/claims-tools.js +68 -18
- package/packages/@monomind/cli/dist/src/mcp-tools/config-tools.js +33 -5
- package/packages/@monomind/cli/dist/src/mcp-tools/coordination-tools.js +59 -4
- package/packages/@monomind/cli/dist/src/mcp-tools/daa-tools.js +46 -10
- package/packages/@monomind/cli/dist/src/mcp-tools/embeddings-tools.js +46 -5
- package/packages/@monomind/cli/dist/src/mcp-tools/github-tools.js +29 -16
- package/packages/@monomind/cli/dist/src/mcp-tools/graphify-tools.d.ts +4 -67
- package/packages/@monomind/cli/dist/src/mcp-tools/graphify-tools.js +40 -1250
- package/packages/@monomind/cli/dist/src/mcp-tools/guidance-tools.js +38 -10
- package/packages/@monomind/cli/dist/src/mcp-tools/hive-mind-tools.js +96 -33
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-tools.js +70 -37
- package/packages/@monomind/cli/dist/src/mcp-tools/index.d.ts +1 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/index.js +1 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/memory-tools.js +29 -13
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +6306 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/neural-tools.js +121 -37
- package/packages/@monomind/cli/dist/src/mcp-tools/performance-tools.js +21 -8
- package/packages/@monomind/cli/dist/src/mcp-tools/progress-tools.js +10 -8
- package/packages/@monomind/cli/dist/src/mcp-tools/request-tracker.js +4 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/ruvllm-tools.js +19 -8
- package/packages/@monomind/cli/dist/src/mcp-tools/session-tools.js +57 -17
- package/packages/@monomind/cli/dist/src/mcp-tools/swarm-tools.js +35 -17
- package/packages/@monomind/cli/dist/src/mcp-tools/system-tools.js +4 -3
- package/packages/@monomind/cli/dist/src/mcp-tools/task-tools.js +53 -13
- package/packages/@monomind/cli/dist/src/mcp-tools/terminal-tools.js +63 -14
- package/packages/@monomind/cli/dist/src/mcp-tools/transfer-tools.js +21 -16
- package/packages/@monomind/cli/dist/src/mcp-tools/workflow-tools.js +92 -23
- package/packages/@monomind/cli/dist/src/memory/ewc-consolidation.js +41 -10
- package/packages/@monomind/cli/dist/src/memory/intelligence.d.ts +2 -2
- package/packages/@monomind/cli/dist/src/memory/intelligence.js +39 -13
- package/packages/@monomind/cli/dist/src/memory/memory-bridge.d.ts +1 -0
- package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +149 -56
- package/packages/@monomind/cli/dist/src/memory/memory-initializer.js +107 -45
- package/packages/@monomind/cli/dist/src/memory/sona-optimizer.d.ts +8 -1
- package/packages/@monomind/cli/dist/src/memory/sona-optimizer.js +25 -8
- package/packages/@monomind/cli/dist/src/observability/replay-reader.d.ts +40 -0
- package/packages/@monomind/cli/dist/src/observability/replay-reader.js +138 -0
- package/packages/@monomind/cli/dist/src/orchestration/routing-modes.js +35 -5
- package/packages/@monomind/cli/dist/src/parser.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/parser.js +48 -14
- package/packages/@monomind/cli/dist/src/plugins/manager.js +112 -19
- package/packages/@monomind/cli/dist/src/plugins/store/discovery.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/plugins/store/discovery.js +80 -62
- package/packages/@monomind/cli/dist/src/production/circuit-breaker.js +8 -1
- package/packages/@monomind/cli/dist/src/production/error-handler.d.ts +4 -2
- package/packages/@monomind/cli/dist/src/production/error-handler.js +27 -5
- package/packages/@monomind/cli/dist/src/production/monitoring.js +8 -4
- package/packages/@monomind/cli/dist/src/production/rate-limiter.js +30 -22
- package/packages/@monomind/cli/dist/src/ruvector/agent-wasm.js +2 -2
- package/packages/@monomind/cli/dist/src/ruvector/coverage-router.js +19 -9
- package/packages/@monomind/cli/dist/src/ruvector/diff-classifier.d.ts +1 -0
- package/packages/@monomind/cli/dist/src/ruvector/diff-classifier.js +26 -6
- package/packages/@monomind/cli/dist/src/ruvector/enhanced-model-router.js +24 -2
- package/packages/@monomind/cli/dist/src/ruvector/index.d.ts +1 -2
- package/packages/@monomind/cli/dist/src/ruvector/index.js +2 -2
- package/packages/@monomind/cli/dist/src/ruvector/model-router.d.ts +4 -2
- package/packages/@monomind/cli/dist/src/ruvector/model-router.js +30 -6
- package/packages/@monomind/cli/dist/src/ruvector/moe-router.d.ts +7 -0
- package/packages/@monomind/cli/dist/src/ruvector/moe-router.js +35 -12
- package/packages/@monomind/cli/dist/src/ruvector/q-learning-router.d.ts +7 -1
- package/packages/@monomind/cli/dist/src/ruvector/q-learning-router.js +40 -9
- package/packages/@monomind/cli/dist/src/services/claim-service.d.ts +3 -1
- package/packages/@monomind/cli/dist/src/services/claim-service.js +33 -2
- package/packages/@monomind/cli/dist/src/services/config-file-manager.d.ts +16 -2
- package/packages/@monomind/cli/dist/src/services/config-file-manager.js +105 -17
- package/packages/@monomind/cli/dist/src/services/container-worker-pool.js +51 -11
- package/packages/@monomind/cli/dist/src/services/headless-worker-executor.d.ts +7 -0
- package/packages/@monomind/cli/dist/src/services/headless-worker-executor.js +188 -45
- package/packages/@monomind/cli/dist/src/services/registry-api.js +62 -9
- package/packages/@monomind/cli/dist/src/services/ruvector-training.js +8 -0
- package/packages/@monomind/cli/dist/src/services/worker-daemon.d.ts +4 -1
- package/packages/@monomind/cli/dist/src/services/worker-daemon.js +112 -28
- package/packages/@monomind/cli/dist/src/services/worker-queue.d.ts +9 -2
- package/packages/@monomind/cli/dist/src/services/worker-queue.js +86 -5
- package/packages/@monomind/cli/dist/src/suggest.js +9 -0
- package/packages/@monomind/cli/dist/src/swarm/flow-enforcer.d.ts +5 -3
- package/packages/@monomind/cli/dist/src/swarm/flow-enforcer.js +17 -5
- package/packages/@monomind/cli/dist/src/swarm/flow-visualizer.d.ts +3 -0
- package/packages/@monomind/cli/dist/src/swarm/flow-visualizer.js +30 -6
- package/packages/@monomind/cli/dist/src/transfer/anonymization/index.js +5 -3
- package/packages/@monomind/cli/dist/src/transfer/export.js +5 -3
- package/packages/@monomind/cli/dist/src/transfer/ipfs/client.js +84 -7
- package/packages/@monomind/cli/dist/src/transfer/ipfs/upload.js +13 -4
- package/packages/@monomind/cli/dist/src/transfer/storage/gcs.js +19 -10
- package/packages/@monomind/cli/dist/src/transfer/store/discovery.d.ts +9 -2
- package/packages/@monomind/cli/dist/src/transfer/store/discovery.js +68 -13
- package/packages/@monomind/cli/dist/src/transfer/store/download.d.ts +15 -6
- package/packages/@monomind/cli/dist/src/transfer/store/download.js +113 -24
- package/packages/@monomind/cli/dist/src/transfer/store/publish.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/transfer/store/publish.js +13 -14
- package/packages/@monomind/cli/dist/src/transfer/store/registry.d.ts +3 -3
- package/packages/@monomind/cli/dist/src/transfer/store/registry.js +32 -16
- package/packages/@monomind/cli/dist/src/update/checker.js +17 -4
- package/packages/@monomind/cli/dist/src/update/executor.js +25 -20
- package/packages/@monomind/cli/dist/src/update/rate-limiter.d.ts +11 -0
- package/packages/@monomind/cli/dist/src/update/rate-limiter.js +23 -3
- package/packages/@monomind/cli/dist/src/utils/parse-jsonl.d.ts +6 -0
- package/packages/@monomind/cli/dist/src/utils/parse-jsonl.js +22 -0
- package/packages/@monomind/cli/dist/src/workflow/condition-evaluator.js +37 -3
- package/packages/@monomind/cli/dist/src/workflow/dag-builder.js +27 -11
- package/packages/@monomind/cli/dist/src/workflow/dag-executor.js +51 -13
- package/packages/@monomind/cli/dist/src/workflow/dsl-schema.d.ts +3 -0
- package/packages/@monomind/cli/dist/src/workflow/dsl-schema.js +6 -2
- package/packages/@monomind/cli/dist/src/workflow/template-engine.js +7 -0
- package/packages/@monomind/cli/dist/src/workflow/workflow-executor.js +95 -14
- package/packages/@monomind/cli/package.json +5 -2
- package/packages/@monomind/guidance/README.md +0 -3
- package/packages/@monomind/shared/README.md +0 -1
- package/packages/@monomind/shared/dist/types/consensus-audit.d.ts +3 -1
- package/packages/README.md +15 -16
- package/.claude/agents/design/design-brand-guardian.md +0 -323
- package/.claude/agents/design/design-image-prompt-engineer.md +0 -237
- package/.claude/agents/design/design-inclusive-visuals-specialist.md +0 -72
- package/.claude/agents/design/design-ui-designer.md +0 -384
- package/.claude/agents/design/design-ux-architect.md +0 -470
- package/.claude/agents/design/design-ux-researcher.md +0 -330
- package/.claude/agents/design/design-visual-storyteller.md +0 -150
- package/.claude/agents/design/design-whimsy-injector.md +0 -439
- package/.claude/agents/v3/integration-architect.md +0 -338
- package/.claude/commands/analysis/COMMAND_COMPLIANCE_REPORT.md +0 -54
- package/.claude/commands/coordination/init.md +0 -44
- package/.claude/commands/coordination/orchestrate.md +0 -43
- package/.claude/commands/coordination/spawn.md +0 -45
- package/.claude/commands/github/code-review-swarm.md +0 -550
- package/.claude/commands/github/code-review.md +0 -25
- package/.claude/commands/github/github-swarm.md +0 -121
- package/.claude/commands/github/issue-triage.md +0 -25
- package/.claude/commands/github/multi-repo-swarm.md +0 -519
- package/.claude/commands/github/pr-enhance.md +0 -26
- package/.claude/commands/github/project-board-sync.md +0 -471
- package/.claude/commands/github/release-swarm.md +0 -590
- package/.claude/commands/github/repo-analyze.md +0 -25
- package/.claude/commands/github/swarm-issue.md +0 -482
- package/.claude/commands/github/swarm-pr.md +0 -310
- package/.claude/commands/github/workflow-automation.md +0 -468
- package/.claude/commands/hive-mind/hive-mind-metrics.md +0 -8
- package/.claude/commands/hive-mind/hive-mind-resume.md +0 -8
- package/.claude/commands/hive-mind/hive-mind-sessions.md +0 -8
- package/.claude/commands/hive-mind/hive-mind-wizard.md +0 -8
- package/.claude/commands/list-agents.md +0 -17
- package/.claude/commands/memory/memory-persist.md +0 -25
- package/.claude/commands/memory/memory-usage.md +0 -25
- package/.claude/commands/memory/neural.md +0 -47
- package/.claude/commands/metrics.md +0 -11
- package/.claude/commands/monitoring/real-time-view.md +0 -25
- package/.claude/commands/monitoring/swarm-monitor.md +0 -25
- package/.claude/commands/monomind-createtask.md +0 -302
- package/.claude/commands/monomind-help.md +0 -103
- package/.claude/commands/monomind-memory.md +0 -107
- package/.claude/commands/monomind-repeat.md +0 -149
- package/.claude/commands/monomind-swarm.md +0 -205
- package/.claude/commands/optimization/cache-manage.md +0 -25
- package/.claude/commands/optimization/topology-optimize.md +0 -25
- package/.claude/commands/pair/commands.md +0 -546
- package/.claude/commands/pair/config.md +0 -510
- package/.claude/commands/pair/start.md +0 -209
- package/.claude/commands/use-agent.md +0 -67
- package/.claude/skills/monomind-createtask/SKILL.md +0 -269
- package/.claude/skills/monomind-task-engine/SKILL.md +0 -358
- /package/.claude/agents/{v3 → specialists}/memory-specialist.md +0 -0
- /package/.claude/agents/{v3 → specialists}/performance-engineer.md +0 -0
- /package/.claude/agents/{v3 → specialists}/queen-coordinator.md +0 -0
- /package/.claude/agents/{v3 → specialists}/security-architect.md +0 -0
|
@@ -16,11 +16,19 @@
|
|
|
16
16
|
* - Network isolation per worker type
|
|
17
17
|
*/
|
|
18
18
|
import { EventEmitter } from 'events';
|
|
19
|
-
import { spawn, exec } from 'child_process';
|
|
19
|
+
import { spawn, exec, execFile } from 'child_process';
|
|
20
20
|
import { promisify } from 'util';
|
|
21
21
|
import { existsSync, mkdirSync } from 'fs';
|
|
22
22
|
import { join } from 'path';
|
|
23
23
|
const execAsync = promisify(exec);
|
|
24
|
+
const execFileAsync = promisify(execFile);
|
|
25
|
+
/** Allowlist: registry/name:tag with optional digest — no shell metacharacters */
|
|
26
|
+
const DOCKER_IMAGE_RE = /^[a-zA-Z0-9][a-zA-Z0-9.\-_/:@]*$/;
|
|
27
|
+
function validateDockerImage(image) {
|
|
28
|
+
if (!DOCKER_IMAGE_RE.test(image)) {
|
|
29
|
+
throw new Error(`Invalid Docker image name: "${image}"`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
24
32
|
// ============================================
|
|
25
33
|
// Constants
|
|
26
34
|
// ============================================
|
|
@@ -134,6 +142,10 @@ export class ContainerWorkerPool extends EventEmitter {
|
|
|
134
142
|
}
|
|
135
143
|
}
|
|
136
144
|
// Queue the task
|
|
145
|
+
const MAX_TASK_QUEUE = 500;
|
|
146
|
+
if (this.taskQueue.length >= MAX_TASK_QUEUE) {
|
|
147
|
+
return this.createErrorResult(options.workerType, 'Task queue is full');
|
|
148
|
+
}
|
|
137
149
|
return new Promise((resolve, reject) => {
|
|
138
150
|
this.taskQueue.push({
|
|
139
151
|
options,
|
|
@@ -236,13 +248,14 @@ export class ContainerWorkerPool extends EventEmitter {
|
|
|
236
248
|
*/
|
|
237
249
|
async ensureImage() {
|
|
238
250
|
try {
|
|
239
|
-
|
|
251
|
+
validateDockerImage(this.config.image);
|
|
252
|
+
await execFileAsync('docker', ['image', 'inspect', this.config.image], { timeout: 10000 });
|
|
240
253
|
}
|
|
241
254
|
catch {
|
|
242
255
|
// Image not found, try to pull
|
|
243
256
|
this.emit('imagePull', { image: this.config.image });
|
|
244
257
|
try {
|
|
245
|
-
await
|
|
258
|
+
await execFileAsync('docker', ['pull', this.config.image], { timeout: 300000 });
|
|
246
259
|
}
|
|
247
260
|
catch (error) {
|
|
248
261
|
this.emit('warning', { message: `Failed to pull image: ${error}` });
|
|
@@ -267,23 +280,48 @@ export class ContainerWorkerPool extends EventEmitter {
|
|
|
267
280
|
this.containers.set(id, containerInfo);
|
|
268
281
|
this.emit('containerCreating', { id, name });
|
|
269
282
|
try {
|
|
270
|
-
// Build docker run command
|
|
283
|
+
// Build docker run command with hardening flags.
|
|
284
|
+
// Without these flags the container ran with default capabilities and
|
|
285
|
+
// privilege escalation enabled — combined with a writable host mount
|
|
286
|
+
// that is JSON-parsed by the daemon, this was a container-to-host RCE
|
|
287
|
+
// chain (claims.json/daemon-state.json could be rewritten from inside).
|
|
288
|
+
const stateMount = join(this.projectRoot, this.config.statePath);
|
|
271
289
|
const args = [
|
|
272
290
|
'run', '-d',
|
|
273
291
|
'--name', name,
|
|
292
|
+
// Resource limits
|
|
274
293
|
'--cpus', this.config.resources.cpus,
|
|
275
294
|
'--memory', this.config.resources.memory,
|
|
295
|
+
'--pids-limit', '256',
|
|
296
|
+
// Privilege & capability hardening
|
|
297
|
+
'--security-opt', 'no-new-privileges:true',
|
|
298
|
+
'--cap-drop', 'ALL',
|
|
299
|
+
'--user', '1000:1000',
|
|
300
|
+
'--ipc', 'none',
|
|
301
|
+
// Mounts: workspace read-only, state read-only by default with a
|
|
302
|
+
// narrow rw output channel for the worker to write back results.
|
|
276
303
|
'-v', `${this.projectRoot}:${this.config.workspacePath}:ro`,
|
|
277
|
-
'-v', `${
|
|
304
|
+
'-v', `${stateMount}:/root/.monomind:ro`,
|
|
305
|
+
'-v', `${join(stateMount, 'output')}:/output:rw`,
|
|
306
|
+
// tmpfs for /tmp without exec
|
|
307
|
+
'--tmpfs', '/tmp:rw,noexec,nosuid,size=64m',
|
|
278
308
|
'-w', this.config.workspacePath,
|
|
279
309
|
];
|
|
280
|
-
// Add environment variables
|
|
310
|
+
// Add environment variables.
|
|
311
|
+
// SECURITY: ANTHROPIC_API_KEY is intentionally NOT injected via -e here.
|
|
312
|
+
// Visible via `docker inspect` and process listings on the host, and
|
|
313
|
+
// exfiltrable by indirect prompt injection via process.env. Containers
|
|
314
|
+
// that need API access must use a host-side proxy. Set
|
|
315
|
+
// MONOMIND_CONTAINER_PASS_ANTHROPIC_KEY=1 to opt back in for trusted
|
|
316
|
+
// single-tenant deployments.
|
|
281
317
|
const env = {
|
|
282
318
|
...this.config.env,
|
|
283
|
-
ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY || '',
|
|
284
319
|
CLAUDE_CODE_HEADLESS: 'true',
|
|
285
320
|
CLAUDE_CODE_SANDBOX_MODE: this.config.defaultSandbox,
|
|
286
321
|
};
|
|
322
|
+
if (process.env.MONOMIND_CONTAINER_PASS_ANTHROPIC_KEY === '1' && process.env.ANTHROPIC_API_KEY) {
|
|
323
|
+
env.ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
|
|
324
|
+
}
|
|
287
325
|
for (const [key, value] of Object.entries(env)) {
|
|
288
326
|
if (value) {
|
|
289
327
|
args.push('-e', `${key}=${value}`);
|
|
@@ -296,7 +334,7 @@ export class ContainerWorkerPool extends EventEmitter {
|
|
|
296
334
|
// Add image and entrypoint to keep container running
|
|
297
335
|
args.push(this.config.image, 'tail', '-f', '/dev/null');
|
|
298
336
|
// Create the container (async)
|
|
299
|
-
const { stdout } = await
|
|
337
|
+
const { stdout } = await execFileAsync('docker', args, { timeout: 60000 });
|
|
300
338
|
const containerId = stdout.trim();
|
|
301
339
|
containerInfo.state = 'ready';
|
|
302
340
|
this.emit('containerCreated', { id, name, containerId });
|
|
@@ -317,7 +355,7 @@ export class ContainerWorkerPool extends EventEmitter {
|
|
|
317
355
|
return;
|
|
318
356
|
container.state = 'terminated';
|
|
319
357
|
try {
|
|
320
|
-
await
|
|
358
|
+
await execFileAsync('docker', ['rm', '-f', container.name], { timeout: 30000 });
|
|
321
359
|
}
|
|
322
360
|
catch {
|
|
323
361
|
// Ignore removal errors
|
|
@@ -410,11 +448,13 @@ export class ContainerWorkerPool extends EventEmitter {
|
|
|
410
448
|
let stdout = '';
|
|
411
449
|
let stderr = '';
|
|
412
450
|
let timedOut = false;
|
|
451
|
+
let exited = false;
|
|
452
|
+
child.once('exit', () => { exited = true; });
|
|
413
453
|
const timeout = setTimeout(() => {
|
|
414
454
|
timedOut = true;
|
|
415
455
|
child.kill('SIGTERM');
|
|
416
456
|
setTimeout(() => {
|
|
417
|
-
if (!
|
|
457
|
+
if (!exited) {
|
|
418
458
|
child.kill('SIGKILL');
|
|
419
459
|
}
|
|
420
460
|
}, 5000);
|
|
@@ -492,7 +532,7 @@ export class ContainerWorkerPool extends EventEmitter {
|
|
|
492
532
|
continue;
|
|
493
533
|
try {
|
|
494
534
|
// Check if container is running (async)
|
|
495
|
-
const { stdout } = await
|
|
535
|
+
const { stdout } = await execFileAsync('docker', ['inspect', '-f', '{{.State.Running}}', container.name], { timeout: 10000 });
|
|
496
536
|
const output = stdout.trim();
|
|
497
537
|
if (output !== 'true') {
|
|
498
538
|
container.healthCheckFailures++;
|
|
@@ -205,7 +205,9 @@ export declare class HeadlessWorkerExecutor extends EventEmitter {
|
|
|
205
205
|
private pendingQueue;
|
|
206
206
|
private contextCache;
|
|
207
207
|
private claudeCodeAvailable;
|
|
208
|
+
private claudeCodeAvailableCheckedAt;
|
|
208
209
|
private claudeCodeVersion;
|
|
210
|
+
private activeReservations;
|
|
209
211
|
constructor(projectRoot: string, options?: HeadlessExecutorConfig);
|
|
210
212
|
/**
|
|
211
213
|
* Check if Claude Code CLI is available
|
|
@@ -231,6 +233,11 @@ export declare class HeadlessWorkerExecutor extends EventEmitter {
|
|
|
231
233
|
* Cancel a running execution
|
|
232
234
|
*/
|
|
233
235
|
cancel(executionId: string): boolean;
|
|
236
|
+
/**
|
|
237
|
+
* Cancel all running executions for a specific worker type.
|
|
238
|
+
* Used by the timeout handler in worker-daemon to avoid killing unrelated workers.
|
|
239
|
+
*/
|
|
240
|
+
cancelByType(workerType: string): boolean;
|
|
234
241
|
/**
|
|
235
242
|
* Cancel all running executions
|
|
236
243
|
*/
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import { spawn, execSync } from 'child_process';
|
|
22
22
|
import { EventEmitter } from 'events';
|
|
23
|
-
import { existsSync, readFileSync, readdirSync, mkdirSync, writeFileSync } from 'fs';
|
|
23
|
+
import { existsSync, readFileSync, readdirSync, mkdirSync, writeFileSync, renameSync } from 'fs';
|
|
24
24
|
import { join } from 'path';
|
|
25
25
|
// ============================================
|
|
26
26
|
// Constants
|
|
@@ -91,7 +91,7 @@ Provide a JSON report with:
|
|
|
91
91
|
sandbox: 'strict',
|
|
92
92
|
model: 'haiku',
|
|
93
93
|
outputFormat: 'json',
|
|
94
|
-
contextPatterns: ['**/*.ts', '**/*.js', '
|
|
94
|
+
contextPatterns: ['**/*.ts', '**/*.js', '**/package.json'],
|
|
95
95
|
timeoutMs: 5 * 60 * 1000,
|
|
96
96
|
},
|
|
97
97
|
},
|
|
@@ -366,7 +366,13 @@ export class HeadlessWorkerExecutor extends EventEmitter {
|
|
|
366
366
|
pendingQueue = [];
|
|
367
367
|
contextCache = new Map();
|
|
368
368
|
claudeCodeAvailable = null;
|
|
369
|
+
claudeCodeAvailableCheckedAt = null;
|
|
369
370
|
claudeCodeVersion = null;
|
|
371
|
+
// SECURITY: synchronous reservation counter so processQueue() does not
|
|
372
|
+
// drain the entire pendingQueue before async pool insertions catch up.
|
|
373
|
+
// Without this, a single processQueue() iteration could spawn hundreds of
|
|
374
|
+
// claude child processes simultaneously (DoS amplifier: 1 dequeue → 500 spawns).
|
|
375
|
+
activeReservations = 0;
|
|
370
376
|
constructor(projectRoot, options) {
|
|
371
377
|
super();
|
|
372
378
|
this.projectRoot = projectRoot;
|
|
@@ -390,8 +396,14 @@ export class HeadlessWorkerExecutor extends EventEmitter {
|
|
|
390
396
|
* Check if Claude Code CLI is available
|
|
391
397
|
*/
|
|
392
398
|
async isAvailable() {
|
|
393
|
-
|
|
394
|
-
|
|
399
|
+
const NEGATIVE_CACHE_TTL_MS = 60_000; // re-check after 1 min on negative result
|
|
400
|
+
if (this.claudeCodeAvailable === true) {
|
|
401
|
+
return true;
|
|
402
|
+
}
|
|
403
|
+
if (this.claudeCodeAvailable === false && this.claudeCodeAvailableCheckedAt !== null) {
|
|
404
|
+
if (Date.now() - this.claudeCodeAvailableCheckedAt < NEGATIVE_CACHE_TTL_MS) {
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
395
407
|
}
|
|
396
408
|
try {
|
|
397
409
|
const output = execSync('claude --version', {
|
|
@@ -401,12 +413,14 @@ export class HeadlessWorkerExecutor extends EventEmitter {
|
|
|
401
413
|
windowsHide: true, // Prevent phantom console windows on Windows
|
|
402
414
|
});
|
|
403
415
|
this.claudeCodeAvailable = true;
|
|
416
|
+
this.claudeCodeAvailableCheckedAt = Date.now();
|
|
404
417
|
this.claudeCodeVersion = output.trim();
|
|
405
418
|
this.emit('status', { available: true, version: this.claudeCodeVersion });
|
|
406
419
|
return true;
|
|
407
420
|
}
|
|
408
421
|
catch {
|
|
409
422
|
this.claudeCodeAvailable = false;
|
|
423
|
+
this.claudeCodeAvailableCheckedAt = Date.now();
|
|
410
424
|
this.emit('status', { available: false });
|
|
411
425
|
return false;
|
|
412
426
|
}
|
|
@@ -433,9 +447,14 @@ export class HeadlessWorkerExecutor extends EventEmitter {
|
|
|
433
447
|
this.emit('error', result);
|
|
434
448
|
return result;
|
|
435
449
|
}
|
|
436
|
-
// Check concurrent limit
|
|
437
|
-
|
|
450
|
+
// Check concurrent limit using activeReservations (synchronous counter)
|
|
451
|
+
// rather than processPool.size (which is updated only after async setup).
|
|
452
|
+
if (this.activeReservations >= this.config.maxConcurrent) {
|
|
438
453
|
// Queue the request
|
|
454
|
+
const MAX_PENDING = 500;
|
|
455
|
+
if (this.pendingQueue.length >= MAX_PENDING) {
|
|
456
|
+
return this.createErrorResult(workerType, 'Pending queue is full');
|
|
457
|
+
}
|
|
439
458
|
return new Promise((resolve, reject) => {
|
|
440
459
|
const entry = {
|
|
441
460
|
workerType,
|
|
@@ -451,8 +470,16 @@ export class HeadlessWorkerExecutor extends EventEmitter {
|
|
|
451
470
|
});
|
|
452
471
|
});
|
|
453
472
|
}
|
|
454
|
-
//
|
|
455
|
-
|
|
473
|
+
// Reserve the slot synchronously and release it (regardless of success
|
|
474
|
+
// or failure) so processQueue can pull the next pending item.
|
|
475
|
+
this.activeReservations++;
|
|
476
|
+
try {
|
|
477
|
+
return await this.executeInternal(workerType, configOverrides);
|
|
478
|
+
}
|
|
479
|
+
finally {
|
|
480
|
+
this.activeReservations--;
|
|
481
|
+
this.processQueue();
|
|
482
|
+
}
|
|
456
483
|
}
|
|
457
484
|
/**
|
|
458
485
|
* Get pool status
|
|
@@ -491,13 +518,63 @@ export class HeadlessWorkerExecutor extends EventEmitter {
|
|
|
491
518
|
return false;
|
|
492
519
|
}
|
|
493
520
|
clearTimeout(entry.timeout);
|
|
494
|
-
|
|
521
|
+
let exited = false;
|
|
522
|
+
entry.process.once('exit', () => { exited = true; });
|
|
523
|
+
try {
|
|
524
|
+
entry.process.kill('SIGTERM');
|
|
525
|
+
}
|
|
526
|
+
catch { /* may already be dead */ }
|
|
527
|
+
const killTimer = setTimeout(() => {
|
|
528
|
+
if (!exited) {
|
|
529
|
+
try {
|
|
530
|
+
entry.process.kill('SIGKILL');
|
|
531
|
+
}
|
|
532
|
+
catch { /* ignore */ }
|
|
533
|
+
}
|
|
534
|
+
}, 5000);
|
|
535
|
+
killTimer.unref();
|
|
536
|
+
entry.process.once('exit', () => clearTimeout(killTimer));
|
|
495
537
|
this.processPool.delete(executionId);
|
|
496
538
|
this.emit('cancelled', { executionId });
|
|
497
539
|
// Process next in queue
|
|
498
540
|
this.processQueue();
|
|
499
541
|
return true;
|
|
500
542
|
}
|
|
543
|
+
/**
|
|
544
|
+
* Cancel all running executions for a specific worker type.
|
|
545
|
+
* Used by the timeout handler in worker-daemon to avoid killing unrelated workers.
|
|
546
|
+
*/
|
|
547
|
+
cancelByType(workerType) {
|
|
548
|
+
let cancelled = false;
|
|
549
|
+
const entries = Array.from(this.processPool.entries());
|
|
550
|
+
for (const [executionId, entry] of entries) {
|
|
551
|
+
if (entry.workerType !== workerType)
|
|
552
|
+
continue;
|
|
553
|
+
clearTimeout(entry.timeout);
|
|
554
|
+
let exited = false;
|
|
555
|
+
entry.process.once('exit', () => { exited = true; });
|
|
556
|
+
try {
|
|
557
|
+
entry.process.kill('SIGTERM');
|
|
558
|
+
}
|
|
559
|
+
catch { /* may already be dead */ }
|
|
560
|
+
const killTimer = setTimeout(() => {
|
|
561
|
+
if (!exited) {
|
|
562
|
+
try {
|
|
563
|
+
entry.process.kill('SIGKILL');
|
|
564
|
+
}
|
|
565
|
+
catch { /* ignore */ }
|
|
566
|
+
}
|
|
567
|
+
}, 5000);
|
|
568
|
+
killTimer.unref();
|
|
569
|
+
entry.process.once('exit', () => clearTimeout(killTimer));
|
|
570
|
+
this.processPool.delete(executionId);
|
|
571
|
+
this.emit('cancelled', { executionId });
|
|
572
|
+
cancelled = true;
|
|
573
|
+
}
|
|
574
|
+
if (cancelled)
|
|
575
|
+
this.processQueue();
|
|
576
|
+
return cancelled;
|
|
577
|
+
}
|
|
501
578
|
/**
|
|
502
579
|
* Cancel all running executions
|
|
503
580
|
*/
|
|
@@ -507,15 +584,26 @@ export class HeadlessWorkerExecutor extends EventEmitter {
|
|
|
507
584
|
const entries = Array.from(this.processPool.entries());
|
|
508
585
|
for (const [executionId, entry] of entries) {
|
|
509
586
|
clearTimeout(entry.timeout);
|
|
510
|
-
|
|
511
|
-
//
|
|
512
|
-
|
|
587
|
+
// Track exit so the SIGKILL fallback can't be sent to a recycled PID.
|
|
588
|
+
// entry.process.killed is set by Node when .kill() was called, NOT when
|
|
589
|
+
// the OS process actually exited — without an explicit 'exit' listener,
|
|
590
|
+
// a recycled PID can receive our SIGKILL.
|
|
591
|
+
let exited = false;
|
|
592
|
+
entry.process.once('exit', () => { exited = true; });
|
|
593
|
+
try {
|
|
594
|
+
entry.process.kill('SIGTERM');
|
|
595
|
+
}
|
|
596
|
+
catch { /* may already be dead */ }
|
|
597
|
+
const killTimer = setTimeout(() => {
|
|
598
|
+
if (exited)
|
|
599
|
+
return;
|
|
513
600
|
try {
|
|
514
|
-
|
|
515
|
-
entry.process.kill('SIGKILL');
|
|
601
|
+
entry.process.kill('SIGKILL');
|
|
516
602
|
}
|
|
517
603
|
catch { /* already dead */ }
|
|
518
|
-
}, 5000)
|
|
604
|
+
}, 5000);
|
|
605
|
+
killTimer.unref();
|
|
606
|
+
entry.process.once('exit', () => clearTimeout(killTimer));
|
|
519
607
|
this.emit('cancelled', { executionId });
|
|
520
608
|
cancelled++;
|
|
521
609
|
}
|
|
@@ -637,14 +725,21 @@ export class HeadlessWorkerExecutor extends EventEmitter {
|
|
|
637
725
|
* Process the pending queue
|
|
638
726
|
*/
|
|
639
727
|
processQueue() {
|
|
728
|
+
// Gate on activeReservations (synchronous counter) so we cannot
|
|
729
|
+
// accidentally drain the queue past maxConcurrent before async setup
|
|
730
|
+
// populates processPool.
|
|
640
731
|
while (this.pendingQueue.length > 0 &&
|
|
641
|
-
this.
|
|
732
|
+
this.activeReservations < this.config.maxConcurrent) {
|
|
642
733
|
const next = this.pendingQueue.shift();
|
|
643
734
|
if (!next)
|
|
644
735
|
break;
|
|
736
|
+
this.activeReservations++;
|
|
645
737
|
this.executeInternal(next.workerType, next.config)
|
|
646
|
-
.then(next.resolve)
|
|
647
|
-
.
|
|
738
|
+
.then(next.resolve, next.reject)
|
|
739
|
+
.finally(() => {
|
|
740
|
+
this.activeReservations--;
|
|
741
|
+
this.processQueue();
|
|
742
|
+
});
|
|
648
743
|
}
|
|
649
744
|
}
|
|
650
745
|
/**
|
|
@@ -670,13 +765,18 @@ export class HeadlessWorkerExecutor extends EventEmitter {
|
|
|
670
765
|
// Deduplicate and limit
|
|
671
766
|
const uniqueFiles = Array.from(new Set(files)).slice(0, this.config.maxContextFiles);
|
|
672
767
|
// Build context
|
|
768
|
+
const { resolve: resolvePath, sep } = await import('path');
|
|
673
769
|
const contextParts = [];
|
|
674
770
|
for (const file of uniqueFiles) {
|
|
675
771
|
try {
|
|
676
772
|
const fullPath = join(this.projectRoot, file);
|
|
677
|
-
|
|
773
|
+
const resolvedFull = resolvePath(fullPath);
|
|
774
|
+
const resolvedRoot = resolvePath(this.projectRoot);
|
|
775
|
+
if (!resolvedFull.startsWith(resolvedRoot + sep) && resolvedFull !== resolvedRoot)
|
|
776
|
+
continue;
|
|
777
|
+
if (!existsSync(resolvedFull))
|
|
678
778
|
continue;
|
|
679
|
-
const content = readFileSync(
|
|
779
|
+
const content = readFileSync(resolvedFull, 'utf-8');
|
|
680
780
|
const truncated = content.slice(0, this.config.maxCharsPerFile);
|
|
681
781
|
const wasTruncated = content.length > this.config.maxCharsPerFile;
|
|
682
782
|
contextParts.push(`--- ${file}${wasTruncated ? ' (truncated)' : ''} ---\n${truncated}`);
|
|
@@ -686,8 +786,13 @@ export class HeadlessWorkerExecutor extends EventEmitter {
|
|
|
686
786
|
}
|
|
687
787
|
}
|
|
688
788
|
const contextContent = contextParts.join('\n\n');
|
|
689
|
-
// Cache the result
|
|
789
|
+
// Cache the result (evict oldest when at capacity)
|
|
690
790
|
if (this.config.cacheContext) {
|
|
791
|
+
if (this.contextCache.size >= 50) {
|
|
792
|
+
const oldestKey = this.contextCache.keys().next().value;
|
|
793
|
+
if (oldestKey !== undefined)
|
|
794
|
+
this.contextCache.delete(oldestKey);
|
|
795
|
+
}
|
|
691
796
|
this.contextCache.set(cacheKey, {
|
|
692
797
|
content: contextContent,
|
|
693
798
|
timestamp: Date.now(),
|
|
@@ -815,8 +920,45 @@ Analyze the above codebase context and provide your response following the forma
|
|
|
815
920
|
*/
|
|
816
921
|
executeClaudeCode(prompt, options) {
|
|
817
922
|
return new Promise((resolve) => {
|
|
923
|
+
// SECURITY: in strict sandbox mode, build a minimal env allowlist instead
|
|
924
|
+
// of forwarding the entire parent env. The previous spread also passed
|
|
925
|
+
// LD_PRELOAD, NODE_OPTIONS, DYLD_INSERT_LIBRARIES, PYTHONPATH, HTTP_PROXY,
|
|
926
|
+
// etc. — which means "strict" was a label, not a control. Now it actually
|
|
927
|
+
// strips dangerous loaders/proxies even when the parent has them set.
|
|
928
|
+
const STRICT_ENV_ALLOWLIST = new Set([
|
|
929
|
+
'PATH', 'HOME', 'USER', 'LOGNAME', 'LANG', 'LC_ALL', 'TZ', 'TERM', 'SHELL',
|
|
930
|
+
'TMPDIR', 'TMP', 'TEMP',
|
|
931
|
+
'ANTHROPIC_API_KEY', 'ANTHROPIC_MODEL', 'ANTHROPIC_BASE_URL',
|
|
932
|
+
]);
|
|
933
|
+
const FORBIDDEN_ENV = new Set([
|
|
934
|
+
'LD_PRELOAD', 'LD_LIBRARY_PATH', 'LD_AUDIT',
|
|
935
|
+
'DYLD_INSERT_LIBRARIES', 'DYLD_LIBRARY_PATH', 'DYLD_FALLBACK_LIBRARY_PATH',
|
|
936
|
+
'NODE_OPTIONS', 'NODE_PATH',
|
|
937
|
+
'PYTHONPATH', 'PYTHONHOME', 'PYTHONSTARTUP',
|
|
938
|
+
'HTTP_PROXY', 'HTTPS_PROXY', 'NO_PROXY',
|
|
939
|
+
'BASH_ENV', 'ENV', 'CDPATH',
|
|
940
|
+
'PERL5OPT', 'RUBYOPT',
|
|
941
|
+
'JAVA_TOOL_OPTIONS', '_JAVA_OPTIONS', 'JDK_JAVA_OPTIONS',
|
|
942
|
+
]);
|
|
943
|
+
const baseEnv = {};
|
|
944
|
+
if (options.sandbox === 'strict') {
|
|
945
|
+
for (const k of STRICT_ENV_ALLOWLIST) {
|
|
946
|
+
const v = process.env[k];
|
|
947
|
+
if (typeof v === 'string')
|
|
948
|
+
baseEnv[k] = v;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
else {
|
|
952
|
+
for (const [k, v] of Object.entries(process.env)) {
|
|
953
|
+
if (typeof v !== 'string')
|
|
954
|
+
continue;
|
|
955
|
+
if (FORBIDDEN_ENV.has(k))
|
|
956
|
+
continue; // always strip in any mode
|
|
957
|
+
baseEnv[k] = v;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
818
960
|
const env = {
|
|
819
|
-
...
|
|
961
|
+
...baseEnv,
|
|
820
962
|
CLAUDE_CODE_HEADLESS: 'true',
|
|
821
963
|
CLAUDE_CODE_SANDBOX_MODE: options.sandbox,
|
|
822
964
|
// Fix #1395 Bug 2: Workers fail inside active Claude Code session.
|
|
@@ -831,23 +973,36 @@ Analyze the above codebase context and provide your response following the forma
|
|
|
831
973
|
// Set model
|
|
832
974
|
// Resolve model: user env override > config override > default alias
|
|
833
975
|
env.ANTHROPIC_MODEL = process.env.ANTHROPIC_MODEL || MODEL_IDS[options.model];
|
|
834
|
-
// Spawn claude CLI process
|
|
835
|
-
const child = spawn('claude', ['--print', prompt], {
|
|
976
|
+
// Spawn claude CLI process — `--` terminates option parsing so prompt can't smuggle flags
|
|
977
|
+
const child = spawn('claude', ['--print', '--', prompt], {
|
|
836
978
|
cwd: this.projectRoot,
|
|
837
979
|
env,
|
|
838
980
|
stdio: ['ignore', 'pipe', 'pipe'], // 'ignore' closes stdin at spawn — fixes #1395 where claude --print blocks on EOF
|
|
839
981
|
windowsHide: true, // Prevent phantom console windows on Windows
|
|
840
982
|
});
|
|
841
|
-
// Setup timeout
|
|
983
|
+
// Setup timeout — track real exit via 'exit' listener.
|
|
984
|
+
// child.killed is set by Node when .kill() is called (signal sent), NOT
|
|
985
|
+
// when the OS process exited. Without an explicit `exited` flag the
|
|
986
|
+
// SIGKILL fallback never fires for a process that ignores SIGTERM, and
|
|
987
|
+
// the slot leaks indefinitely.
|
|
988
|
+
let sigkillTimer;
|
|
989
|
+
let childExited = false;
|
|
990
|
+
child.once('exit', () => { childExited = true; });
|
|
842
991
|
const timeoutHandle = setTimeout(() => {
|
|
843
992
|
if (this.processPool.has(options.executionId)) {
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
993
|
+
try {
|
|
994
|
+
child.kill('SIGTERM');
|
|
995
|
+
}
|
|
996
|
+
catch { /* may already be dead */ }
|
|
997
|
+
sigkillTimer = setTimeout(() => {
|
|
998
|
+
if (childExited)
|
|
999
|
+
return;
|
|
1000
|
+
try {
|
|
848
1001
|
child.kill('SIGKILL');
|
|
849
1002
|
}
|
|
1003
|
+
catch { /* already dead */ }
|
|
850
1004
|
}, 5000);
|
|
1005
|
+
sigkillTimer.unref();
|
|
851
1006
|
}
|
|
852
1007
|
}, options.timeoutMs);
|
|
853
1008
|
// Track in process pool
|
|
@@ -864,6 +1019,7 @@ Analyze the above codebase context and provide your response following the forma
|
|
|
864
1019
|
let resolved = false;
|
|
865
1020
|
const cleanup = () => {
|
|
866
1021
|
clearTimeout(timeoutHandle);
|
|
1022
|
+
clearTimeout(sigkillTimer);
|
|
867
1023
|
this.processPool.delete(options.executionId);
|
|
868
1024
|
};
|
|
869
1025
|
child.stdout?.on('data', (data) => {
|
|
@@ -906,21 +1062,6 @@ Analyze the above codebase context and provide your response following the forma
|
|
|
906
1062
|
error: error.message,
|
|
907
1063
|
});
|
|
908
1064
|
});
|
|
909
|
-
// Handle timeout
|
|
910
|
-
setTimeout(() => {
|
|
911
|
-
if (resolved)
|
|
912
|
-
return;
|
|
913
|
-
if (!this.processPool.has(options.executionId))
|
|
914
|
-
return;
|
|
915
|
-
resolved = true;
|
|
916
|
-
child.kill('SIGTERM');
|
|
917
|
-
cleanup();
|
|
918
|
-
resolve({
|
|
919
|
-
success: false,
|
|
920
|
-
output: stdout || stderr,
|
|
921
|
-
error: `Execution timed out after ${options.timeoutMs}ms`,
|
|
922
|
-
});
|
|
923
|
-
}, options.timeoutMs + 100); // Slightly after the kill timeout
|
|
924
1065
|
});
|
|
925
1066
|
}
|
|
926
1067
|
/**
|
|
@@ -1012,7 +1153,9 @@ Analyze the above codebase context and provide your response following the forma
|
|
|
1012
1153
|
const timestamp = new Date().toISOString();
|
|
1013
1154
|
const logFile = join(this.config.logDir, `${executionId}_${type}.log`);
|
|
1014
1155
|
const logContent = `[${timestamp}] ${type.toUpperCase()}\n${'='.repeat(60)}\n${content}\n`;
|
|
1015
|
-
|
|
1156
|
+
const tmpLog = logFile + '.tmp';
|
|
1157
|
+
writeFileSync(tmpLog, logContent);
|
|
1158
|
+
renameSync(tmpLog, logFile);
|
|
1016
1159
|
}
|
|
1017
1160
|
catch {
|
|
1018
1161
|
// Ignore log write errors
|
|
@@ -9,12 +9,65 @@
|
|
|
9
9
|
* - Input validation
|
|
10
10
|
*/
|
|
11
11
|
const REGISTRY_API_URL = 'https://us-central1-monomind.cloudfunctions.net/publish-registry';
|
|
12
|
+
/**
|
|
13
|
+
* Read a fetch response body with a hard byte cap. AbortSignal.timeout bounds
|
|
14
|
+
* time, NOT bytes — a hijacked endpoint or MITM (TLS without pinning) can
|
|
15
|
+
* stream a multi-GB body that the CLI buffers into memory and OOMs. Cap the
|
|
16
|
+
* read here so all downstream JSON.parse / .text() calls are bounded.
|
|
17
|
+
*/
|
|
18
|
+
async function readBoundedText(response, maxBytes) {
|
|
19
|
+
const lenHdr = response.headers.get('content-length');
|
|
20
|
+
if (lenHdr) {
|
|
21
|
+
const declared = parseInt(lenHdr, 10);
|
|
22
|
+
if (Number.isFinite(declared) && declared > maxBytes) {
|
|
23
|
+
throw new Error(`Response too large: ${declared} bytes (max ${maxBytes})`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (!response.body)
|
|
27
|
+
return '';
|
|
28
|
+
const reader = response.body.getReader();
|
|
29
|
+
const chunks = [];
|
|
30
|
+
let total = 0;
|
|
31
|
+
while (true) {
|
|
32
|
+
const { done, value } = await reader.read();
|
|
33
|
+
if (done)
|
|
34
|
+
break;
|
|
35
|
+
if (value) {
|
|
36
|
+
total += value.byteLength;
|
|
37
|
+
if (total > maxBytes) {
|
|
38
|
+
await reader.cancel();
|
|
39
|
+
throw new Error(`Response too large: exceeded ${maxBytes} bytes`);
|
|
40
|
+
}
|
|
41
|
+
chunks.push(value);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const buf = new Uint8Array(total);
|
|
45
|
+
let off = 0;
|
|
46
|
+
for (const c of chunks) {
|
|
47
|
+
buf.set(c, off);
|
|
48
|
+
off += c.byteLength;
|
|
49
|
+
}
|
|
50
|
+
return new TextDecoder('utf-8').decode(buf);
|
|
51
|
+
}
|
|
52
|
+
async function readBoundedJson(response, maxBytes = 1_048_576) {
|
|
53
|
+
const text = await readBoundedText(response, maxBytes);
|
|
54
|
+
return JSON.parse(text);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Strip control chars and cap length on attacker-controlled error body before
|
|
58
|
+
* inlining into a thrown Error.message. Without this, a malicious or
|
|
59
|
+
* compromised endpoint can deliver multi-MB error bodies that flow into log
|
|
60
|
+
* aggregators via unhandled-rejection traces.
|
|
61
|
+
*/
|
|
62
|
+
function safeErrText(s) {
|
|
63
|
+
return s.replace(/[\x00-\x1f\x7f]/g, '?').slice(0, 512);
|
|
64
|
+
}
|
|
12
65
|
/**
|
|
13
66
|
* Validate item ID to prevent injection
|
|
14
67
|
*/
|
|
15
68
|
function validateItemId(itemId) {
|
|
16
|
-
//
|
|
17
|
-
return /^[
|
|
69
|
+
// Scoped packages (@scope/name) or plain identifiers — no other slashes allowed
|
|
70
|
+
return /^(@[a-zA-Z0-9][a-zA-Z0-9_-]*\/[a-zA-Z0-9][a-zA-Z0-9_-]*|[a-zA-Z0-9][a-zA-Z0-9_-]*)$/.test(itemId) && itemId.length < 100;
|
|
18
71
|
}
|
|
19
72
|
/**
|
|
20
73
|
* Validate rating value
|
|
@@ -44,10 +97,10 @@ export async function rateItem(itemId, rating, itemType = 'plugin', userId) {
|
|
|
44
97
|
signal: AbortSignal.timeout(10000),
|
|
45
98
|
});
|
|
46
99
|
if (!response.ok) {
|
|
47
|
-
const error = await response.
|
|
48
|
-
throw new Error(`Rating failed: ${error}`);
|
|
100
|
+
const error = await readBoundedText(response, 64 * 1024).catch(() => 'unreadable');
|
|
101
|
+
throw new Error(`Rating failed: ${safeErrText(error)}`);
|
|
49
102
|
}
|
|
50
|
-
return response
|
|
103
|
+
return readBoundedJson(response);
|
|
51
104
|
}
|
|
52
105
|
/**
|
|
53
106
|
* Get ratings for a single item
|
|
@@ -67,7 +120,7 @@ export async function getRating(itemId, itemType = 'plugin') {
|
|
|
67
120
|
if (!response.ok) {
|
|
68
121
|
throw new Error('Failed to get ratings');
|
|
69
122
|
}
|
|
70
|
-
return response
|
|
123
|
+
return readBoundedJson(response);
|
|
71
124
|
}
|
|
72
125
|
/**
|
|
73
126
|
* Get ratings for multiple items (batch)
|
|
@@ -93,7 +146,7 @@ export async function getBulkRatings(itemIds, itemType = 'plugin') {
|
|
|
93
146
|
if (!response.ok) {
|
|
94
147
|
throw new Error('Failed to get bulk ratings');
|
|
95
148
|
}
|
|
96
|
-
return response
|
|
149
|
+
return readBoundedJson(response, 4 * 1024 * 1024);
|
|
97
150
|
}
|
|
98
151
|
/**
|
|
99
152
|
* Get analytics data
|
|
@@ -105,7 +158,7 @@ export async function getAnalytics() {
|
|
|
105
158
|
if (!response.ok) {
|
|
106
159
|
throw new Error('Failed to get analytics');
|
|
107
160
|
}
|
|
108
|
-
return response
|
|
161
|
+
return readBoundedJson(response);
|
|
109
162
|
}
|
|
110
163
|
/**
|
|
111
164
|
* Track a download event
|
|
@@ -134,7 +187,7 @@ export async function checkHealth() {
|
|
|
134
187
|
const response = await fetch(`${REGISTRY_API_URL}?action=status`, {
|
|
135
188
|
signal: AbortSignal.timeout(5000),
|
|
136
189
|
});
|
|
137
|
-
return response
|
|
190
|
+
return readBoundedJson(response, 64 * 1024);
|
|
138
191
|
}
|
|
139
192
|
catch (error) {
|
|
140
193
|
return {
|